【问题标题】:Hibernate Spring bi directional many to many removal not workingHibernate Spring双向多对多删除不起作用
【发布时间】:2015-09-05 12:48:26
【问题描述】:

我在尝试以多对多关系执行删除时遇到问题。首先我想提一下这个故事。我有两个实体类,分别称为 Post 和 Category,这里 Posts 与 Categories 相关联,反之亦然。我可以创建类别和帖子,并且在创建或编辑帖子时我可以关联类别没有问题。当我从帖子中删除类别时会出现问题。没有抛出异常或错误,但不会将其删除。

通常,正如您将在代码下方看到的那样,在一个帖子集中,当我删除一个类别对象并保存该对象时,hibernate 会负责删除共享表中的相关关系,但这不会发生!

除此之外,我尝试了多种解决方案,但它们也没有完全奏效。

  1. https://forum.hibernate.org/viewtopic.php?f=1&t=17
  2. Hibernate Bidirectional ManyToMany delete issue 3.http://www.codereye.com/2009/06/hibernate-bi-directional-many-to-many.html 4.Hibernate Bi-Directional Many to Many association creates duplicates 5.https://howtoprogramwithjava.com/hibernate-manytomany-unidirectional-bidirectional/

发布实体

@Entity
@Table(name = "posts")
public class Post {

    @Id
    @Column(name = "postid")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long postId;

    @Column(name = "postTitle")
    private String postTitle;

    @Column(name = "postContext")
    @Type(type = "text")
    private String postContext;

    @Column(name = "postedDate")
    private Date postedDate;

    @Column(name = "visitCount")
    private long visitCount;

    @Column(name = "publishable")
    private boolean publishable;

    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, targetEntity = Category.class, fetch = FetchType.EAGER)
    //@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.EAGER,mappedBy = "posts")
    @JoinTable(name = "post_category", joinColumns = {@JoinColumn(name = "postid")}, inverseJoinColumns = {@JoinColumn(name = "categoryid")})
    private Set<Category> categories = new HashSet<>();

    @OneToOne(cascade = CascadeType.ALL)
    private User user;

    public Post() {
    }

    public Post(String postTitle, String postContext, Date postedDate, boolean publishable, Set<Category> categories, User user) {
        this.postTitle = postTitle;
        this.postContext = postContext;
        this.postedDate = postedDate;
        this.publishable = publishable;
        this.categories = categories;
        this.user = user;
    }

    public long getPostId() {
        return postId;
    }

    public void setPostId(long postId) {
        this.postId = postId;
    }

    public String getPostTitle() {
        return postTitle;
    }

    public void setPostTitle(String postTitle) {
        this.postTitle = postTitle;
    }

    public String getPostContext() {
        return postContext;
    }

    public void setPostContext(String postContext) {
        this.postContext = postContext;
    }

    public Date getPostedDate() {
        return postedDate;
    }

    public long getVisitCount() {
        return visitCount;
    }

    public void setVisitCount(long visitCount) {
        this.visitCount = visitCount;
    }

    public void setPostedDate(Date postedDate) {
        this.postedDate = postedDate;
    }

    public boolean isPublishable() {
        return publishable;
    }

    public void setPublishable(boolean publishable) {
        this.publishable = publishable;
    }

    public Set<Category> getCategories() {
        return categories;
    }

    public void setCategories(Set<Category> categories) {
        this.categories = categories;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}

类别实体

@Entity
@Table(name = "categories")
public class Category {

    @Id
    @Column(name = "categoryid")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long categoryId;

    @Column(name = "categoryName")
    private String categoryName;

    @Column(name = "createdDate")
    private Date createdDate;

    @ManyToOne
    @JoinColumn(name = "parent_category_id")
    private Category parentCategory;

    @OneToMany(mappedBy = "parentCategory", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    public Set<Category> subCategories = new HashSet<>();

   // @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.EAGER, targetEntity = Post.class)
   // @JoinTable(name = "post_category", joinColumns = {@JoinColumn(name = "categoryid")}, inverseJoinColumns = {@JoinColumn(name = "postid")})
   @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.EAGER,mappedBy = "categories")
    private Set<Post> posts = new HashSet<>();

    public Category() {
    }

    public Category(String categoryName) {
        this.categoryName = categoryName;
    }

    public Category(String categoryName, Date createdDate, Category parentCategory) {
        this.categoryName = categoryName;
        this.createdDate = createdDate;
        this.parentCategory = parentCategory;
        this.subCategories = new HashSet<>();
        if (parentCategory != null) {
            parentCategory.subCategories.add(this);
        }
    }

    public Set<Post> getPosts() {
        return posts;
    }

    public void setPosts(Set<Post> posts) {
        this.posts = posts;
    }

    public long getCategoryId() {
        return categoryId;
    }

    public void setCategoryId(long categoryId) {
        this.categoryId = categoryId;
    }

    public String getCategoryName() {
        return categoryName;
    }

    public void setCategoryName(String categoryName) {
        this.categoryName = categoryName;
    }

    public Date getCreatedDate() {
        return createdDate;
    }

    public void setCreatedDate(Date createdDate) {
        this.createdDate = createdDate;
    }

    public Category getParentCategory() {
        return parentCategory;
    }

    public void setParentCategory(Category parentCategory) {
        this.parentCategory = parentCategory;
    }

    public Set<Category> getSubCategories() {
        return subCategories;
    }

    public void setSubCategories(Set<Category> subCategories) {
        this.subCategories = subCategories;
    }
}

测试

@Test
    public void stage09_EditCreatedPostTest() {
        //Fetch the post
        Post post = appService.getPostByName(postName);

        //Fetch the child category;
        Category category = appService.getCategoryByName(childCatName);

        //Remove the child category
        post.getCategories().remove(category);

        //Selected child category is not null
        assertNotNull(category);

        appService.editPost(post);

        //Fetch the post again
        Post post2 = appService.getPostByName(postName);

        //Check the post2 object it is not null
        assertNotNull(post2);

        //Check category size
        assertEquals(1, post2.getCategories().size());
    }

编辑 后编辑 DAO 方法

public void editPost(Post post) {
        Session session = sessionFactory.openSession();
        Transaction transaction = session.beginTransaction();
        try {
            session.update(post);
            transaction.commit();
        } catch (HibernateException e) {
            System.err.println(e);
            transaction.rollback();
        } finally {
            session.close();
        }
    }

更新

几天来,我一直在谷歌搜索和检查不同的来源,最后我决定在非 J2EE 环境中使用 Hibernate 和没有 Spring 的 EclipseLink JPA 测试整个结构和实体类。我开始意识到,在 Spring 容器中使用应用程序时,问题仍然存在,但没有 Spring 环境,令人惊讶的是根本没有这样的问题。所有测试都可以正常工作,我一直在努力让它工作的双向多对多注释也是如此。

特此分享一下spring配置和maven pom配置,供大家观察和建议。简而言之,我在休眠方面管理事务。如果您发现任何差距或错误,我将非常感谢您的帮助!

再次感谢

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/context
  http://www.springframework.org/schema/context/spring-context.xsd
  http://www.springframework.org/schema/tx
  http://www.springframework.org/schema/tx/spring-tx.xsd">

    <context:property-placeholder location="classpath:hibernate.properties"/>

    <!-- Hibernate connection configuration -->
    <bean id="dataSource"
          class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${orm.connection.driver_class}"/>
        <property name="url" value="${orm.connection.url}"/>
        <property name="username" value="${orm.connection.username}"/>
        <property name="password" value="${orm.connection.password}"/>
    </bean>

    <!-- Hibernate configuration settings -->
    <bean id="sessionFactory"
          class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="packagesToScan" value="com.tugrulaslan.entity"/>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">${orm.dialect}</prop>
                <prop key="hibernate.show_sql">${orm.show_sql}</prop>
                <prop key="hibernate.hbm2ddl.auto">${orm.hbm2ddl.auto}</prop>
              <!-- --> <prop key="current_session_context_class">thread</prop>
            </props>
        </property>
    </bean>

    <!-- Hibernate Session Factory creation
    <bean id="transactionManager"
          class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>

    <tx:annotation-driven transaction-manager="transactionManager"/> -->

    <context:component-scan base-package="com.tugrulaslan"/>
</beans>

pom.xml http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 com.tugrulaslan 网络应用 战争 1.0-快照 BlogWebApp Maven Webapp http://maven.apache.org

<properties>
    <project-java.version>1.7</project-java.version>
    <maven-compiler-plugin.version>3.1</maven-compiler-plugin.version>
    <junit.version>4.11</junit.version>
    <mysql-connector.version>5.1.34</mysql-connector.version>
    <hibernate.version>4.3.6.Final</hibernate.version>
    <javax-persistance-api.version>1.0.2</javax-persistance-api.version>
    <spring.version>4.0.6.RELEASE</spring.version>
</properties>

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>${junit.version}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>${mysql-connector.version}</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>${hibernate.version}</version>
    </dependency>
    <dependency>
        <groupId>javax.persistence</groupId>
        <artifactId>persistence-api</artifactId>
        <version>${javax-persistance-api.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <!--
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>${spring.version}</version>
    </dependency>-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>${spring.version}</version>
    </dependency>
</dependencies>
<build>
    <finalName>BlogWebApp</finalName>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>${maven-compiler-plugin.version}</version>
            <configuration>
                <source>${project-java.version}</source>
                <target>${project-java.version}</target>
            </configuration>
        </plugin>
    </plugins>
</build>

【问题讨论】:

  • 您遇到了什么错误?您是否尝试过 cascade = CascadeType.ALL 的 ManyToMany 关系?
  • JUnit 抛出错误,因为不满足给定的情况。在我在上面编辑并检查帖子的类别大小后,它应该是 1,但它仍然是 2。我尝试了很多解决方案和组合,这些解决方案和组合在列表方法上方被注释掉

标签: java mysql spring hibernate jakarta-ee


【解决方案1】:

经过长时间的研究和测试,我在调试应用程序时发现了问题所在。当我调试移除部分时,我突然注意到 Set 集合中的大小和元素与移除操作之前相同。无法确定目标集合中对象的相等性,因此无法进行删除操作。

在我承认这一点之后,我立即重写了对象的 equals 方法,并包含了唯一不可变和不可更改的字段,在我的情况下似乎只是类别 ID,其他字段是立即可互换的.因此,您将在下面找到完整的 Category 实体,其中包含由 IntelliJ IDE 生成的重写对象 equals 方法。

除此之外,我已经针对两种情况进行了案例,以了解我碰巧在 Category 实体中定义的上述帖子集合中定义的级联选项是否至关重要,提到“cascade = {CascadeType.PERSIST, CascadeType.MERGE}”。所以最终我开始意识到在多对多关系中定义的相应级联的缺失或存在没有区别。它的存在并没有改变在其他不同测试用例中所经历的任何事情。

我希望我的问题和解决方案能给阴影区域带来一些启发,并在您遇到类似问题时为您提供帮助。

@Entity
@Table(name = "categories")
public class Category {

    @Id
    @Column(name = "categoryid")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long categoryId;

    @Column(name = "categoryName")
    private String categoryName;

    @Column(name = "createdDate")
    private Date createdDate;

    @ManyToOne
    @JoinColumn(name = "parent_category_id")
    private Category parentCategory;

    @OneToMany(mappedBy = "parentCategory", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    private Set<Category> subCategories = new HashSet<>();

    @ManyToMany(fetch = FetchType.EAGER, mappedBy = "categories")
    private Set<Post> posts = new HashSet<>();

    public Category() {
    }

    public Category(String categoryName) {
        this.categoryName = categoryName;
    }

    public Category(String categoryName, Date createdDate, Category parentCategory, Set<Category> subCategories, Set<Post> posts) {
        this.categoryName = categoryName;
        this.createdDate = createdDate;
        this.parentCategory = parentCategory;
        this.subCategories = new HashSet<>();
        if (parentCategory != null) {
            parentCategory.subCategories.add(this);
        }
        this.subCategories = subCategories;
        this.posts = posts;
    }

    public Set<Post> getPosts() {
        return posts;
    }

    public void setPosts(Set<Post> posts) {
        this.posts = posts;
    }

    public long getCategoryId() {
        return categoryId;
    }

    public void setCategoryId(long categoryId) {
        this.categoryId = categoryId;
    }

    public String getCategoryName() {
        return categoryName;
    }

    public void setCategoryName(String categoryName) {
        this.categoryName = categoryName;
    }

    public Date getCreatedDate() {
        return createdDate;
    }

    public void setCreatedDate(Date createdDate) {
        this.createdDate = createdDate;
    }

    public Category getParentCategory() {
        return parentCategory;
    }

    public void setParentCategory(Category parentCategory) {
        this.parentCategory = parentCategory;
    }

    public Set<Category> getSubCategories() {
        return subCategories;
    }

    public void setSubCategories(Set<Category> subCategories) {
        this.subCategories = subCategories;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Category category = (Category) o;

        return categoryId == category.categoryId;

    }

    @Override
    public int hashCode() {
        return (int) (categoryId ^ (categoryId >>> 32));
    }

【讨论】:

    【解决方案2】:

    在您的服务中检查您是否在删除后提交,如下所示

    session.getTransaction().commit(); 
    

    【讨论】:

    • 我已将我的方法包含在您的观察中
    猜你喜欢
    • 2019-07-29
    • 1970-01-01
    • 1970-01-01
    • 2012-12-17
    • 1970-01-01
    • 2020-04-12
    • 1970-01-01
    • 2017-02-24
    • 2011-05-20
    相关资源
    最近更新 更多