Java Spring和Hibernate的LazyInitialization异常

Java Spring和Hibernate的LazyInitialization异常,java,hibernate,spring,Java,Hibernate,Spring,我想我缺少了一些关于Hibernate工作原理的基本知识,特别是在惰性加载方面。我的问题是调试,因为我不确定这是Hibernate问题还是Spring问题。我想在进行一些重大重构之前,我会在这里问一下 我有两个实体。一方持有另一方的集合,形成一对一的关系。对于我的网页,我希望抓取所有第一个实体,然后抓取每个实体的关联实体集并显示它们 我相信我的问题在于:我使用JpaTemplate查找所有实体。这很好,但是由于延迟加载,我没有得到相关实体的关联集。在我的视图(jsp)中,我希望访问这个集合,但它

我想我缺少了一些关于Hibernate工作原理的基本知识,特别是在惰性加载方面。我的问题是调试,因为我不确定这是Hibernate问题还是Spring问题。我想在进行一些重大重构之前,我会在这里问一下

我有两个实体。一方持有另一方的集合,形成一对一的关系。对于我的网页,我希望抓取所有第一个实体,然后抓取每个实体的关联实体集并显示它们

我相信我的问题在于:我使用JpaTemplate查找所有实体。这很好,但是由于延迟加载,我没有得到相关实体的关联集。在我的视图(jsp)中,我希望访问这个集合,但它当然是空的,因为它是延迟加载的。现在,我得到一个LazyInitialization异常,声明事务已经结束。对我来说这是有道理的,当然交易现在应该结束了。问题是,如果事务结束,关联集怎么可能延迟加载?

实体类:

@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();      
}