1
votes

I have three spring+JPA projects...a Base project A, a plugin project B and the actual project C. In C, both of the other projects are imported as A.jar and B.jar. Each project have their own ApplicationContext.xml. The leaf project i.e. C has its persistence.xml in custom location as mentioned in appcontext.xml in proeject-A (while start up project A looks for the appcontext.xml and persistence.xml in entire classpath and hence able to load xmls from B and C).

below is my configuration, ApplicationContext.xml - in project A

<bean id="jpaQueryManager" class="com.motherframework.base.dao.jpa.JPAQueryManager">
    <property name="jpaTemplate" ref="jpaTemplate"/>
</bean>

<bean id="jpaTemplate" class="org.springframework.orm.jpa.JpaTemplate">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <!-- <property name="persistenceUnitManager" ref="pum"/>
    <property name="persistenceUnitName" value="SchoolWebsitePersistenceUnit"/> -->
    <property name="persistenceXmlLocation" value="classpath*:configuration/xml/persistence.xml"/>
    <!-- <property name="persistenceUnitPostProcessors">
      <list>
        <bean class="com.motherframework.base.dao.jpa.EntityScanner"/>
      </list>
    </property> -->
    <property name="dataSource"><ref bean="dataSource"/></property>
    <property name="jpaVendorAdapter">
      <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
        <property name="showSql" value="true"/>
        <property name="generateDdl" value="true"/>
        <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/>
      </bean>
    </property>
</bean>

And this is my persistence.xml

<persistence-unit name="SchoolWebsitePersistenceUnit" transaction-type="RESOURCE_LOCAL">

    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <class>com.motherframework.plugin.school.entity.Account</class>
    <class>com.motherframework.plugin.school.entity.Module</class>
    <class>com.motherframework.plugin.school.entity.NavigationMenu</class>
    <class>com.motherframework.plugin.school.entity.User</class>


    <properties>
        <property name="dialect" value="org.hibernate.dialect.SQLServerDialect"/>
        <property name="hibernate.connection.autocommit" value="true"/>
        <property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/>
        <property name="hibernate.show_sql" value="true"/>
        <property name="hibernate.archive.autodetection" value="class, hbm"/>
    </properties>

</persistence-unit>

Now from my client class (a main method) i am fetching a Entity (e.g. User) using @transactional Service-DAO layer.

User u = ((TestService) ApplicationContext.getBean("testService")).fetchUser();
// FROM User u WHERE u.id=1  (Note: no INNER JOIN u.account)

print(u.getAccount())

It should give me NULL, but it is throwing exception,

DEBUG DefaultListableBeanFactory:241 - Returning cached instance of singleton bean 'org.springframework.transaction.interceptor.TransactionInterceptor#0'
 DEBUG AnnotationTransactionAttributeSource:106 - Adding transactional method 'fetchUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly; ''
DEBUG DefaultListableBeanFactory:241 - Returning cached instance of singleton bean 'jpaTxManager'
DEBUG JpaTransactionManager:365 - Creating new transaction with name [com.motherframework.plugin.test.service.TestService.fetchUser]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly; ''
DEBUG JpaTransactionManager:323 - Opened new EntityManager [org.hibernate.ejb.EntityManagerImpl@d964af] for JPA transaction
DEBUG JpaTransactionManager:355 - Exposing JPA transaction as JDBC transaction [SimpleConnectionHandle: jdbc:mysql://localhost:3306/schooldb, UserName=scott@localhost, MySQL-AB JDBC Driver]
DEBUG TransactionSynchronizationManager:183 - Bound value [org.springframework.jdbc.datasource.ConnectionHolder@45378f] for key [org.apache.commons.dbcp.BasicDataSource@181b3d4] to thread [main]
DEBUG TransactionSynchronizationManager:183 - Bound value [org.springframework.orm.jpa.EntityManagerHolder@29d65b] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@b8176d] to thread [main]
DEBUG TransactionSynchronizationManager:258 - Initializing transaction synchronization
DEBUG TransactionInterceptor:381 - Getting transaction for [com.motherframework.plugin.test.service.TestService.fetchUser]
DEBUG TransactionSynchronizationManager:139 - Retrieved value [org.springframework.orm.jpa.EntityManagerHolder@29d65b] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@b8176d] bound to thread [main]
DEBUG TransactionSynchronizationManager:139 - Retrieved value [org.springframework.orm.jpa.EntityManagerHolder@29d65b] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@b8176d] bound to thread [main]
DEBUG TransactionSynchronizationManager:139 - Retrieved value [org.springframework.orm.jpa.EntityManagerHolder@29d65b] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@b8176d] bound to thread [main]
Hibernate: select user0_.id as id3_, user0_.accountId as accountId3_, user0_.email as email3_, user0_.loginId as loginId3_, user0_.name as name3_, user0_.password as password3_ from User user0_ where user0_.id=1
DEBUG TransactionInterceptor:410 - Completing transaction for [com.motherframework.plugin.test.service.TestService.fetchUser]
DEBUG JpaTransactionManager:925 - Triggering beforeCommit synchronization
DEBUG JpaTransactionManager:938 - Triggering beforeCompletion synchronization
 DEBUG JpaTransactionManager:752 - Initiating transaction commit
DEBUG JpaTransactionManager:462 - Committing JPA transaction on EntityManager [org.hibernate.ejb.EntityManagerImpl@d964af]
DEBUG JpaTransactionManager:951 - Triggering afterCommit synchronization
DEBUG JpaTransactionManager:967 - Triggering afterCompletion synchronization
DEBUG TransactionSynchronizationManager:311 - Clearing transaction synchronization
DEBUG TransactionSynchronizationManager:229 - Removed value [org.springframework.orm.jpa.EntityManagerHolder@29d65b] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@b8176d] from thread [main]
DEBUG TransactionSynchronizationManager:229 - Removed value [org.springframework.jdbc.datasource.ConnectionHolder@45378f] for key [org.apache.commons.dbcp.BasicDataSource@181b3d4] from thread [main]
DEBUG JpaTransactionManager:548 - Closing JPA EntityManager [org.hibernate.ejb.EntityManagerImpl@d964af] after transaction
DEBUG EntityManagerFactoryUtils:329 - Closing JPA EntityManager
856 [main] ERROR org.hibernate.LazyInitializationException - could not initialize proxy - no Session
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
at            org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:132)
at     org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:174)
Exception in thread "main" org.hibernate.LazyInitializationException: could not initialize proxy - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:132)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:174)
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190)
at      com.motherframework.plugin.school.entity.Account_$$_javassist_3.toString(Account_$$_javassist_3.java)
at java.lang.String.valueOf(Unknown Source)
at java.io.PrintStream.println(Unknown Source)
at 

You can find that my entity manager is closed, so it will give an exception if it tries to fetch account on user....that i understand...but if the entitymanager is closed then why it is trying to do that? how to stop it? Is there any problem with configuration?

One more thing, is the entitymanagerfactory that i am using is proper for a public webapplication in production environment (and in tomcat 7)?

Thanks in advance..:)

Thanks Nizet..you mean my PersistenceContext is already closed as EntityManager is closed. But if entity is really out of PersistenceContext then why should it try to fetch its relation?..it is then a simple POJO, not @Entity anymore..right? For the second issue, yes I missed that

<tx:annotation-driven transaction-manager="jpaTxManager"/>
<bean id="jpaTxManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory"><ref bean="entityManagerFactory"/></property>
    <property name="dataSource"><ref bean="dataSource"/></property>
</bean>

Please check my Entitymanager type and transactionmanager...are they proper for Production?

This is my DS:

<bean id="dataSource" destroy-method="close"   class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${motherframework.configuration.db.driver}" />
<property name="url" value="${motherframework.configuration.db.url}" />
<property name="username" value="${motherframework.configuration.db.username}" />
<property name="password" value="${motherframework.configuration.db.password}" />

But do u want to mean that, though persistence context is closed, the "user" object still holds some property which makes it different from normal POJO class?

1

1 Answers

5
votes

Your user has an association with an account. The account is lazy-loaded. This means that the first time you call any method on the account (user.getAccount().getName()) for example, Hibernate will load the state of the account from the database, and return the name of the account. And it can only do that while the entity manager is open, else, it doesn't have any connection to the database anymore.

It can't return null: returning null would mean: this user doesn't have an account, which is wrong.

If you want to access the account after the entity manager is closed, then you need to initialize the account before closing it. There's no way around that.

Regarding your config, no, it's not ready for production:

  • showSql should be false
  • hibernate.show_sql should be false
  • generateDdl should be false
  • hibernate.connection.autocommit should be false
  • you haven't shown your datasource definition, which is a very important part