Java Spring和Hibernate的LazyInitialization异常
我想我缺少了一些关于Hibernate工作原理的基本知识,特别是在惰性加载方面。我的问题是调试,因为我不确定这是Hibernate问题还是Spring问题。我想在进行一些重大重构之前,我会在这里问一下 我有两个实体。一方持有另一方的集合,形成一对一的关系。对于我的网页,我希望抓取所有第一个实体,然后抓取每个实体的关联实体集并显示它们 我相信我的问题在于:我使用JpaTemplate查找所有实体。这很好,但是由于延迟加载,我没有得到相关实体的关联集。在我的视图(jsp)中,我希望访问这个集合,但它当然是空的,因为它是延迟加载的。现在,我得到一个LazyInitialization异常,声明事务已经结束。对我来说这是有道理的,当然交易现在应该结束了。问题是,如果事务结束,关联集怎么可能延迟加载? 实体类:Java Spring和Hibernate的LazyInitialization异常,java,hibernate,spring,Java,Hibernate,Spring,我想我缺少了一些关于Hibernate工作原理的基本知识,特别是在惰性加载方面。我的问题是调试,因为我不确定这是Hibernate问题还是Spring问题。我想在进行一些重大重构之前,我会在这里问一下 我有两个实体。一方持有另一方的集合,形成一对一的关系。对于我的网页,我希望抓取所有第一个实体,然后抓取每个实体的关联实体集并显示它们 我相信我的问题在于:我使用JpaTemplate查找所有实体。这很好,但是由于延迟加载,我没有得到相关实体的关联集。在我的视图(jsp)中,我希望访问这个集合,但它
@Entity
public class LearningEntry implements Serializable {
private Long id;
String imagePath = "";
Set<Sample> samples = null;
//------------------------------
// Constructors
//------------------------------
public LearningEntry(){
imagePath = "";
samples = new HashSet<Sample>();
}
//------------------------------
// Instance Methods
//------------------------------
public void addSample(Sample s){
samples.add(s);
}
public void removeSample(Sample s){
samples.remove(s);
}
//------------------------------
// Setters and Getters
//------------------------------
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
//@Column(name = "wisi_LE_IMAGEPATH", length = 100, nullable = false)
public String getImagePath() {
return imagePath;
}
public void setImagePath(String imagePath) {
this.imagePath = imagePath;
}
// TODO - ONly works with fetch type EAGER
//@OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
public Set<Sample> getSamples() {
return samples;
}
public void setSamples(Set<Sample> samples) {
this.samples = samples;
}
}
DAO类:
LearningEntryDAO
@Transactional
public class JpaLearningEntryDAO implements LearningEntryDAO{
private JpaTemplate jpaTemplate;
public JpaLearningEntryDAO(){
}
public void setJpaTemplate(JpaTemplate jpaTemplate){
this.jpaTemplate = jpaTemplate;
}
@Override
//@Transactional
public void delete(Long leId) {
LearningEntry dp = jpaTemplate.find(LearningEntry.class, leId);
jpaTemplate.remove(dp);
}
@Override
@SuppressWarnings("unchecked")
//@Transactional
public List<LearningEntry> findAll() {
return jpaTemplate.find("from LearningEntry");
}
@Override
//@Transactional
public LearningEntry findById(Long leId) {
return jpaTemplate.find(LearningEntry.class, leId);
}
@Override
//@Transactional
public LearningEntry store(LearningEntry dp) {
return jpaTemplate.merge(dp);
}
@Override
@SuppressWarnings("unchecked")
//@Transactional
public void deleteAll(){
throw new RuntimeException("deleteAll not implemented");
}
}
@Transactional
public class JpaSampleDAO implements SampleDAO{
private JpaTemplate jpaTemplate;
public JpaSampleDAO(){}
public void setJpaTemplate(JpaTemplate jpaTemplate){
this.jpaTemplate = jpaTemplate;
}
@Override
//@Transactional
public void delete(Long sampleId) {
Sample dp = jpaTemplate.find(Sample.class, sampleId);
jpaTemplate.remove(dp);
}
@Override
@SuppressWarnings("unchecked")
public List<Sample> findAll() {
return jpaTemplate.find("from Sample");
}
@Override
public Sample findById(Long sampleId) {
return jpaTemplate.find(Sample.class, sampleId);
}
@Override
public Sample store(Sample dp) {
return jpaTemplate.merge(dp);
}
@Override
@SuppressWarnings("unchecked")
public void deleteAll(){
throw new RuntimeException("deleteAll not implemented");
}
}
我希望您正在使用
findAll()
方法来结束调用。您可以通过如下修改方法加载所有关联的示例
public List<LearningEntry> findAll() {
List<LearningEntry> entries = jpaTemplate.find("from LearningEntry");
for(LearningEntry entry : entries){
entry.getSamples().size();
}
return entries;
}
公共列表findAll(){
List entries=jpaTemplate.find(“从LearningEntry”);
用于(学习条目:条目){
entry.getSamples().size();
}
返回条目;
}
或者,如您所知,您也可以通过将fetch
更改为FetchType.EAGER
来实现这一点。但这可能并不适合所有情况。所以,前者更好
或者,您可能希望在任何地方都不做任何更改,并定义另一种方法来基于
LearningEntry
获取所有示例,这样您就能够在某些事件上启动AJAX调用。但在这种情况下,这可能不适合这里。多亏了醋提供了一个有效的答案(投票)
我决定添加这个同样对我有用的答案。我之所以采用这种方法,是因为我将来可能希望进行单独的ajax调用。换句话说,我可以在一个事务中请求LearningEntry,而不是在以后的某个时间请求它的样本
@Transactional
public Set<Sample> getSamplesForLearningEntry(LearningEntry le) {
// Reload the le from the database so it is not transient:
LearningEntry le = leDAO.store(le);
le.getSamples.size();
return le.getSamples();
}
@Transactional
公共设置getSamplesForLearningEntry(LearningEntry le){
//从数据库重新加载le,使其不是暂时的:
LearningEntry le=leDAO.store(le);
le.getSamples.size();
返回le.getSamples();
}
大多数框架都提供“在视图中打开会话”模式。见:
解决方案在两层系统中,
通过操作执行,数据访问
通过会话和渲染
在同一虚拟环境中查看所有内容
机器,用于保持会话打开
直到视图被渲染
对于经常读取且几乎从不更新的数据,查询缓存也有帮助。这减少了数据库的负载,但增加了内存使用。Hibernate可以配置为为您执行此操作。ahhh,因此,如果我想获取与给定LearningEntry相关的示例实体集,我需要对数据库进行全新调用?这很有道理,尽管这比我希望的要多。也许LearningEntryDAO中的一个方法,如getSampleForLearningEntry(LearningEntry le)是合适的?是的,但现在看看如何将两个表混合到一个DAO中,尽管您有一个SampleDAO,但请从LearningEntryDAO中询问示例。依我看,最好是在用例驱动的设计中进行。这里可能不需要两个单独的DAO。延迟加载也意味着要访问DB。是的,没错,这就是我在第三点中提到的。
<section id="content" class="body">
<ol id="posts-list" class="hfeed">
<c:forEach items="${learningEntries}" var="learningEntry">
<li>
<table class="wisiEntry">
<tr>
<td class="pictureCell">
<img class="wisiEntry-pic" src="${learningEntry.imagePath}" />
</td>
<td class="previousNextCell"
<div class="wisiEntry-nextSampleButton">Next</div>
<div class="wisiEntry-previousSampleButton">Previous</div>
<br />
<div class="wisiEntry-addTagButton">Tag</div>
<div class="wisiEntry-addCommentButton">Comment</div>
<br />
<div class="wisiEntry-uploadButton">Upload</div>
</td>
<td>
<!-- ERROR HAPPENS HERE. Samples should not be null -->
<c:forEach items="${learningEntry.samples}" var="sample" varStatus = "status">
<table class="sampleEntry" ${status.first ? '' : 'style = "display:none"'}>
<tr>
<td class="sampleCell">
<p class="description">
${sample.description}
</p>
<audio src="${sample.audioFileLocation}" controls>
Your browser does not support the <code>audio</code> element.
</audio>
</td>
<td class="voteCell">
<img class="upVote" src="/images/upArrow.jpeg" />
<span class="voteNumber">${sample.votes}</span>
<img class="downVote" src="/images/downArrow.jpeg" />
</td>
</tr>
</table>
</c:forEach>
</td>
</tr>
</table>
</li>
</c:forEach>
</ol><!-- /#posts-list -->
</section><!-- /#content -->
public List<LearningEntry> findAll() {
List<LearningEntry> entries = jpaTemplate.find("from LearningEntry");
for(LearningEntry entry : entries){
entry.getSamples().size();
}
return entries;
}
@Transactional
public Set<Sample> getSamplesForLearningEntry(LearningEntry le) {
// Reload the le from the database so it is not transient:
LearningEntry le = leDAO.store(le);
le.getSamples.size();
return le.getSamples();
}