0

Edit5:

The code used to explore the solution for this question is up on bitbucket: https://bitbucket.org/jcalleja/lazy-init-exception


I realise there are many other LazyInitializationException posts on SO, but I couldn't get the information I need from them.

The projects involved:

  • memdrill-data
  • data-use

memdrill-data is a Spring Roo managed project. There is no web component here. I have simply:

persistence setup --provider HIBERNATE --database HYPERSONIC_PERSISTENT

and added some entities. For instance:

@RooJavaBean
@RooToString
@RooEntity
public class Item {

    @NotNull
    @Column(unique = true)
    @Size(min = 1, max = 200)
    private String itemId;

    @NotNull
    @Lob
    @OneToOne(cascade = javax.persistence.CascadeType.ALL, fetch = FetchType.LAZY)
    private LobString content;
}

@RooJavaBean
@RooEntity
public class LobString {

    @Lob
    @Basic(fetch=FetchType.LAZY)
    private String data;

    public LobString() {
        super();
    }

    public LobString(String data) {
        this.data = data;
    }
}

It shouldn't matter, but for the record, I am using Roo 1.1.4. Probably, the applicationContext.xml is not needed in this discussion but here it is anyway (untouched after generated by Roo):

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd         http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd         http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd         http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

<context:property-placeholder location="classpath*:META-INF/spring/*.properties"/>

<context:spring-configured/>

<context:component-scan base-package="com.memdrill.data">
    <context:exclude-filter expression=".*_Roo_.*" type="regex"/>
    <context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
</context:component-scan>
<bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dataSource">
    <property name="driverClassName" value="${database.driverClassName}"/>
    <property name="url" value="${database.url}"/>
    <property name="username" value="${database.username}"/>
    <property name="password" value="${database.password}"/>
    <property name="testOnBorrow" value="true"/>
    <property name="testOnReturn" value="true"/>
    <property name="testWhileIdle" value="true"/>
    <property name="timeBetweenEvictionRunsMillis" value="1800000"/>
    <property name="numTestsPerEvictionRun" value="3"/>
    <property name="minEvictableIdleTimeMillis" value="1800000"/>
</bean>
<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory">
    <property name="persistenceUnitName" value="persistenceUnit"/>
    <property name="dataSource" ref="dataSource"/>
</bean>

data-use depends on memdrill-data and I'm just test driving memdrill-data through it for now. This is all that's going on in data-use:

public static void main(String[] args) {
    ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(
            "classpath*:META-INF/spring/applicationContext*.xml");

    Item item = new Item();
    item.setItemId("abc123");
    item.setContent(new LobString("this is the content of the item"));

    item.persist();

    System.out.println("persist - OK");

    List<Item> items = Item.findAllItems();

    Item i = items.get(0);
    System.out.println("i.getId() = " + i.getId());
    System.out.println("i.getItemId() = " + i.getItemId());
    System.out.println("i.getContent() = " + i.getContent());
    System.out.println("i.getContent().getId() = " + i.getContent().getId());
    System.out.println("i.getContent().getData() = " + i.getContent().getData());

    appContext.close();
}

findAllItems() is the default one generated by Roo in Item_Roo_Entity.aj:

public static List<Item> Item.findAllItems() {
    return entityManager().createQuery("SELECT o FROM Item o", Item.class).getResultList();
}

This is what I get:

persist - OK
i.getId() = 1
i.getItemId() = abc123
2014-07-05 13:23:30,732 [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:167)
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:215)
    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190)
    at com.memdrill.data.util.LobString_$$_javassist_0.toString(LobString_$$_javassist_0.java)
    at java.lang.String.valueOf(String.java:2826)
    at java.lang.StringBuilder.append(StringBuilder.java:115)
    at com.memdrill.prototypes.datause.Main.main(Main.java:34)

So when I try to access the lazily loaded data to fill in the LobString POJO, there's no session in progress and it blows up.

The closest I can get to (in my code i.e. not the libraries I'm depending on) putting in the LobString access code (item.getContent().getId() for e.g.) is something like pushing in the findAllItems() or creating a new method like this just to illustrate the point:

public static List<Item> findAllItemsWithRefs() {
    List<Item> items = entityManager().createQuery("SELECT o FROM Item o", Item.class).getResultList();
    for(Item item : items) {
        System.out.println("item.getContent().getId() = " + item.getContent().getId());
    }
    return items;
}

That still gives me the same exception implying that there is no session going on exactly after .getResultList() returns the List.

I have tried, as other SO posts have suggested, to put @Transactional over the method but I still get the LazyInitializationException. But even if that did work... why do I need to start a transaction to read data from the db? Aren't transactions meant for preserving data integrity while writing?

It seems to me that one way around this would be to somehow specify a different query from "SELECT o FROM Item o"... one which also gets the LobString data... but this doesn't seem quite right even if it would fly in this particular case.

I was expecting Hibernate to start a new session when I access data it hasn't loaded yet... but obviously, it doesn't work as I expect.

Can someone familiar with Hibernate explain what's going on in this case and maybe suggest what options are available to deal with this?

Thanks

Edit1:

Btw, if I add the following log4j.propeties to the classpath:

log4j.rootLogger=error, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

# Print the date in ISO 8601 format
log4j.appender.stdout.layout.ConversionPattern=%d [%t] %-5p %c - %m%n

log4j.logger.org.hibernate.SQL=DEBUG
log4j.logger.org.hibernate.type=TRACE

I get:

persist - OK
2014-07-05 16:17:35,707 [main] DEBUG org.hibernate.SQL - select item0_.id as id1_, item0_.content as content1_, item0_.item_id as item2_1_, item0_.version as version1_ from item item0_
2014-07-05 16:17:35,711 [main] TRACE org.hibernate.type.descriptor.sql.BasicExtractor - found [1] as column [id1_]
2014-07-05 16:17:35,713 [main] TRACE org.hibernate.type.descriptor.sql.BasicExtractor - found [1] as column [content1_]
2014-07-05 16:17:35,713 [main] TRACE org.hibernate.type.descriptor.sql.BasicExtractor - found [abc123] as column [item2_1_]
2014-07-05 16:17:35,713 [main] TRACE org.hibernate.type.descriptor.sql.BasicExtractor - found [0] as column [version1_]
i.getId() = 1
i.getItemId() = abc123
2014-07-05 16:17:35,717 [main] ERROR org.hibernate.LazyInitializationException - could not initialize proxy - no Session

So as the line "found 1 as column [content1_] shows, I at least have the foreign key of the row in the table housing the LobString data... but I can't access that id without getting a LazyInitializationException. Maybe I could fetch the LobString by id if I could get hold of it... but i.getContent() and i.getContent().getId() give the exception. The foreign key should match the primary key id of the LobString table i.e. i.getContent().getId().. but there seems to be no way of accessing the foreign key from i... which as the logs show, was actually fetched from the db.

In any case, even if I did get the foreign key... having to stay LobString.find(id) or something like that is not a nice solution. Hibernate is supposed to populate your object graph...

Edit2:

2 points to make in this edit. Execution passes through the following: org.springframework.orm.jpa.SharedEntityManagerCreator.DeferredQueryInvocationHandler.invoke(Object, Method, Object[]):

        // Invoke method on actual Query object.
        try {
            Object retVal = method.invoke(this.target, args);
            return (retVal == this.target ? proxy : retVal);
        }
        catch (InvocationTargetException ex) {
            throw ex.getTargetException();
        }
        finally {
            if (method.getName().equals("getResultList") || method.getName().equals("getSingleResult") ||
                    method.getName().equals("executeUpdate")) {
                EntityManagerFactoryUtils.closeEntityManager(this.em);
            }
        }

First of all, the EntityManager is being closed if "getResultList" is used. I'll assume that the EntityManager is a JPA construct that, without going into details, maps to Hibernate's Session.

Second... if I drill down enough while debugging I can see what should be lazily loaded data "this is the content of the item"... so I actually have that data loaded in memory... 1) since it's lazy it shouldn't 2) even though it's there I cannot access it since I get the LazyInitializationException.

While debugging, I can see the data is loaded in the following two places:

1) My main method (see above: Item i = items.get(0) in the main method) com.memdrill.prototypes.datause.Main.main(String[]):

i -> content -> handler -> target -> data: "this is the content of the item"

2) In the method that closes the Session: org.springframework.orm.jpa.SharedEntityManagerCreator.DeferredQueryInvocationHandler.invoke(Object, Method, Object[]):

retVal -> elementData[0] -> content -> handler -> target -> data: "this is the content of the item"

So much for lazily loaded data... what am I doing so terribly wrong?

I seem to have got the specification of LobString to be lazily loaded too. But if that's the case, and I do have the content in memory... why can't I access it?

Edit3:

It looks like the behaviour in Edit2 (to have the lazily loaded data in memory) only happens while debugging if I stay long enough in debugging mode.....

So basically, if I run in debug and just hit "play" "play" "play" all the way through my breakpoints, I get the LazyInitializationException... but if I stay long enough in debug mode, no LazyInitializationException, I get the data and print to standard out.

Any ideas?

Edit4:

Thanks to SO question I am now able to eagerly fetch all of LobString using:

    EntityManager em = Item.entityManager();
    List<Item> items = em.createQuery("SELECT o FROM Item o JOIN FETCH o.content", Item.class).getResultList();

Output:

2014-07-06 21:44:56,862 [main] INFO  org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean - Building JPA container EntityManagerFactory for persistence unit 'persistenceUnit'
in LobString default constructor
in LobString (String data) constructor
2014-07-06 21:44:57,891 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Creating new transaction with name [com.memdrill.data.entity.Item.persist]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2014-07-06 21:44:57,944 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Opened new EntityManager [org.hibernate.ejb.EntityManagerImpl@779d9c0d] for JPA transaction
2014-07-06 21:44:57,948 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Not exposing JPA transaction [org.hibernate.ejb.EntityManagerImpl@779d9c0d] as JDBC transaction because JpaDialect [org.springframework.orm.jpa.DefaultJpaDialect@d7e60a1] does not support JDBC Connection retrieval
2014-07-06 21:44:57,999 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Initiating transaction commit
2014-07-06 21:44:58,000 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Committing JPA transaction on EntityManager [org.hibernate.ejb.EntityManagerImpl@779d9c0d]
2014-07-06 21:44:58,002 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Closing JPA EntityManager [org.hibernate.ejb.EntityManagerImpl@779d9c0d] after transaction
2014-07-06 21:44:58,002 [main] DEBUG org.springframework.orm.jpa.EntityManagerFactoryUtils - Closing JPA EntityManager
persist - OK - in Main of memdrill-data not data-use
2014-07-06 21:44:58,008 [main] DEBUG org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler - Creating new EntityManager for shared EntityManager invocation
in LobString default constructor
2014-07-06 21:44:58,133 [main] DEBUG org.springframework.orm.jpa.EntityManagerFactoryUtils - Closing JPA EntityManager
i.getId() = 1
i.getItemId() = abc123
i.getContent() = com.memdrill.data.util.LobString@6fe30af
i.getContent().getId() = 1
i.getContent().getData() = this is the content of the item
2014-07-06 21:44:58,134 [main] INFO  org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean - Closing JPA EntityManagerFactory for persistence unit 'persistenceUnit'

This is almost what I want. I just want the ids of the LobString. The idea is that this is going to be a REST call and I just want to return the Item "basic" data and ids of the LobString which can be used for separate requests.

Any idea what kind of JPQL query would achieve this? (any comments in general to help with understanding what's going on under the hood would be appreciated) - thanks

Community
  • 1
  • 1
justin
  • 3,357
  • 1
  • 23
  • 28

3 Answers3

1

To solve this you can use the @Transactional annotation on a no-static method.

Example:

@RooJavaBean
@RooToString
@RooEntity
public class Item {

    @NotNull
    @Column(unique = true)
    @Size(min = 1, max = 200)
    private String itemId;

    @NotNull
    @Lob
    @OneToOne(cascade = javax.persistence.CascadeType.ALL, fetch = FetchType.LAZY)
    private LobString content;

    @Transactional(readOnly=true)
    LobString loadContent(){
       getContent().getId();
       return getContent();
    }
}

Then, use the loadContent() method to get LobString.

This happens because when you call i.getContent() the connection to DB used to load data is already closed.

Using a method annotated @Transactional (warning it must be no-static method) forces the context to create (or continue, see annotation's JavaDoc) a DB connection before execute the method body. So, when the Proxy inserted by hibernate in Item.content property try to load data from DB, it founds a alive connection.

jmvivo
  • 2,653
  • 1
  • 16
  • 20
  • Hi jmvivo - thanks for answering :) I'm not sure I understand how loadContent() is meant to be used. After findAllItems() the session is closed so if I try to access Item's content using either getContent() or loadContent() I get the lazy init exception. Btw, I have uploaded the code to bitbucket: https://bitbucket.org/jcalleja/lazy-init-exception – justin Jul 07 '14 at 08:35
  • I just updated my code to force loading. I think should solve problem using `loadContent()` – jmvivo Jul 07 '14 at 10:03
  • Same result - getting the exception. You can try it out in the project on bitbucket if you want to. The thing is that after I call findAllItems() the Hibernate Session is closed and any data which was not loaded in the original query "SELECT o FROM Item o" is not accessible unless you start another Transaction. It is unfortunate that I am now writing a test to assert expected data loaded and have a different behaviour... – justin Jul 07 '14 at 10:34
  • The behaviour ended up being as expected. I had pushed in @ Transactional in a Roo generated test. I pushed it in so I could comment it out but Roo was running so it re-generated @ Transactional on the aj file. This messed me up a bit. In any case, I sorted that out and when running the test outside of a transaction, it is consistently passing. Not sure if this is the proper way to test this (outside transaction).. but that's how the code is set up unfortunately. The real answer to this question (I guess) would be how/why the transaction is scoped to getResultList() – justin Jul 07 '14 at 16:16
  • "@Transactional annotation on a _no-static_ method" was the answer to my lazy loading problem - thanks a lot. – rmv Oct 16 '14 at 21:08
0

Ok, so the answer was on SO after all: SO getid

I ended up pushing in private Long id in LobString_Roo_Entity.aj and adding the @Access(AccessType.PROPERTY) annotation:

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
@Access(AccessType.PROPERTY)
private Long id;

Now, using Item.findAllItems() as shown below:

    Item item = new Item();
    item.setItemId("abc123");
    item.setContent(new LobString("this is the lob data"));

    item.persist();

    System.out.println("persist - OK");

    List<Item> items = Item.findAllItems();

    Item i = items.get(0);

    System.out.println("i.getId() = " + i.getId());
    System.out.println("i.getItemId() = " + i.getItemId());
    //      System.out.println("i.getContent() = " + i.getContent());
    System.out.println("i.getContent().getId() = " + i.getContent().getId());
    System.out.println("i.getContent().getData() = " + i.getContent().getData());

I get the LazyInitializationException when I try to i.getContent().getData() or even if I try i.getContent() (which is why it's commented out). But, if I do i.getContent().getId()... I get the id. It's weird how that works (as in, i.getContent() blows up but i.getContent().getId() doesn't).

Here's the console output for the code above:

i.getId() = 1
i.getItemId() = abc123
i.getContent().getId() = 1
2014-07-06 22:32:51,309 [main] ERROR org.hibernate.LazyInitializationException - could not initialize proxy - no Session
org.hibernate.LazyInitializationException: could not initialize proxy - no Session

If the commented out line is uncommented, the LazyInitializationException happens before i.getContent().getId() = 1 is printed.

This is the behaviour I was looking for... unfortunately... I don't really understand what's going on. If you think you can explain a bit of what's going on, please do!

Thanks

Edit1:

Note: I do not consider this as an answer because it only answers this particular case. I need to figure out how to demarcate the transactions properly. I am guessing I could instantiate the transactions manually and have that cross cutting concern in the code. What I would like is to figure out the current configuration. What is limiting the transaction to findAllItems()? And how can that be configured otherwise?

Community
  • 1
  • 1
justin
  • 3,357
  • 1
  • 23
  • 28
0

This answer lists some of the mistakes I was making in case you're doing the same. The mistakes are with regards to transaction demarcation.

Mistake 1:

In the Roo project's pom file I have:

            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <!-- NB: do use 1.3 or 1.3.x due to MASPECTJ-90 - wait for 1.4 -->
<!--                 <version>1.5</version> -->
                <version>1.2</version>


                <dependencies>
                    <!-- NB: You must use Maven 2.0.9 or above or these are ignored (see MNG-2972) -->
                    <dependency>
                        <groupId>org.aspectj</groupId>
                        <artifactId>aspectjrt</artifactId>
                        <version>${aspectj.version}</version>
                    </dependency>
                    <dependency>
                        <groupId>org.aspectj</groupId>
                        <artifactId>aspectjtools</artifactId>
                        <version>${aspectj.version}</version>
                    </dependency>
                </dependencies>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>test-compile</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <outxml>true</outxml>
                    <aspectLibraries>
                        <aspectLibrary>
                            <groupId>org.springframework</groupId>
                            <artifactId>spring-aspects</artifactId>
                        </aspectLibrary>
                    </aspectLibraries>
                    <complianceLevel>1.6</complianceLevel>
                    <source>1.6</source>
                    <target>1.6</target>
<!--                     <complianceLevel>${java.version}</complianceLevel> -->
<!--                    <source>${java.version}</source> -->
<!--                    <target>${java.version}</target> -->
                </configuration>
            </plugin>

i.e. spring-aspects are being compiled into the project and as the spring configuration up in the question shows, my Spring context is configured to use: mode="aspectj" in <tx:annotation-driven>. Now, I take it Spring has some way of being configured to generate these aspects at runtime, but in my case, I was oblivious to everything, using @Transactional in another project which did not have the aspect compilation in its build and so no aspects were generated .. so yes, obviously the @Transactional become pretty useless.

If you tree ./target/classes where maven generates your project's compiled code, you should see something like $AjcClosure1.class or something. These weren't generated in the project without the aspectj compilation in the pom.xml but using @Transactional anyway - (also you can use javap -classpath ./target/classes com.blah.MyClass to see woven code on your original class).

So lesson 1: Make sure you're using @Transactional in a project that actually weaves spring-aspects in your code when you're using mode="aspectj".

Mistake2:

This is from trial and error so far but the @Transactional annotated method apparently needs to be non-static public. This worked:

@Transactional(readOnly=true)
    public List<Item> findAllItemsWithContentId() {
        List<Item> items = entityManager().createQuery(
                "SELECT o FROM Item o", Item.class)
                .getResultList();

        for(Item item: items) {
            System.out.println("about to item.getContent().getId()");
            System.out.println(item.getContent().getId());
        }
        return items;
    }

As in, I could get the transaction to span for the whole method not just the getResultList() invocation.

Unfortunately, this also seems to fetch all the LobString's data (even though it's lazily defined). i.e. if I try:

List<Item> items = new Item().findAllItemsWithContentId();
Item i = items.get(0);

System.out.println("i.getId() = " + i.getId());
System.out.println("i.getItemId() = " + i.getItemId());
System.out.println("i.getContent() = " + i.getContent());
System.out.println("i.getContent().getId() = " + i.getContent().getId());
System.out.println("i.getContent().getData() = " + i.getContent().getData());

I get no LazyInitializationException meaning that accessing item.getContent().getId() while still in a transaction in findAllItemsWithContentId() ended up fetching all the data. If I leave the for loop out of findAllItemsWithContentId(), then I do get the LazyInitializationException from the System.out.println's for data which was not fetched in the transactional method.

... so yes, that is another problem.

But note, this was done without the @Access(AccessType.PROPERTY) in my other answer. If that's used then I can get the id of the LobString without getting the content. This answer then gives some info on extending the transaction scope.

So basically: 1. use in project which compiles aspects 2. use on public instance method.

Cheers

justin
  • 3,357
  • 1
  • 23
  • 28