1

Im having some trouble understanding the way spring handles the hibernate entities, and does the lazy loading process.

So for this case we have to entities

@Entity
public class EntityA{

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;


    @ManyToMany(cascade={CascadeType.PERSIST, CascadeType.REFRESH})
    private Collection<EntityB> bss= new ArrayList<EntityB>();

and the agregated entity

@Entity
public class EntityB{

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;


    @ManyToMany(mappedBy="bss")
    private Collection<EntityA> ass= new ArrayList<EntityA>();

then I have a simple business class marked as transactional:

@Component
@Scope("session")
@Transactional(propagation=Propagation.TRIED_EVERY_SINGLE_ONE)
public class GenericTestBean {

    private EntityA entityA;

    @Autowired
    private IGenericDAO genericDAO;


    public GenericTestBean() {
        System.out.println("bean creado!");
    }

    public void testQuery() {
        entityA= genericDAO.get(EntityA.class, 1l);
        System.out.println(TransactionIndicatingUtil.getTransactionStatus(true));
        System.out.println("is element atached? :" + genericDAO.isAtached(entityA));
        //this.loadData();

    }

    public void loadData(){

        System.out.println(TransactionIndicatingUtil.getTransactionStatus(true));
        System.out.println("is element atached? :" + genericDAO.isAtached(currentCompany));

        System.out.println(entityA.getBss.size());
    }

}

and a Simple test class

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "applicationContext.xml" })
@WebAppConfiguration
public class EntityQueryTest {

//  @Autowired
//  private SessionFactory sessionFactory;

    @Autowired
    GenericTestBean genericTestBean;


    @Test
    public void consultarCompania(){

        genericTestBean.testQuery();
        genericTestBean.loadData();
    }

what Im guessin that should happen is: 1. GenericTestBean gets instantiated 2. testQuery is called from outside the proxy starting the transaction 2. loadData is called, the proxy sees the active transaction, and takes the existent resources 3. the data is retrieived 4. The transacion is closed...

But this doesn't happens, there seems to be two different transactions in each method call, and the entity gets detached between calls, issuing a lazy init exception.

the actual output log is this:

bean creado!  --here the object get created
Hibernate: select company0_.companyId as ..... --here the first query get executed
[com.pe.controlLines.data.dao.GenericTestBean.testQuery] --here we check the name for the transaction
is element atached? :true --and see if the element is attached to the session
[com.pe.controlLines.data.dao.GenericTestBean.loadData]  --this is in the second method call (with a different transaction name :O )
is element atached? :false --both now, the same element get detached

I tried re-attaching the entity, but this gives me a new query to the database (a new query to table EntityA is sent plus the query to get the objects, which I really don't like).

Im hoping to save one additional query just in order to have lazy loading, or does it have to be this way?, or maybe I have some configuration wrong?

Pdta: I think that the view filter option is even worst than the re-attaching option, it could lead to serious performance issues under high concurrency.

Can anyone please clarify the behavior of transaction contexts between methods calls, and how it get related to the session and the state of the entities?

the TransactionIndicatingUtil implementation is taken from here http://java.dzone.com/articles/monitoring-declarative-transac?page=0,1

and the generic dao is build after this idea General or specific DAO to record delivery with information from multiple tables?

Update

in case is of some use, here is the spring config file

<context:component-scan base-package="xxxxxx" />

    <context:annotation-config />
    <context:spring-configured />

    <aop:aspectj-autoproxy proxy-target-class="true"/>

    <!-- Data Source Declaration -->
    <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />

        <property name="url" value="jdbc:mysql://localhost:3306/xxxx" />

        <property name="username" value="xxxxx" />
        <property name="password" value="xxxxx" />
        <property name="initialSize" value="2" />
        <property name="minIdle" value="0" />
        <property name="minEvictableIdleTimeMillis" value="120000" />
        <property name="maxActive" value="20" />
        <property name="maxWait" value="5000" />
    </bean>

    <!-- Session Factory Declaration <bean id="SessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> -->
    <!-- Session Factory Declaration -->
    <bean id="SessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="myDataSource" />
        <property name="packagesToScan">
            <list>
                <value>com.xx.xx.xx.xx</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.search.default.directory_provider">filesystem</prop>
                <prop key="hibernate.search.default.indexBase">C:/DEVELOPMENT/lucene/indexes</prop>


            </props>
        </property>
    </bean>

    <!-- Enable the configuration of transactional behavior based on annotations -->
    <tx:annotation-driven transaction-manager="txManager"/>

    <!-- Transaction Manager is defined -->
    <bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="SessionFactory"/>
    </bean>
Community
  • 1
  • 1
Camilo Casadiego
  • 907
  • 3
  • 9
  • 33
  • I am not quite sure if you are talking about the (commented out) internal call from testQuery() to loadData() but if you are then: http://stackoverflow.com/a/8233459/1356423 – Alan Hay Oct 09 '14 at 15:31
  • Nop, actually when I uncomment it it works like I expect it to :s could it be some spring miss configuration issue? – Camilo Casadiego Oct 09 '14 at 15:40

2 Answers2

0

The JUnit test itself is not transactional. Each method of your GenericTestBean bean is transactional. So, each time the non-transactional test calls a method of the transactional bean, a transaction is started, the bean method is executed, and the transaction is committed. Since you call two transactional methods successively, so two separate transactions are started.

If the test method itself was transactional, then a transaction would be started for the test method, and (by default), the two bean methods would execute in the context of the existing transaction, started by the test. The transaction would commit after the test method has returned.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • huuuum I get it, but in the case the last element I have is a jsp, how can I keep the transaction and connection alive in order to have a single transaction for many operations? (also the lazy working) – Camilo Casadiego Oct 09 '14 at 16:35
0

The Test class should be transactional too

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "applicationContext.xml" })
@Transactional // <--- add
public class EntityQueryTest {

I am assuming that your applicationContext.xml file is the XML code shown in your post

Manuel Jordan
  • 15,253
  • 21
  • 95
  • 158
  • its the similar comment to the other option, if the first layer I have is a xhtml, not a class itself, how I accomplish this? I mean, to keep transnational management through various calls (for example a web wizard) – Camilo Casadiego Oct 09 '14 at 17:43
  • please correct me If I'm wrong. the spring proxy itself isn't transacctional right? it just creates and holds the transaction for each method call. In the case we propagated the transaction through the whole class, the session would keep a database connection open, which leads to the obvious problems. (maybe this is stupid)...Theres a way to remap the new connection to the existing entities without doing the query again?, I could be 100% sure about the detached entities not being modified at all by concurrent sessions, is this possible somehow? – Camilo Casadiego Oct 10 '14 at 14:19
  • **One**: Any bean with `@Transactional` is wrapped with a proxy to **add** transactional support (methods must be public, not sure if works for protected too). **Two**: When you run a Spring App, an `ApplicationContext` is created and manage all the beans. Consider Testing a special scenario where you must indicate to Spring **run** these `@Test` methods with Transaction support, thats why the class has `@Transactional`, where are in a Testing environment, not in a production/running environment. – Manuel Jordan Oct 10 '14 at 15:32
  • **Three** Either the beans annotated with `@Transactional` have no idea if they are working in a Testing or Production environment **Four**: Spring handles the resource management of the datasource, open, close connection, so the risk to have leaks **does not** exists. **Five**: Read the Spring Reference documentation about Transactions. – Manuel Jordan Oct 10 '14 at 15:32