0

在我的 TagController 中调用 purchaseService.updatePurchase(purchase) 时出现以下错误:

SEVERE: Servlet.service() for servlet [PurchaseAPIServer] in context with path [/PurchaseAPIServer] threw exception [Request processing failed; nested exception is org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.app.model.Purchase.tags, no session or session was closed] with root cause
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.app.model.Purchase.tags, no session or session was closed
    at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:383)
    at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:375)
    at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:368)
    at org.hibernate.collection.PersistentSet.add(PersistentSet.java:212)
    at com.app.model.Purchase.addTags(Purchase.java:207)
    at com.app.controller.TagController.createAll(TagController.java:79)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:212)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:126)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:96)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:617)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:900)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:827)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:789)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:641)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:225)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:169)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:927)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:999)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:565)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:309)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:662)

标签控制器:

@RequestMapping(value = "purchases/{purchaseId}/tags", method = RequestMethod.POST, params = "manyTags")
@ResponseStatus(HttpStatus.CREATED)
public void createAll(@PathVariable("purchaseId") final Long purchaseId, @RequestBody final Tag[] entities)
{
    Purchase purchase = purchaseService.getById(purchaseId);

    Set<Tag> tags = new HashSet<Tag>(Arrays.asList(entities));

    purchase.addTags(tags);
    purchaseService.updatePurchase(purchase);
}


购买:

@Entity
@XmlRootElement
public class Purchase implements Serializable
{

    /**
     * 
     */
    private static final long serialVersionUID = 6603477834338392140L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @OneToMany(mappedBy = "purchase", fetch = FetchType.LAZY, cascade={CascadeType.ALL})
    private Set<Tag> tags;

    @JsonIgnore
    public Set<Tag> getTags()
    {
        if (tags == null)
        {
            tags = new LinkedHashSet<Tag>();
        }
        return tags;
    }

    public void setTags(Set<Tag> tags)
    {
        this.tags = tags;
    }

    public void addTag(Tag tag)
    {
        tag.setPurchase(this);
        this.tags.add(tag);
    }

    public void addTags(Set<Tag> tags)
    {
        Iterator<Tag> it = tags.iterator();

        while (it.hasNext())
        {
            Tag tag = it.next();
            tag.setPurchase(this);
            this.tags.add(tag);
        }
    }

    ...
}


标签:

@Entity
@XmlRootElement
public class Tag implements Serializable
{
    /**
     * 
     */
    private static final long serialVersionUID = 5165922776051697002L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumns({@JoinColumn(name = "PURCHASEID", referencedColumnName = "ID")})
    private Purchase purchase;

    @JsonIgnore
    public Purchase getPurchase()
    {
        return purchase;
    }

    public void setPurchase(Purchase purchase)
    {
        this.purchase = purchase;
    }
}


购买服务:

@Service
public class PurchaseService implements IPurchaseService
{
    @Autowired
    private IPurchaseDAO purchaseDAO;

    public PurchaseService()
    {

    }

    @Transactional
    public List<Purchase> getAll()
    {
        return purchaseDAO.findAll();
    }

    @Transactional
    public Purchase getById(Long id)
    {
        return purchaseDAO.findOne(id);
    }

    @Transactional
    public void addPurchase(Purchase purchase)
    {
        purchaseDAO.save(purchase);
    }

    @Transactional
    public void updatePurchase(Purchase purchase)
    {
        purchaseDAO.update(purchase);
    }
}


标签服务:

@Service
public class TagService implements ITagService
{
    @Autowired
    private ITagDAO tagDAO;

    public TagService()
    {

    }

    @Transactional
    public List<Tag> getAll()
    {
        return tagDAO.findAll();
    }

    @Transactional
    public Tag getById(Long id)
    {
        return tagDAO.findOne(id);
    }

    @Transactional
    public void addTag(Tag tag)
    {
        tagDAO.save(tag);
    }

    @Transactional
    public void updateTag(Tag tag)
    {
        tagDAO.update(tag);
    }
}


关于如何解决这个问题的任何想法?(我想避免使用 EAGER 加载)。

我是否需要为交易设置某种形式的会话管理?

谢谢

基于 JB 建议的更新

标签控制器

@RequestMapping(value = "purchases/{purchaseId}/tags", method = RequestMethod.POST, params = "manyTags")
@ResponseStatus(HttpStatus.CREATED)
public void createAll(@PathVariable("purchaseId") final Long purchaseId, @RequestBody final Tag[] entities)
{
    Purchase purchase = purchaseService.getById(purchaseId);

    // Validation
    RestPreconditions.checkRequestElementNotNull(purchase);
    RestPreconditions.checkRequestElementIsNumeric(purchaseId);

    Set<Tag> tags = new HashSet<Tag>(Arrays.asList(entities));

    purchaseService.addTagsToPurchase(purchaseId, tags);
}

采购服务

@Transactional
public void addTagsToPurchase(Long purchaseId, Set<Tag> tags)
{
    Purchase p = purchaseDAO.findOne(purchaseId);

    p.addTags(tags);
}
4

1 回答 1

0

您在不初始化其标签集合的情况下加载购买,然后在您的控制器中,一旦提交事务并且会话关闭,您将向这个未初始化的集合添加标签。所以很明显,这个异常被抛出了。

你有两种可能:

  1. 使用 Purchase 加载标签,方法是调用,或执行一个 JPQL 查询,在单个查询 ( )Hibernate.initialize(purchase.getTags())中加载标签及其标签。select distinct p from Purchase p left join fetch p.tags
  2. addTagsToPurchase(Long purchaseId, Set<Tag> tags)添加一个真正的服务方法来重新加载购买并将标签添加到该购买中,而不是提供比 DAO 更多的服务类。这实际上是您的控制器所需的服务方法。

到目前为止,我当然更喜欢第二种解决方案:

  • 它显示了为什么服务层很有用,
  • 它从表示层中删除业务代码以将其放入服务层,
  • 它更有效
  • 它更安全,因为它使用附加实体,从而避免了您遇到的那种异常。

一般来说,如果你的控制器为了执行一个原子动作,需要多次调用服务层,这意味着服务层没有提供所需的功能,而服务是在表示层中实现的。

于 2012-06-03T16:49:55.657 回答