使用SpringRoo项目并获取org.hibernate.LazyInitializationException:无法初始化代理-无会话
Edit5: 用于探索此问题解决方案的代码位于bitbucket上:使用SpringRoo项目并获取org.hibernate.LazyInitializationException:无法初始化代理-无会话,hibernate,spring-roo,spring-orm,Hibernate,Spring Roo,Spring Orm,Edit5: 用于探索此问题解决方案的代码位于bitbucket上: 我意识到还有很多其他的LazyInitializationException帖子,但是我无法从他们那里得到我需要的信息 涉及的项目包括: memdrill数据 数据使用 memdrill数据是Spring Roo管理的项目。这里没有web组件。我只是: persistence setup --provider HIBERNATE --database HYPERSONIC_PERSISTENT 并添加了一些实体。例如:
我意识到还有很多其他的LazyInitializationException帖子,但是我无法从他们那里得到我需要的信息 涉及的项目包括:
- memdrill数据
- 数据使用
persistence setup --provider HIBERNATE --database HYPERSONIC_PERSISTENT
并添加了一些实体。例如:
@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;
}
}
这不要紧,但为了记录在案,我使用的是Roo1.1.4。本次讨论中可能不需要applicationContext.xml,但在这里它仍然存在(由Roo生成后未被触及):
因此,当我尝试访问延迟加载的数据以填充LobString POJO时,没有正在进行的会话,它会崩溃
我能找到的最接近LobString访问代码(例如,item.getContent().getId())的地方(在我的代码中,即不是我所依赖的库中)是类似于推入findAllItems()或创建这样的新方法来说明这一点:
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;
}
我得到:
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
因此,正如“find as column[content1_uz]一行所示,我至少拥有表中包含LobString数据的行的外键……但是如果不获得LazyInitializationException,我就无法访问该id。如果我能够获得LobString,也许我可以通过id获取它……但是I.getContent()和I.getContent().getId()给出异常。外键应与LobString表的主键id匹配,即i.getContent().getId()…但似乎无法从i…访问外键,如日志所示,它实际上是从db中获取的
在任何情况下,即使我得到了外键…必须保持LobString.find(id)或类似的方式不是一个好的解决方案。Hibernate应该填充您的对象图
Edit2:
在这次编辑中需要指出的两点。执行过程包括以下几点:org.springframework.orm.jpa.SharedEntityManagerCreator.DeferredQueryInvocationHandler.invoke(Object,Method,Object[])
:
首先,如果使用“getResultList”,EntityManager将被关闭。我将假设EntityManager是一个JPA构造,它映射到Hibernate的会话,而不进行详细说明
第二…如果我在调试时深入研究,我可以看到应该延迟加载的数据“这是项的内容”…因此我实际上在内存中加载了该数据…1)因为它是延迟的,所以它不应该2)即使它在那里,我也无法访问它,因为我得到了LazyInitializationException
调试时,我可以看到数据加载在以下两个地方:
1) 我的主方法(请参见上文:主方法中的Item i=items.get(0))
com.memdrill.prototype.datause.Main.Main(字符串[])
:
i->content->handler->target->data:“这是项目的内容”
2) 在关闭会话的方法中:
org.springframework.orm.jpa.SharedEntityManagerCreator.DeferredQueryInvocationHandler.invoke(对象、方法、对象[])
:
retVal->elementData[0]->content->handler->target->data:“这是项的内容”
懒散加载的数据就这么多了……我做错了什么
我似乎也有LobString的规范要延迟加载。但如果是这样的话,而且我的内存中确实有内容……为什么我不能访问它
Edit3:
看起来Edit2中的行为(在内存中延迟加载数据)只有在调试时才会发生,如果我在调试模式中停留足够长的时间
因此,基本上,如果我在调试模式下运行,在断点处一直点击“播放”“播放”“播放”,我会得到LazyInitializationException…但是如果我在调试模式下停留足够长的时间,没有LazyInitializationException,我会得到数据并打印到标准输出
有什么想法吗
Edit4:
多亏了,我现在能够使用以下工具急切地获取所有LobString:
EntityManager em = Item.entityManager();
List<Item> items = em.createQuery("SELECT o FROM Item o JOIN FETCH o.content", Item.class).getResultList();
这几乎就是我想要的。我只想要LobString的ID。我的想法是这将是一个REST调用,我只想返回项目“基本”数据和LobString的ID,它们可以用于单独的请求
你知道什么样的JPQL查询可以实现这一点吗?(任何有助于理解引擎盖下发生的事情的评论都将不胜感激)-谢谢好的,所以答案毕竟是开着的: 最后,我在LobString\u Roo\u Entity.aj中输入了
private Long id
,并添加了@Access(AccessType.PROPERTY)
注释:
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
@Access(AccessType.PROPERTY)
private Long id;
现在,使用Item.findAllItems()如下所示:
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.getContent().getId()=1
之前会发生LazyInitializationException
这是我一直在寻找的行为……不幸的是……我真的不明白发生了什么。如果你认为你能解释一下发生了什么,请解释一下
谢谢
Edit1:
注:我不认为这是一个答案,因为它只回答了这个特殊的情况。我需要弄清楚如何恰当地划定事务。我猜想我可以手动实例化交易并在代码中有交叉切关。我想知道当前的配置。限制T的是什么?Transaction to findAllItems()?如何配置它?要解决这个问题,可以在非静态方法上使用
@Transactional
注释
例如:
@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();
}
}
然后,使用loadContent()
方法获取LobString
这是因为当您调用i.getContent()
时,用于加载数据的数据库连接已经关闭
使用带注释的方法@transaction a
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
// 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);
}
}
EntityManager em = Item.entityManager();
List<Item> items = em.createQuery("SELECT o FROM Item o JOIN FETCH o.content", Item.class).getResultList();
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'
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
@Access(AccessType.PROPERTY)
private Long id;
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.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
@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();
}
}
<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>
@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;
}
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());