Announcement Announcement Module
Collapse
No announcement yet.
Spring JPA+Postgres Incantation without XML a total mystery Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Spring JPA+Postgres Incantation without XML a total mystery

    I can't find any functioning way for Spring with Annotations to create and inject an EntityManager object for me, backed by JPA and Postgres.

    Can anyone share the minimal set of @Bean methods in an @Configuration-annotated object to get JPA working against Postgres?

    I've created a minimal test application in an ordinary Eclipse Java project to show my working so far. I think everything should be self explanatory (and the error is probably located) in App.java linked below.

    In App I'm trying to create an AnnotationConfigApplicationContext and get it to use the annotated methods in App.Config as a factory for a DataSource and an EntityManagerFactoryInfo. Then I try to load an EntityManager bean from the ApplicationContext, thinking it has enough information to proceed.

    Running my code triggers no errors until the terminal error "No unique bean of type [javax.persistence.EntityManager]" when I try to load the EntityManager bean

    I'm sure I'm just doing the wrong thing, but I can't find a simple statement of the minimal chain of events/requirements to configure and inject an EntityManager into my App. Currently it seems to think there is no definition of an EntityManager despite the EntityManagerFactoryInfo object and DataSource object available.

    I figure all I need to provide is a database, and the right incantation somewhere in annotations or maybe java BeanDefinition passing, after which everything will work like magic.

    I've set up and tested the database, but the magic incantation is eluding me. My aim is to avoid mysterious unvalidated XML files so I'm focusing on Annotations, or maybe even direct Java calls if anyone knows how, to configure and trigger the DI.

    After much trial and error, (and I really mean a lot), I've had to give up and throw myself on your mercy.

    I'm trying to avoid loading in a million and one unnecessary things from a test application configured by Maven and reams of boilerplate XML to establish if Spring components can be used in a more comprehensible and selective way.

    The core example code of the approach I'm using so far is available in the App file, and the libraries on the build path of the Eclipse project are shown in the listjars.txt file also linked below.

    When I run this App as is, I get no errors until my explicit getBean(EntityManager.class) call fails with...

    Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefini tionException: No unique bean of type [javax.persistence.EntityManager] is defined: expected single bean but found 0:
    at org.springframework.beans.factory.support.DefaultL istableBeanFactory.getBean(DefaultListableBeanFact ory.java:269)
    at org.springframework.context.support.AbstractApplic ationContext.getBean(AbstractApplicationContext.ja va:1083)
    at com.cefn.filesystem.App.run(App.java:46)
    at com.cefn.filesystem.App.main(App.java:29)


    App.java is available at...

    https://github.com/cefn/Spring-JPA-M...ystem/App.java

    The libraries in my Build path are listed here...

    https://github.com/cefn/Spring-JPA-M...b/listjars.txt

    ...and the remainder of the source is available by browsing around the src directory in the github tree if that helps.

    EPILOGUE

    Constructors and other established java conventions normally steer you towards resolving dependencies, and prevent runtime failures.

    I find that Spring's divergence from this approach makes it more or less impossible for me to identify the minimal functioning configuration for any object, and leaves failures silent until service objects are demanded.

    For EntityManager and JPA there now seems to be a history of about a thousand different recommended ways to do it, all using arcane XML files and most of which are now deprecated, and there are many suggested alternatives even for the recommended routes.

    How am I meant to approach this problem in general as I'm sure I'll run into it in other parts of Spring?
    Last edited by cefn; Nov 9th, 2010, 01:23 PM. Reason: Added link

  • #2
    spring config file hibernate JPA

    This is complete spring config file (JPA with Hibernate Implementation), You could use this file as is in your code just read up on following bullet point
    • You need a persistence.xml under the default directory (META-INF)
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xmlns:aop="http://www.springframework.org/schema/aop"
    	xmlns:context="http://www.springframework.org/schema/context"
    	xmlns:tx="http://www.springframework.org/schema/tx"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans
               http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
               http://www.springframework.org/schema/aop
               http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
               http://www.springframework.org/schema/tx
               http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
               http://www.springframework.org/schema/context
               http://www.springframework.org/schema/context/spring-context-2.5.xsd">
    	
              <!--  allows use of @Transaction on service classes -->
    	<tx:annotation-driven />
    
    	<!-- data source using connection pool -->
    
    	<bean id="pgDS"
    		class="com.mchange.v2.c3p0.ComboPooledDataSource"
    		destroy-method="close">
    		<property name="driverClass" value="org.postgresql.Driver" />
    		<property name="jdbcUrl" value="jdbc:postgresql:dburl" />
    		<property name="user" value="youruser" />
    		<property name="password" value="yourpassword" />
    		<property name="minPoolSize" value="1" />
    		<property name="acquireIncrement" value="1" />
    		<property name="maxPoolSize" value="1" />
    
    	</bean>	
    
                
             <!-- em factory -->
             <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    		
    		<property name="dataSource" ref="pgDS" />
    		<property name="jpaPropertyMap">
    			<map>
    				<entry key="hibernate.jdbc.fetch_size" value="100"></entry>
    				<entry key="hibernate.jdbc.batch_size" value="100"></entry>
    				<entry key="hibernate.cache.provider_class" value="org.hibernate.cache.EhCacheProvider"></entry>
    				<entry key="hibernate.cache.use_query_cache" value="true"></entry>
    			</map>
    		</property>
    		<property name="jpaVendorAdapter">
    			<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
    				<property name="showSql" value="false" />
    				<property name="generateDdl" value="false" />
    			</bean>
    		</property>
    	</bean>
            
    
    	
    <!-- tx manager -->
           <bean id="transactionManager"
    		class="org.springframework.orm.jpa.JpaTransactionManager">
    		<property name="entityManagerFactory" ref="entityManagerFactory" />
    		<property name="dataSource" ref="pgDS" />
    	</bean>
    
    </beans>

    Comment


    • #3
      Appreciate the help

      Thanks for taking the time to reply. As mentioned in the first line of the original post, I've been looking for an annotation-based solution. I'd prefer to avoid XML files everywhere.

      I'll try to translate from your XML into actual Java code, but even this is pretty mysterious.

      For example, it's not obvious to me how I create an @Bean LocalContainerEntityManagerFactoryBean which will be wired to an @Bean DataSource which is provided by a different method in the same @Configuration object.

      Using XML seems like a massively retrograde step given the strengths of the Java type system and the compile-time (and incidentally run-time) checking which is therefore possible. It was perhaps worthwhile as a stopgap before Java 1.5 brought in Annotations, but surely superceded now. If there is still a case for using XML, I don't know what it is, assuming you're writing your own code.

      Understanding what actual objects need to be brought into existence to do this simple job (making an EntityManager available) means I can either construct my own procedurally, or use DI and get the same result. Without this equivalence, it's hard to debug anything.

      I feel the XML approach simply leaves huge opportunities for mystery as to the actual things and their relationships, since elements and attributes are opaquely translated into behaviours over factories and beans with no linked documentation.

      Worse, updates to Java classes can be made which require changes somewhere in a monolithic XML configuration file; whilst using annotations directly on classes and methods mitigates this problem hugely. As I'm responsible for my colleagues' future with this system, I feel XML is absolutely the wrong way to go.

      BTW does anyone else find that sessions time out after an incredibly short time on this forum (<< 1Hour), leading to lost posts.

      Comment


      • #4
        Sorry I did not read your question before answering, Here is what I had to do to get your code working, Please look at the discussion below
        I did following changes to your config code
        HTML Code:
        @Configuration
        	public static class Config 
        	{
        		@Bean
        		DataSource getDataSource() {
        			SimpleDriverDataSource bean = new SimpleDriverDataSource(new org.postgresql.Driver(), "jdbc:postgresql://localhost/cefn", "cefn", "cefn");
        					
        			return bean;
        		}
        		
        		@Bean
        		EntityManager getEntityManager()
        		{
        			return (EntityManager)getEntityManagerFactoryInfo().getNativeEntityManagerFactory().createEntityManager();
        		}
        
        		@Bean
        		EntityManagerFactoryInfo getEntityManagerFactoryInfo() 
        		{
        			LocalContainerEntityManagerFactoryBean bean = new org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean();
        			bean.setDataSource(getDataSource());
        			bean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        			bean.setPersistenceXmlLocation("classpath*:persistence.xml");
        			return bean;
        		}					
        }
        I also made a small change to your run method code
        PHP Code:
        appContext.register(Config.class);             
        //the following line did the trick
        appContext.refresh(); 
        look here for refresh reference
        http://static.springsource.org/sprin...ava.lang.Class...)

        Comment


        • #5
          Wow that's incredibly helpful

          Thanks so much for taking the time.

          I'll be able to progress things nicely when I'm back in work on Monday morning.

          Let me know if I can repay the favour: help you with any JQuery or XQuery or XPath or Processing.org or Arduino.cc or something which I AM good at

          Comment

          Working...
          X