Hibernate在SpringMVC中的Join Table上重复输入

时间:2014-05-01 13:49:00

标签: java mysql hibernate spring-mvc

我一直在努力去理解为什么会这样,但我似乎无法得到它。我基本上有三个类/实体。顶级实体具有第二级实体的OneToMany列表,第二级实体具有第三级的OneToMany列表。现在,当第二级中的第二级中的两个被添加到第二级中的任何一个时,然后在尝试将新的第二级添加到顶级时,我在顶级和第二级实体的连接表上获得约束违规。我已经让hibernate显示了sql,似乎它试图将第二级元素和两个第三级元素添加到顶级和第二级实体的连接表中两次。因此违规。但为什么要这样做呢?我似乎无法理解为什么。我认为它可能与spring事务管理有关,因为当我在spring之外做同样的事情时,只是在main方法中,我无法重现这种行为。

顶级。

@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
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:/";

    }

}

视图层只是带表单的基本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'

2 个答案:

答案 0 :(得分:3)

终于解决了这个问题。

事实证明,它在春天之外是可重现的,因此对交易经理来说不是问题。

如果查看调试日志,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表中的所有行,并且似乎试图从上面的外连接表中重新填充表。对于Review的每个Comment,此表都有一个条目,用于连接表列中的每个Review实体。这就是导致约束违规的原因。

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

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

解决方案只是在每个集合上打一个@OrderColumn注释。使用此索引,hibernate不再需要将List视为Bag,因此在插入之前无需执行删除操作。所以不需要使用外连接表。

这是一个非常常见的问题,只需一个简单的解决方案。惊讶没有人接受它。

答案 1 :(得分:2)

对您的馆藏使用java.util.Set代替java.util.List,并确保为每个实体bean正确实施了equalshashCode