LazyInitializationException даже с @Transactional - PullRequest
0 голосов
/ 17 февраля 2019

С трудом заставляет работать простую OneToMany, сопоставляемую сущностью ManyToOne, когда я иду за комментариями пользователя.Другие ответы предполагают, что вы должны создать запрос самостоятельно с помощью entityManager, но это кажется ужасным.Какой смысл в ORM, если вы не можете даже сделать что-то простое без жесткого кодирования встроенного SQL?Скорее всего, я делаю что-то не так.

Похоже, что это как-то связано с тем, что я обращаюсь к методу user.getComments () из jsp с помощью модели.Не уверен, что лучший способ сделать это.

Схема:

CREATE TABLE users (
    id INTEGER AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(255) UNIQUE NOT NULL,
    created_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE comments (
    id INTEGER AUTO_INCREMENT PRIMARY KEY,
    comment_text VARCHAR(255) NOT NULL,
    photo_id INTEGER NOT NULL,
    user_id INTEGER NOT NULL,
    created_at TIMESTAMP DEFAULT NOW(),
    FOREIGN KEY(user_id) REFERENCES users(id)
);

Метод контроллера пользователя:

@RequestMapping("/user")
public ModelAndView getUser(@RequestParam int id) {
    return new ModelAndView("user", "message", userService.getUser(id));
}

UserService:

@Service
public class UserService {

    private UserDAO userDAO;

    @Autowired
    public UserService(UserDAO userDAO) {
        this.userDAO = userDAO;
    }

    @Transactional
    public User getUser(int id) {
        return userDAO.getUser(id);
    }

}

UserDAO:

@Repository
public class UserDAO {

    @PersistenceContext
    EntityManager entityManager;

    @Nullable
    public User getUser(int id)
    {
        return entityManager.find(User.class, id);
    }
}

Сущность пользователя:

@Entity
@Table(name="users")
public class User {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="id")
    private int id;

    @Column(name="username")
    private String userName;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name="created_at")
    private Date createdAt;

    @OneToMany(mappedBy="user")
    private List<Comment> comments;

    public int getId() {
        return id;
    }

    public String getUserName() {
        return userName;
    }

    public Date getCreatedAt() {
        return createdAt;
    }

    @Transactional
    public List<Comment> getComments() {
        return comments;
    }
}

Сущность комментария:

@Entity
@Table(name="comments")
public class Comment {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="id")
    private int id;

    @Column(name="comment_text")
    private String commentText;

    @Column(name="photo_id")
    private int photoId;

    @ManyToOne
    @JoinColumn(name="user_id")
    private User user;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "created_at")
    private Date createdAt;

    @Override
    public String toString() {
        return "Comment [id=" + id + ", commentText=" + commentText + ", photoId=" + photoId + ", userId=" + user.getId()
                + ", createdAt=" + createdAt + "]";
    }

    public int getId() {
        return id;
    }

    public String getCommentText() {
        return commentText;
    }

    public int getPhotoId() {
        return photoId;
    }

    public User getUser() {
        return user;
    }

    public Date getCreatedAt() {
        return createdAt;
    }
}

Стеки:

SEVERE:Servlet.service () для сервлета [dispatcher] в контексте с путем [/ test] вызвал исключение [Произошла исключительная ситуация при обработке [WEB-INF / jsp / user.jsp] в строке [34]

31: Пользователь32: Дата создания 33: 34: 35: 36: 37:

Stacktrace:] с первопричиной org.hibernate.LazyInitializationException: не удалось лениво инициализировать коллекцию ролей: com.instagramviewer.entity.User.comments, не удалось инициализировать прокси-сервер - нет сеанса в org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException (AbstractPersistentCollection.java:602) в org.hibernate.collection.internal.AbstractPersistentCollection.withTeilitarySessionIjistingSolvedrg.hibernate.collection.internal.AbstractPersistentCollection.initialize (AbstractPersistentCollection.java:581) в org.hibernate.collection.internal.AbstractPersistentCollection.read (AbstractPersistentCollection.java:148) в org.hibernate.collection.agitePagPersistentBag.java:303) в org.apache.taglibs.standard.tag.common.core.ForEachSupport.toForEachIterator (ForEachSupport.java:348) в org.apache.taglibs.standard.tag.common.core.ForEachSTpptesusuper.suForEachSupport.java:224) в org.apache.taglibs.standard.tag.common.core.ForEachSupport.prepare (ForEachSupport.java:155) в javax.servlet.jsp.jstl.core.LoopTagSupport.doTjSag256) в org.apache.jsp.WEB_002dINF.jsp.user_jsp._jspx_meth_c_005fforEach_005f0 (user_jsp.java:285) в org.apache.jsp.WEB_002dINF.jsp.user_jsp.jp..runtime.HttpJspBase.service (HttpJspBase.java:70) в javax.servlet.http.HttpServlet.service (HttpServlet.java:741) в org.apache.jasper.servlet.JspServletWrapper.service (JspServletWrapper.java:476) в org.apache.jasper.servlet.JspServlet.serviceJspFile (JspServlet.java:385) в org.aplet.jper.service (JspServlet.java:329) в javax.servlet.http.HttpServlet.service (HttpServlet.java:741) в org.apache.catalina.core.ApplicationFilterChain.internalDoFilter (ApplicationFilterChain .java:2: atcatalina.core.Java: 193) в org.apache.catalina.core.ApplicationFilterChain.doFilter (ApplicationFilterChain.java:166) в org.apache.catalina.core.ApplicationDispatcher.invoke (ApplicationDispatcher.java:712) в org.apcore.al.ApplicationDispatcher.processRequest (ApplicationDispatcher.java:459) в org.apache.catalina.core.ApplicationDispatcher.doForward (ApplicationDispatcher.java:384) в org.apache.catalina.core.ApplicationDispatcher.forward (ApplicationDispatcher.java:312) вorg.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel (InternalResourceView.java:170) в org.springframework.web.servlet.view.AbstractView.render (AbstractView.java:316) в org.springframelet.DispatcherServlet.render (DispatcherServlet.java:1370) в org.springframework.web.servlet.DispatcherServlet.processDispatchResult (DispatcherServlet.java:1116) в org.springframework.web.servlet.DispaterServServer.springframework.web.servlet.DispatcherServlet..java: 897) в javax.servlet.http.HttpServlet.service (HttpServlet.java:634) в org.springframework.web.servlet.FrameworkServlet.service (FrameworkServlet.java:882) в javax.servlet.perv.tvслужба (HttpServlet.java:741) в org.apache.catalina.core.ApplicationFilterChain.internalDoFilter (ApplicationFilterChain.java:231) по адресу org.apache.catalina.core.ApplicationFilterChain.doFilter (ApplicationFilterChain.java:166) по адресу org.apache.tomcat.websocket.server.sFilter (WFSF) (WFF)в org.apache.catalina.core.ApplicationFilterChain.internalDoFilter (ApplicationFilterChain.java:193) в org.apache.catalina.core.ApplicationFilterChain.doFilter (ApplicationFilterChain.java:166) в org.apc.(StandardWrapperValve.java:200) в org.apache.catalina.core.StandardContextValve.invoke (StandardContextValve.java:96) в org.apache.catalina.authenticator.AuthenticatorBase.invoke (AuthenticatorBase.javag4. At0).catalina.core.StandardHostValve.invoke (StandardHostValve.java:139) в org.apache.catalina.valves.ErrorReportValve.invoke (ErrorReportValve.java:92) в org.apache.catalina.valves.AbjAveLogValveal ().668) в org.apache.catalina.core.StandardEngineValve.invoke (StandardEngineValve.java:74) в org.apache.catalina.connector.CoyoteAdapter.service (CoyoteAdapter.java:343) в org.apache.coyote.http11.Http11Processor.service (Http11Processor.java:40apache.java:40apache) или org.coyote.AbstractProcessorLight.process (AbstractProcessorLight.java:66) в org.apache.coyote.AbstractProtocol $ ConnectionHandler.process (AbstractProtocol.java:834) в org.apache.tomcat.util.net.NioEndpoint $ SocketProcessor.dopointun.java: 1415) в org.apache.tomcat.util.net.SocketProcessorBase.run (SocketProcessorBase.java:49) в java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1149) в java.util.cThreadPoolExecutor $ Worker.run (ThreadPoolExecutor.java:624) в org.apache.tomcat.util.threads.TaskThread $ WrappingRunnable.run (TaskThread.java:61) в java.lang.Thread.run (Thread.java:748)

Ответы [ 4 ]

0 голосов
/ 19 февраля 2019

Я сделал две вещи неправильно:

Во-первых: я забыл включить управление транзакциями в своем весеннем классе конфигурации Java

@Configuration
@EnableTransactionManagement
public class AppConfig {

  @Bean
  public LocalContainerEntityManagerFactoryBean factoryBean() {
      LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
      factory.setPersistenceProviderClass(HibernatePersistenceProvider.class);

      return factory;
  }

  @Bean
  public PlatformTransactionManager transactionManager(){
     JpaTransactionManager transactionManager = new JpaTransactionManager();
     transactionManager.setEntityManagerFactory(factoryBean().getObject() );
     return transactionManager;
  }
}

Во-вторых: Как сказал Влад, мне нужно было инициализировать списоккомментариев как часть транзакции в моем сервисном слое

@Transactional
public User getUser(int id) {
    User user = userDAO.getUser(id);
    Hibernate.initialize(user.getComments());
    return user;
}

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

0 голосов
/ 17 февраля 2019

@ Влад уже заметил, что вы ссылаетесь на Comment вне транзакции.Поскольку вы используете jpql, вы можете охотно получить User вместе с его комментариями:

public List<User> getAllUsers()
{
    TypedQuery<User> query = entityManager.createQuery("SELECT e from User e left join fetch e.comments", User.class);
    return (List<User>) query.getResultList();
}
0 голосов
/ 18 февраля 2019

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

@Nullable
    public User getUser(int id)
    {
        TypedQuery<User> query = entityManager.createQuery("SELECT e from User e left join fetch e.comments c where e.id = :id", User.class);
        query.setParameter("id", id);
        return query.getSingleResult();
    }
0 голосов
/ 17 февраля 2019

@Transactional не поможет вам, потому что, когда вы пытаетесь загрузить комментарии (на уровне JSP), транзакция уже закрыта (она открылась при запуске метода getUser и закрылась при завершении).Возможный способ - инициализировать ваши комментарии внутри транзакции.

@Transactional
public User getUser(int id) {
    User user = userDAO.getUser(id);
    user.getComments().size(); //initializing
    return user;
}

Это самый примитивный способ, но он должен работать для вас.Если это работает, вы можете прочитать о Named Entity Graph позже.Это более продвинутый способ инициализации необходимых ленивых ассоциаций.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...