mardi 23 juin 2015

Hibernate Spring bi directional many to many removal not working

I am having an issue while trying to conduct a deletion in a many-to-many relationship. First of all I'd like to mention the story. I have two entity classes called Post and Category, Here Posts are associated with Categories, and it is also true other way around. I can create categories and post, and when creating or editing a post I can associate categories no problem. The issue arises when I remove a category from a post. There is no exception or error thrown, but it does not remove it.

Normally as you will see below the code, in a post set, when I remove a category object and save the object, hibernate will take care of removing the regarding relation in the shared table, but it does not just happen!

Other than that, I've tried multiple solutions, but they did not quite work out either.

  1. http://ift.tt/1RfYeT3
  2. Hibernate Bidirectional ManyToMany delete issue 3.http://ift.tt/1RfYeT7 4.Hibernate Bi-Directional Many to Many association creates duplicates 5.http://ift.tt/1RfYeTb

Post Entity

@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;
    }
}

Category Entity

@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

@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());
    }

Edit Post Edit DAO Method

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();
        }
    }

UPDATE

For a couple of days, I've been googling and checking out different sources and finally I've decided to test entire structure and the entity classes in Non J2EE environments using Hibernate and EclipseLink JPA without Spring. I've come to realize that while using the application in the Spring container, the issue still continues, but without Spring environment, surprisingly there is no such issue at all. All tests work fine, so does bi directional many to many annotation I've been dwelling on getting it work.

I'd like to hereby share the spring configuration and maven pom configuration for your observation and suggestions what to do. Briefly I manage the transactions on hibernate's side. If you see any gap or wrong I'll really appreciate your help!

Thanks again

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://ift.tt/GArMu6"
       xmlns:xsi="http://ift.tt/ra1lAU" xmlns:p="http://ift.tt/1jdM0fE"
       xmlns:tx="http://ift.tt/OGfeU2" xmlns:context="http://ift.tt/GArMu7"
       xsi:schemaLocation="http://ift.tt/GArMu6
  http://ift.tt/1jdM0fG
  http://ift.tt/GArMu7
  http://ift.tt/1jdLYo7
  http://ift.tt/OGfeU2
  http://ift.tt/18tm2Tg">

    <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://ift.tt/HBk9RF"> 4.0.0 com.tugrulaslan WebApp war 1.0-SNAPSHOT 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>

Aucun commentaire:

Enregistrer un commentaire