Исключение LazyInitialization с помощью Spring и Hibernate - PullRequest
0 голосов
/ 11 января 2010

Я думаю, что мне не хватает чего-то фундаментального в том, как работает Hibernate, особенно с ленивой загрузкой. Моя проблема - отладка, так как я не уверен, является ли это проблемой Hibernate или скрытой проблемой Spring. Я решил спросить здесь, прежде чем делать какой-то серьезный рефакторинг.

У меня есть две сущности. Один содержит коллекцию другого в отношениях OneToMany. Для моей веб-страницы я хочу получить все первые объекты, а затем получить набор связанных объектов для каждого и отобразить их.

Я считаю, что моя проблема заключается в следующем: я использую 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;
}
}

Образец объекта

@Entity
public class Sample implements Serializable {


private Long id;
Date creationDate;
String audioFileLocation;
Integer votes;
String description;

public Sample(){
    creationDate = new Date();
    audioFileLocation = "";
    votes = 0;
    description = "";
}

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Long getId() {
    return id;
}

public void setId(Long id) {
    this.id = id;
}

public String getAudioFileLocation() {
    return audioFileLocation;
}

public void setAudioFileLocation(String audioFileLocation) {
    this.audioFileLocation = audioFileLocation;
}

@Temporal(TemporalType.DATE)
public Date getCreationDate() {
    return creationDate;
}

public void setCreationDate(Date creationDate) {
    this.creationDate = creationDate;
}

public String getDescription() {
    return description;
}

public void setDescription(String description) {
    this.description = description;
}

public Integer getVotes() {
    return votes;
}

public void setVotes(Integer votes) {
    this.votes = votes;
}
}

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");
}
}

Образец DAO

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

Контроллер

@RequestMapping(value = "/index.htm", method = RequestMethod.GET)
public ModelAndView sayHello(HttpServletRequest request,
        HttpServletResponse response) throws ServletException, IOException {

    Map<String, Object> model = new HashMap<String, Object>();
    List<LearningEntry> le = learningEntryService.getLearningEntries();
    model.put("learningEntries", le);
    return new ModelAndView("main", model);
}

View

<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 -->

Ответы [ 3 ]

1 голос
/ 11 января 2010

Я надеюсь, что вы используете findAll() метод для вызова. Вы можете загрузить все связанные образцы, изменив свой метод, как показано ниже.

public List<LearningEntry> findAll() {
   List<LearningEntry> entries = jpaTemplate.find("from LearningEntry");
   for(LearningEntry entry : entries){
      entry.getSamples().size();
   }
   return entries;
}

Или, как вы уже знаете, вы также можете добиться этого, изменив fetch на FetchType.EAGER. Но это может не подойти вам во всех случаях. Поэтому прежний способ лучше.

Или вы можете нигде не вносить изменений и определить другой метод для получения всех выборок на основе LearningEntry, таким образом вы сможете запустить вызов AJAX для какого-либо события. Но это может не подходить здесь в этом случае.

0 голосов
/ 11 января 2010

Большинство фреймворков предлагают шаблон «открытая сессия» Смотри https://www.hibernate.org/43.html:

Решение, в двухуровневых системах, с выполнением действия, доступ к данным через сессию и рендеринг зрения все в том же виртуальном машина, чтобы держать сессию открытой пока представление не будет отображено.

Для данных, которые часто читаются и редко обновляются, может также помочь кэширование запросов. Это уменьшает нагрузку на базу данных, но увеличивает использование памяти. Hibernate может быть настроен для вас.

0 голосов
/ 11 января 2010

Спасибо Vinegar за предоставленный рабочий ответ (upvoted).

Я решил добавить этот ответ, который также работал для меня. Я воспользовался этим подходом, потому что в будущем я могу захотеть сделать отдельные вызовы 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();      
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...