我一直在试图理解为什么会这样,但我似乎无法理解。我基本上有三个类/实体。顶级实体有一个二级实体的 OneToMany 列表,二级实体有一个三级的 OneToMany 列表。现在,当将 2 个或更多的第三级添加到第二级中的任何一个时,然后在尝试将新的第二级添加到顶层时,我在顶层和二级实体的连接表上遇到了约束冲突.我让 hibernate 显示了 sql,它似乎试图将第二级元素和两个第三级元素添加到顶级和第二级实体的连接表中两次。因此违规。但它为什么要这样做呢?我只是不明白为什么。我认为这可能与 spring 事务管理有关,因为当我在 spring 之外做同样的事情时,只是在一个主要方法中,我无法重现该行为。

顶层。

@Entity 
public class ReviewSubject { 
 
    @Id @GeneratedValue 
    private long id; 
 
 
    public long getId() { 
        return id; 
    } 
 
    public void setId(long id) { 
        this.id = id; 
    } 
 
    private String subject; 
 
    @OneToMany(fetch = FetchType.EAGER) 
    private List<Review> reviews = new ArrayList<Review>(); 
 
    public ReviewSubject(){}; 
 
    public ReviewSubject(String subject) { 
        this.subject = subject; 
    } 
 
    public String getSubject() { 
        return subject; 
    } 
 
    public void setSubject(String subject) { 
        this.subject = subject; 
    } 
 
    public List<Review> getReviews() { 
        return reviews; 
    } 
 
    public void setReviews(List<Review> reviews) { 
        this.reviews = reviews; 
    } 
 
    public void addReview(Review review) { 
        getReviews().add(review); 
    } 
 
} 

二级

@Entity 
public class Review { 
 
    @Id @GeneratedValue 
    private long id; 
 
    private String text; 
 
    @OneToMany(fetch = FetchType.EAGER) 
    private List<Comment> comments = new ArrayList<Comment>(); 
 
    public Review(){}; 
 
    Review(String text) { 
        this.text = text; 
    } 
 
    public long getId() { 
        return id; 
    } 
 
    public void setId(long id) { 
        this.id = id; 
    } 
 
    public List<Comment> getComments() { 
        return comments; 
    } 
 
    public void setComments(List<Comment> comments) { 
        this.comments = comments; 
    } 
 
    public String getText() { 
        return text; 
    } 
 
    public void setText(String text) { 
        this.text = text; 
    } 
 
} 

第三层。

@Entity 
public class Comment { 
 
    @Id @GeneratedValue 
    private long id; 
 
    private String text; 
 
    public Comment(){}; 
 
    public Comment(long reviewId, String text) { 
 
    } 
 
    public String getText() { 
        return text; 
    } 
 
    public void setText(String text) { 
        this.text = text; 
    } 
 
} 

顶级 DAO

@Repository 
public class ReviewSubjectDAOImpl implements ReviewSubjectDAO { 
 
    private SessionFactory sessionFactory; 
 
 
    @Autowired 
    public ReviewSubjectDAOImpl(SessionFactory sessionFactory) { 
        this.sessionFactory = sessionFactory; 
    } 
 
    private Session currentSession() { 
        return sessionFactory.getCurrentSession(); 
    } 
 
    public void saveReviewSubject(ReviewSubject reviewSubject) { 
        currentSession().save(reviewSubject); 
 
    } 
 
    public ReviewSubject getReviewSubject(long id) { 
        return (ReviewSubject) currentSession().get(ReviewSubject.class, id); 
    } 
 
    @SuppressWarnings({ "rawtypes", "unchecked" }) 
    public List<ReviewSubject> getReviewSubjects() { 
        return (List) currentSession().createQuery("from ReviewSubject").list(); 
    } 
 
 
 
} 

二级 DAO

@Repository 
public class ReviewDAOImpl implements ReviewDAO { 
 
    private SessionFactory sessionFactory; 
 
    @Autowired 
    public ReviewDAOImpl(SessionFactory sessionFactory) { 
        this.sessionFactory = sessionFactory; 
    } 
 
    private Session currentSession() { 
        return sessionFactory.getCurrentSession(); 
    } 
 
    public Review getReview(long id) { 
        return (Review) currentSession().get(Review.class, id); 
    } 
 
    public void saveReview(Review review) { 
        currentSession().save(review); 
    } 
 
} 

三级DAO

@Repository 
public class CommentDAOImpl implements CommentDAO{ 
 
    private SessionFactory sessionFactory; 
 
    @Autowired 
    public CommentDAOImpl(SessionFactory sessionFactory) { 
        this.sessionFactory = sessionFactory; 
    } 
 
    private Session currentSession() { 
        return sessionFactory.getCurrentSession(); 
    } 
    public void saveComment(Comment comment) { 
        currentSession().save(comment); 
    } 
} 

服务层

@Service 
@Transactional 
public class Test2ServiceImpl implements Test2Service { 
 
    private ReviewDAO reviewDAO; 
 
    private ReviewSubjectDAO reviewSubjectDAO; 
 
    private CommentDAO commentDAO; 
 
    @Autowired 
    public Test2ServiceImpl(ReviewDAO reviewDAO, ReviewSubjectDAO reviewSubjectDAO, CommentDAO commentDAO) { 
        this.reviewDAO = reviewDAO; 
        this.reviewSubjectDAO = reviewSubjectDAO; 
        this.commentDAO = commentDAO; 
    } 
 
    public void addReviewSubject(ReviewSubject reviewSubject) { 
        reviewSubjectDAO.saveReviewSubject(reviewSubject); 
 
    } 
 
    public ReviewSubject getReviewSubject(long id) { 
        return reviewSubjectDAO.getReviewSubject(id); 
    } 
 
    public void addReview(Review review, long reviewSubjectId) { 
        ReviewSubject reviewSubject = reviewSubjectDAO.getReviewSubject(reviewSubjectId); 
        reviewDAO.saveReview(review); 
        reviewSubject.getReviews().add(review); 
    } 
 
    public void addComment(Comment comment, long id) { 
        Review review = reviewDAO.getReview(id); 
        commentDAO.saveComment(comment); 
        review.getComments().add(comment); 
 
    } 
 
    public List<ReviewSubject> getReviewSubjects() { 
        return reviewSubjectDAO.getReviewSubjects(); 
    } 
 
} 

Controller

@Controller 
public class HomeController { 
 
    private Test2Service test2Service; 
 
    @Autowired 
    public HomeController(Test2Service test2Service) { 
        this.test2Service = test2Service; 
    } 
 
    @RequestMapping({"/"}) 
    public String showHomePage(Model model) { 
        model.addAttribute(test2Service.getReviewSubjects()); 
        List<ReviewSubject> rs = (List) test2Service.getReviewSubjects();    
        return "home"; 
    } 
 
    @RequestMapping({"/addReviewSubject"}) 
    public String addReviewSubject(Model model) { 
        model.addAttribute(new ReviewSubject()); 
        return "addReviewSubject"; 
    } 
 
    @RequestMapping(method=RequestMethod.POST, value="/addReviewSubject") 
    public String addReviewSubjectFromForm(ReviewSubject reviewSubject) { 
        test2Service.addReviewSubject(reviewSubject); 
        return "redirect:/"; 
    } 
 
    @RequestMapping({"/addReview/{id}"})  
    public String addReview(Model model, @PathVariable long id) { 
        model.addAttribute(id); 
        model.addAttribute(new Review()); 
        return "addReview"; 
    } 
 
    @RequestMapping(method=RequestMethod.POST, value ={"/addReview/{id}"})  
    public String addReviewFromForm(Review review, @RequestParam("id") long id) { 
        test2Service.addReview(review, id); 
        return "redirect:/"; 
    } 
 
    @RequestMapping({"/addComment/{id}"}) 
    public String addComment(Model model, @PathVariable long id) { 
        model.addAttribute(id); 
        model.addAttribute(new Comment()); 
        return "addComment"; 
    } 
 
    @RequestMapping(method=RequestMethod.POST, value={"/addComment/{id}"}) 
    public String addCommentFromForm(Comment comment, @RequestParam("id") long id) { 
        test2Service.addComment(comment, id); 
        return "redirect:/"; 
 
    } 
 
} 

View 层只是带有表单的基本 JSP。配置文件只是标准的东西,如果需要会发布。

错误。

01-May-2014 16:05:44 org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions 
WARN: SQL Error: 1062, SQLState: 23000 
01-May-2014 16:05:44 org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions 
ERROR: Duplicate entry '1' for key 'UK_rk0ljdj0mc3adaygir8wu9g9r' 
01-May-2014 16:05:44 org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl release 
INFO: HHH000010: On release of batch it still contained JDBC statements 
01-May-2014 16:05:44 org.apache.catalina.core.StandardWrapperValve invoke 
SEVERE: Servlet.service() for servlet [test2] in context with path [/test2] threw exception [Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause 
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '1' for key 'UK_rk0ljdj0mc3adaygir8wu9g9r' 
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) 
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57) 
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) 

这是插入第二个评论对象时的调试读数

976321 [http-bio-8080-exec-4] DEBUG org.springframework.web.servlet.DispatcherServlet  - DispatcherServlet with name 'test2' processing GET request for [/test2/addReview/1] 
976321 [http-bio-8080-exec-4] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping  - Looking up handler method for path /addReview/1 
976322 [http-bio-8080-exec-4] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping  - Returning handler method [public java.lang.String resources.HomeController.addReview(org.springframework.ui.Model,long)] 
976322 [http-bio-8080-exec-4] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory  - Returning cached instance of singleton bean 'homeController' 
976322 [http-bio-8080-exec-4] DEBUG org.springframework.web.servlet.DispatcherServlet  - Last-Modified value for [/test2/addReview/1] is: -1 
976323 [http-bio-8080-exec-4] DEBUG org.springframework.web.servlet.DispatcherServlet  - Rendering view [org.springframework.web.servlet.view.JstlView: name 'addReview'; URL [/WEB-INF/Views/addReview.jsp]] in DispatcherServlet with name 'test2' 
976323 [http-bio-8080-exec-4] DEBUG org.springframework.web.servlet.view.JstlView  - Added model object 'id' of type [java.lang.Long] to request in view with name 'addReview' 
976323 [http-bio-8080-exec-4] DEBUG org.springframework.web.servlet.view.JstlView  - Added model object 'long' of type [java.lang.Long] to request in view with name 'addReview' 
976324 [http-bio-8080-exec-4] DEBUG org.springframework.web.servlet.view.JstlView  - Added model object 'review' of type [resources.Review] to request in view with name 'addReview' 
976324 [http-bio-8080-exec-4] DEBUG org.springframework.web.servlet.view.JstlView  - Added model object 'org.springframework.validation.BindingResult.review' of type [org.springframework.validation.BeanPropertyBindingResult] to request in view with name 'addReview' 
976324 [http-bio-8080-exec-4] DEBUG org.springframework.web.servlet.view.JstlView  - Forwarding to resource [/WEB-INF/Views/addReview.jsp] in InternalResourceView 'addReview' 
976328 [http-bio-8080-exec-4] DEBUG org.springframework.web.servlet.DispatcherServlet  - Successfully completed request 
980068 [http-bio-8080-exec-4] DEBUG org.springframework.web.servlet.DispatcherServlet  - DispatcherServlet with name 'test2' processing POST request for [/test2/addReview/1] 
980068 [http-bio-8080-exec-4] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping  - Looking up handler method for path /addReview/1 
980070 [http-bio-8080-exec-4] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping  - Returning handler method [public java.lang.String resources.HomeController.addReviewFromForm(resources.Review,long)] 
980070 [http-bio-8080-exec-4] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory  - Returning cached instance of singleton bean 'homeController' 
980071 [http-bio-8080-exec-4] WARN  org.springframework.validation.DataBinder  - Skipping URI variable 'id' since the request contains a bind value with the same name. 
980072 [http-bio-8080-exec-4] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory  - Returning cached instance of singleton bean 'transactionManager' 
980072 [http-bio-8080-exec-4] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager  - Creating new transaction with name [resources.Test2ServiceImpl.addReview]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT 
980073 [http-bio-8080-exec-4] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager  - Opened new Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=org.hibernate.engine.spi.ExecutableList@194dec09 updates=org.hibernate.engine.spi.ExecutableList@4ac34fd9 deletions=org.hibernate.engine.spi.ExecutableList@5caf55e7 orphanRemovals=org.hibernate.engine.spi.ExecutableList@7b30e03a collectionCreations=org.hibernate.engine.spi.ExecutableList@45d13f05 collectionRemovals=org.hibernate.engine.spi.ExecutableList@2c808512 collectionUpdates=org.hibernate.engine.spi.ExecutableList@29a07791 collectionQueuedOps=org.hibernate.engine.spi.ExecutableList@6609e5f0 unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] for Hibernate transaction 
980073 [http-bio-8080-exec-4] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager  - Preparing JDBC Connection of Hibernate Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=org.hibernate.engine.spi.ExecutableList@194dec09 updates=org.hibernate.engine.spi.ExecutableList@4ac34fd9 deletions=org.hibernate.engine.spi.ExecutableList@5caf55e7 orphanRemovals=org.hibernate.engine.spi.ExecutableList@7b30e03a collectionCreations=org.hibernate.engine.spi.ExecutableList@45d13f05 collectionRemovals=org.hibernate.engine.spi.ExecutableList@2c808512 collectionUpdates=org.hibernate.engine.spi.ExecutableList@29a07791 collectionQueuedOps=org.hibernate.engine.spi.ExecutableList@6609e5f0 unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] 
980073 [http-bio-8080-exec-4] DEBUG org.hibernate.engine.jdbc.internal.LogicalConnectionImpl  - Obtaining JDBC connection 
980073 [http-bio-8080-exec-4] DEBUG org.springframework.jdbc.datasource.DriverManagerDataSource  - Creating new JDBC DriverManager Connection to [jdbc:mysql://localhost:3306/test2] 
980080 [http-bio-8080-exec-4] DEBUG org.hibernate.engine.jdbc.internal.LogicalConnectionImpl  - Obtained JDBC connection 
980080 [http-bio-8080-exec-4] DEBUG org.hibernate.engine.transaction.spi.AbstractTransactionImpl  - begin 
980080 [http-bio-8080-exec-4] DEBUG org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction  - initial autocommit status: true 
980081 [http-bio-8080-exec-4] DEBUG org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction  - disabling autocommit 
980081 [http-bio-8080-exec-4] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager  - Exposing Hibernate transaction as JDBC transaction [com.mysql.jdbc.JDBC4Connection@5db2381b] 
980082 [http-bio-8080-exec-4] DEBUG org.hibernate.SQL  -  
    select 
        reviewsubj0_.id as id1_2_0_, 
        reviewsubj0_.subject as subject2_2_0_, 
        reviews1_.ReviewSubject_id as ReviewSu1_2_1_, 
        review2_.id as reviews_2_3_1_, 
        review2_.id as id1_1_2_, 
        review2_.text as text2_1_2_, 
        comments3_.Review_id as Review_i1_1_3_, 
        comment4_.id as comments2_4_3_, 
        comment4_.id as id1_0_4_, 
        comment4_.text as text2_0_4_  
    from 
        ReviewSubject reviewsubj0_  
    left outer join 
        ReviewSubject_Review reviews1_  
            on reviewsubj0_.id=reviews1_.ReviewSubject_id  
    left outer join 
        Review review2_  
            on reviews1_.reviews_id=review2_.id  
    left outer join 
        Review_Comment comments3_  
            on review2_.id=comments3_.Review_id  
    left outer join 
        Comment comment4_  
            on comments3_.comments_id=comment4_.id  
    where 
        reviewsubj0_.id=? 
Hibernate:  
    select 
        reviewsubj0_.id as id1_2_0_, 
        reviewsubj0_.subject as subject2_2_0_, 
        reviews1_.ReviewSubject_id as ReviewSu1_2_1_, 
        review2_.id as reviews_2_3_1_, 
        review2_.id as id1_1_2_, 
        review2_.text as text2_1_2_, 
        comments3_.Review_id as Review_i1_1_3_, 
        comment4_.id as comments2_4_3_, 
        comment4_.id as id1_0_4_, 
        comment4_.text as text2_0_4_  
    from 
        ReviewSubject reviewsubj0_  
    left outer join 
        ReviewSubject_Review reviews1_  
            on reviewsubj0_.id=reviews1_.ReviewSubject_id  
    left outer join 
        Review review2_  
            on reviews1_.reviews_id=review2_.id  
    left outer join 
        Review_Comment comments3_  
            on review2_.id=comments3_.Review_id  
    left outer join 
        Comment comment4_  
            on comments3_.comments_id=comment4_.id  
    where 
        reviewsubj0_.id=? 
980084 [http-bio-8080-exec-4] DEBUG org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl  - Starting ResultSet row #0 
980085 [http-bio-8080-exec-4] DEBUG org.hibernate.loader.plan.exec.process.internal.EntityReferenceInitializerImpl  - On call to EntityIdentifierReaderImpl#resolve, EntityKey was already known; should only happen on root returns with an optional identifier specified 
980085 [http-bio-8080-exec-4] DEBUG org.hibernate.loader.plan.exec.process.internal.CollectionReferenceInitializerImpl  - Found row of collection: [resources.Review.comments#1] 
980086 [http-bio-8080-exec-4] DEBUG org.hibernate.loader.plan.exec.process.internal.CollectionReferenceInitializerImpl  - Found row of collection: [resources.ReviewSubject.reviews#1] 
980086 [http-bio-8080-exec-4] DEBUG org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl  - Starting ResultSet row #1 
980086 [http-bio-8080-exec-4] DEBUG org.hibernate.loader.plan.exec.process.internal.EntityReferenceInitializerImpl  - On call to EntityIdentifierReaderImpl#resolve, EntityKey was already known; should only happen on root returns with an optional identifier specified 
980086 [http-bio-8080-exec-4] DEBUG org.hibernate.loader.plan.exec.process.internal.CollectionReferenceInitializerImpl  - Found row of collection: [resources.Review.comments#1] 
980087 [http-bio-8080-exec-4] DEBUG org.hibernate.loader.plan.exec.process.internal.CollectionReferenceInitializerImpl  - Found row of collection: [resources.ReviewSubject.reviews#1] 
980087 [http-bio-8080-exec-4] DEBUG org.hibernate.engine.internal.TwoPhaseLoad  - Resolving associations for [resources.ReviewSubject#1] 
980087 [http-bio-8080-exec-4] DEBUG org.hibernate.engine.internal.TwoPhaseLoad  - Done materializing entity [resources.ReviewSubject#1] 
980087 [http-bio-8080-exec-4] DEBUG org.hibernate.engine.internal.TwoPhaseLoad  - Resolving associations for [resources.Review#1] 
980087 [http-bio-8080-exec-4] DEBUG org.hibernate.engine.internal.TwoPhaseLoad  - Done materializing entity [resources.Review#1] 
980088 [http-bio-8080-exec-4] DEBUG org.hibernate.engine.internal.TwoPhaseLoad  - Resolving associations for [resources.Comment#1] 
980088 [http-bio-8080-exec-4] DEBUG org.hibernate.engine.internal.TwoPhaseLoad  - Done materializing entity [resources.Comment#1] 
980088 [http-bio-8080-exec-4] DEBUG org.hibernate.engine.internal.TwoPhaseLoad  - Resolving associations for [resources.Comment#2] 
980088 [http-bio-8080-exec-4] DEBUG org.hibernate.engine.internal.TwoPhaseLoad  - Done materializing entity [resources.Comment#2] 
980088 [http-bio-8080-exec-4] DEBUG org.hibernate.engine.loading.internal.CollectionLoadContext  - 1 collections were found in result set for role: resources.Review.comments 
980088 [http-bio-8080-exec-4] DEBUG org.hibernate.engine.loading.internal.CollectionLoadContext  - Collection fully initialized: [resources.Review.comments#1] 
980091 [http-bio-8080-exec-4] DEBUG org.hibernate.engine.loading.internal.CollectionLoadContext  - 1 collections initialized for role: resources.Review.comments 
980091 [http-bio-8080-exec-4] DEBUG org.hibernate.engine.loading.internal.CollectionLoadContext  - 1 collections were found in result set for role: resources.ReviewSubject.reviews 
980091 [http-bio-8080-exec-4] DEBUG org.hibernate.engine.loading.internal.CollectionLoadContext  - Collection fully initialized: [resources.ReviewSubject.reviews#1] 
980091 [http-bio-8080-exec-4] DEBUG org.hibernate.engine.loading.internal.CollectionLoadContext  - 1 collections initialized for role: resources.ReviewSubject.reviews 
980092 [http-bio-8080-exec-4] DEBUG org.hibernate.loader.entity.plan.AbstractLoadPlanBasedEntityLoader  - Done entity load : resources.ReviewSubject#1 
980093 [http-bio-8080-exec-4] DEBUG org.hibernate.engine.spi.ActionQueue  - Executing identity-insert immediately 
980093 [http-bio-8080-exec-4] DEBUG org.hibernate.SQL  -  
    insert  
    into 
        Review 
        (text)  
    values 
        (?) 
Hibernate:  
    insert  
    into 
        Review 
        (text)  
    values 
        (?) 
980104 [http-bio-8080-exec-4] DEBUG org.hibernate.id.IdentifierGeneratorHelper  - Natively generated identity: 2 
980106 [http-bio-8080-exec-4] DEBUG resources.Test2ServiceImpl  - Review Id for review 2 = 2 
980106 [http-bio-8080-exec-4] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager  - Initiating transaction commit 
980106 [http-bio-8080-exec-4] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager  - Committing Hibernate transaction on Session [SessionImpl(PersistenceContext[entityKeys=[EntityKey[resources.Review#2], EntityKey[resources.Comment#2], EntityKey[resources.Comment#1], EntityKey[resources.Review#1], EntityKey[resources.ReviewSubject#1]],collectionKeys=[CollectionKey[resources.Review.comments#1], CollectionKey[resources.ReviewSubject.reviews#1]]];ActionQueue[insertions=org.hibernate.engine.spi.ExecutableList@194dec09 updates=org.hibernate.engine.spi.ExecutableList@4ac34fd9 deletions=org.hibernate.engine.spi.ExecutableList@5caf55e7 orphanRemovals=org.hibernate.engine.spi.ExecutableList@7b30e03a collectionCreations=org.hibernate.engine.spi.ExecutableList@45d13f05 collectionRemovals=org.hibernate.engine.spi.ExecutableList@2c808512 collectionUpdates=org.hibernate.engine.spi.ExecutableList@29a07791 collectionQueuedOps=org.hibernate.engine.spi.ExecutableList@6609e5f0 unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] 
980106 [http-bio-8080-exec-4] DEBUG org.hibernate.engine.transaction.spi.AbstractTransactionImpl  - committing 
980107 [http-bio-8080-exec-4] DEBUG org.hibernate.event.internal.AbstractFlushingEventListener  - Processing flush-time cascades 
980107 [http-bio-8080-exec-4] DEBUG org.hibernate.event.internal.AbstractFlushingEventListener  - Dirty checking collections 
980107 [http-bio-8080-exec-4] DEBUG org.hibernate.engine.spi.CollectionEntry  - Collection dirty: [resources.ReviewSubject.reviews#1] 
980108 [http-bio-8080-exec-4] DEBUG org.hibernate.engine.internal.Collections  - Collection found: [resources.ReviewSubject.reviews#1], was: [resources.ReviewSubject.reviews#1] (initialized) 
980108 [http-bio-8080-exec-4] DEBUG org.hibernate.engine.internal.Collections  - Collection found: [resources.Review.comments#1], was: [resources.Review.comments#1] (initialized) 
980108 [http-bio-8080-exec-4] DEBUG org.hibernate.engine.internal.Collections  - Collection found: [resources.Review.comments#2], was: [<unreferenced>] (initialized) 
980109 [http-bio-8080-exec-4] DEBUG org.hibernate.event.internal.AbstractFlushingEventListener  - Flushed: 0 insertions, 0 updates, 0 deletions to 5 objects 
980109 [http-bio-8080-exec-4] DEBUG org.hibernate.event.internal.AbstractFlushingEventListener  - Flushed: 1 (re)creations, 1 updates, 0 removals to 3 collections 
980109 [http-bio-8080-exec-4] DEBUG org.hibernate.internal.util.EntityPrinter  - Listing entities: 
980113 [http-bio-8080-exec-4] DEBUG org.hibernate.internal.util.EntityPrinter  - resources.Review{id=2, text=review 2, comments=[]} 
980113 [http-bio-8080-exec-4] DEBUG org.hibernate.internal.util.EntityPrinter  - resources.Comment{id=2, text=comment 2} 
980113 [http-bio-8080-exec-4] DEBUG org.hibernate.internal.util.EntityPrinter  - resources.Comment{id=1, text=comment 1} 
980113 [http-bio-8080-exec-4] DEBUG org.hibernate.internal.util.EntityPrinter  - resources.Review{id=1, text=review 1, comments=[resources.Comment#1, resources.Comment#2]} 
980113 [http-bio-8080-exec-4] DEBUG org.hibernate.internal.util.EntityPrinter  - resources.ReviewSubject{id=1, reviews=[resources.Review#1, resources.Review#1, resources.Review#2], subject=review subject 1} 
980114 [http-bio-8080-exec-4] DEBUG org.hibernate.persister.collection.AbstractCollectionPersister  - Deleting collection: [resources.ReviewSubject.reviews#1] 
980114 [http-bio-8080-exec-4] DEBUG org.hibernate.SQL  -  
    delete  
    from 
        ReviewSubject_Review  
    where 
        ReviewSubject_id=? 
Hibernate:  
    delete  
    from 
        ReviewSubject_Review  
    where 
        ReviewSubject_id=? 
980115 [http-bio-8080-exec-4] DEBUG org.hibernate.persister.collection.AbstractCollectionPersister  - Done deleting collection 
980115 [http-bio-8080-exec-4] DEBUG org.hibernate.persister.collection.AbstractCollectionPersister  - Inserting collection: [resources.ReviewSubject.reviews#1] 
980116 [http-bio-8080-exec-4] DEBUG org.hibernate.SQL  -  
    insert  
    into 
        ReviewSubject_Review 
        (ReviewSubject_id, reviews_id)  
    values 
        (?, ?) 
Hibernate:  
    insert  
    into 
        ReviewSubject_Review 
        (ReviewSubject_id, reviews_id)  
    values 
        (?, ?) 
980117 [http-bio-8080-exec-4] DEBUG org.hibernate.SQL  -  
    insert  
    into 
        ReviewSubject_Review 
        (ReviewSubject_id, reviews_id)  
    values 
        (?, ?) 
Hibernate:  
    insert  
    into 
        ReviewSubject_Review 
        (ReviewSubject_id, reviews_id)  
    values 
        (?, ?) 
980125 [http-bio-8080-exec-4] DEBUG org.hibernate.engine.jdbc.spi.SqlExceptionHelper  - could not execute statement [n/a] 
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '1' for key 'UK_rk0ljdj0mc3adaygir8wu9g9r' 

请您参考如下方法:

终于解决了。

事实证明它可以在 spring 之外重现,因此事务管理器没有问题。

如果您查看调试日志,Hibernate 正在做的是,首先,从每个实体表和连接表创建连续左外连接的笛卡尔积,如下所示

select 
        reviewsubj0_.id as id1_2_0_, 
        reviewsubj0_.subject as subject2_2_0_, 
        reviews1_.ReviewSubject_id as ReviewSu1_2_1_, 
        review2_.id as reviews_2_3_1_, 
        reviews1_.reviews_ORDER as reviews_3_1_, 
        review2_.id as id1_1_2_, 
        review2_.text as text2_1_2_, 
        comments3_.Review_id as Review_i1_1_3_, 
        comment4_.id as comments2_4_3_, 
        comments3_.comments_ORDER as comments3_3_, 
        comment4_.id as id1_0_4_, 
        comment4_.text as text2_0_4_  
    from 
        ReviewSubject reviewsubj0_  
    left outer join 
        ReviewSubject_Review reviews1_  
            on reviewsubj0_.id=reviews1_.ReviewSubject_id  
    left outer join 
        Review review2_  
            on reviews1_.reviews_id=review2_.id  
    left outer join 
        Review_Comment comments3_  
            on review2_.id=comments3_.Review_id  
    left outer join 
        Comment comment4_  
            on comments3_.comments_id=comment4_.id  
    where 
        reviewsubj0_.id=? 

变量为 1。给出这个,

+----------+------------------+----------------+----------------+--------------+----------+------------+----------------+----------------+--------------+----------+------------+ 
| id1_2_0_ | subject2_2_0_    | ReviewSu1_2_1_ | reviews_2_3_1_ | reviews_3_1_ | id1_1_2_ | text2_1_2_ | Review_i1_1_3_ | comments2_4_3_ | comments3_3_ | id1_0_4_ | text2_0_4_ | 
+----------+------------------+----------------+----------------+--------------+----------+------------+----------------+----------------+--------------+----------+------------+ 
|        1 | review subject 1 |              1 |              1 |            0 |        1 | review 1   |              1 |              1 |            0 |        1 | comment 1  | 
|        1 | review subject 1 |              1 |              1 |            0 |        1 | review 1   |              1 |              2 |            1 |        2 | comment 2  | 
|        1 | review subject 1 |              1 |              2 |            1 |        2 | review 2   |           NULL |           NULL |         NULL |     NULL | NULL       | 
+----------+------------------+----------------+----------------+--------------+----------+------------+----------------+----------------+--------------+----------+------------+ 

正如您对外部联接所期望的那样。

问题是下一个 Hibernate 删除了 ReviewSubject_Review 表中的所有行,并且似乎试图从上面的外部连接表中重新填充该表。对于评论所具有的每个评论,此表在连接表列中为每个评论实体都有一个条目。这就是导致违反约束的原因。

现在,我不知道如何,或者即使你可以,阻止 Hibernate 尝试从左外连接表中重新填充,但最终,至少在这种情况下,你不需要这样做。

这个问题实际上只是使用没有索引列的列表的良好记录效果的结果。这导致 Hibernate 将 List 视为 Bag,因此在插入之前删除连接表是预期和必要的行为。包和它们的行为在无数博客和 Hibernate 文档中有详细的记录,所以我不会在这里讨论任何内容。

解决方案只是在每个集合上添加一个 @OrderColumn 注释。有了这个索引,hibernate 不再需要像对待 Bag 一样对待 List,因此不需要在插入之前执行删除。所以不需要使用外连接的表。

确实是一个很常见的问题,有一个简单的解决方案。很惊讶没有人注意到它。


评论关闭
IT序号网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!