Search code examples
hibernatemany-to-manyentity-relationshiphibernate-mapping

Cannot save many-to-many relationship in Hibernate


I'm learning hibernate, but I can't save many-to-many relationships. I use hibernate 4.1.8 and spring 3.1.3

tables

CREATE TABLE `t_role` (
`pk_role_id` bigint(20) NOT NULL AUTO_INCREMENT,
`code` varchar(45) DEFAULT NULL,
`description` varchar(45) DEFAULT NULL,
PRIMARY KEY (`pk_role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8$$

CREATE TABLE `t_user` (
`pk_user_id` bigint(20) NOT NULL AUTO_INCREMENT,
`login_name` varchar(45) DEFAULT NULL,
`email` varchar(45) DEFAULT NULL,
`password` varchar(45) DEFAULT NULL,
`ordercode` int(11) DEFAULT NULL,
PRIMARY KEY (`pk_user_id`),
KEY `fk0983` (`pk_user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=99 DEFAULT CHARSET=utf8$$

CREATE TABLE `t_user_role` (
`pk_user_id` bigint(20) NOT NULL,
`pk_role_id` bigint(20) NOT NULL,
PRIMARY KEY (`pk_user_id`,`pk_role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8$$here

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:util="http://www.springframework.org/schema/util" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     http://www.springframework.org/schema/util
     http://www.springframework.org/schema/util/spring-util-3.0.xsd
     http://www.springframework.org/schema/tx 
     http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <property name="driverClass" value="com.mysql.jdbc.Driver" />
        <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/myssh" />
        <property name="user" value="root" />
        <property name="password" value="" />
        <property name="minPoolSize" value="5" />
        <property name="maxPoolSize" value="15" />
        <property name="initialPoolSize" value="3" />
        <property name="maxIdleTime" value="10" />
        <property name="acquireIncrement" value="3" />
        <property name="maxStatements" value="0" />
        <property name="idleConnectionTestPeriod" value="60" />
        <property name="acquireRetryAttempts" value="30" />
        <property name="breakAfterAcquireFailure" value="false" />
        <property name="testConnectionOnCheckout" value="false" />
    </bean>

    <!-- Hibernate SessionFactory -->
    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource">
            <ref local="dataSource" />
        </property>
        <property name="annotatedClasses">
            <list>
                <value>demo.myssh.model.User</value>
                <value>demo.myssh.model.Role</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.format_sql">false</prop>
                <prop key="hibernate.generate_statistics">true</prop>
                <prop key="hibernate.autoReconnect">true</prop>
                <prop key="hibernate.max_fech_depth">5</prop>
                <prop key="hibernate.jdbc.batch_size">50</prop>
                <prop key="hibernate.jdbc.fetch_size">100</prop>
                <prop key="javax.persistence.validation.mode">none</prop>
                <prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate4.SpringSessionContext
                </prop>
            </props>
        </property>
    </bean>

    <tx:annotation-driven />

    <bean id="transactionManager"
        class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

    <bean id="user" class="demo.myssh.model.User"></bean>
    <bean id="message" class="demo.myssh.model.Message"></bean>
    <bean id="role" class="demo.myssh.model.Role"></bean>
</beans>

user

package demo.myssh.model;

import java.util.HashSet;
import java.util.Set;
import javax.persistence.*;

@Entity
@Table(name = "t_user")
public class User implements java.io.Serializable {

    // Fields
    private static final long serialVersionUID = 3302763173315884925L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "pk_user_id", unique = true, nullable = true)
    private Long userID;

    @Column(name = "login_name", length = 45)
    private String loginName;

    @Column(name = "email", length = 45)
    private String email;

    @Column(name = "password", length = 45)
    private String password;

    @Column(name = "ordercode")
    private Integer orderCode;

    @ManyToMany(cascade = { CascadeType.ALL })
    @JoinTable(name = "t_user_role", 
        joinColumns = { @JoinColumn(name = "pk_user_id") }, 
        inverseJoinColumns = { @JoinColumn(name = "pk_role_id") })
    private Set<Role> roles = new HashSet<Role>();

    public User() {
    }
...set,get...

role

package demo.myssh.model;

import java.util.HashSet;
import java.util.Set;
import javax.persistence.*;

@Entity
@Table(name = "t_role")
public class Role implements java.io.Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = -7649041422061458221L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "pk_role_id", unique = true, nullable = true)
    private Long roleID;

    @Column(name = "code", length = 45)
    private String code;

    @Column(name = "description", length = 45)
    private String description;

    @ManyToMany(mappedBy = "roles")
    private Set<User> users = new HashSet<User>();

    public Role () {

    }
...set,get...

test

    package demo.myssh.dao.impl;

    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.*;
    import org.springframework.transaction.annotation.Transactional;
    import demo.myssh.model.Role;
    import demo.myssh.model.User;

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration({ "file:WebRoot/WEB-INF/applicationContext.xml" })
    public class hibernateTest {
        @Autowired
        @Qualifier("sessionFactory")
        private SessionFactory sessionFactory;

        private Session session;

        @Autowired
        @Qualifier("user")
        private User user;

        @Autowired
        @Qualifier("role")
        private Role role;

        @Test
        @Transactional
        public final void testHibernate() {
            session=sessionFactory.getCurrentSession();

            user.setEmail("aaf");
            role.setCode("ccf");
            user.getRoles().add(role);
            session.persist(user);

        }
    }

Console show

2012-12-18 22:39:05 Dialect [INFO] HHH000400: Using dialect: org.hibernate.dialect.MySQL5Dialect
2012-12-18 22:39:05 LobCreatorBuilder [INFO] HHH000424: Disabling contextual LOB creation as createClob() method threw error : java.lang.reflect.InvocationTargetException
2012-12-18 22:39:05 TransactionFactoryInitiator [INFO] HHH000399: Using default transaction strategy (direct JDBC transactions)
2012-12-18 22:39:05 ASTQueryTranslatorFactory [INFO] HHH000397: Using ASTQueryTranslatorFactory
2012-12-18 22:39:06 HibernateTransactionManager [INFO] Using DataSource [com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 1bqou1j8r1cekgod19q9xm8|26d58939, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 1bqou1j8r1cekgod19q9xm8|26d58939, idleConnectionTestPeriod -> 60, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://127.0.0.1:3306/myssh, lastAcquisitionFailureDefaultUser -> null, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 10, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 15, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 5, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> null, properties -> {user=******, password=******}, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]] of Hibernate SessionFactory for HibernateTransactionManager
2012-12-18 22:39:06 Cglib2AopProxy [WARN] Unable to proxy method [public final void demo.myssh.dao.impl.hibernateTest.testHibernate()] because it is final: All calls to this method via a proxy will be routed directly to the proxy.
2012-12-18 22:39:06 TransactionalTestExecutionListener [INFO] Began transaction (1): transaction manager [org.springframework.orm.hibernate4.HibernateTransactionManager@27af8502]; rollback [true]
Hibernate: insert into t_user (email, login_name, ordercode, password) values (?, ?, ?, ?)
Hibernate: insert into t_role (code, description) values (?, ?)
2012-12-18 22:39:06 TransactionalTestExecutionListener [INFO] Rolled back transaction after test execution for test context [[TestContext@4d0ac2a3 testClass = hibernateTest, testInstance = demo.myssh.dao.impl.hibernateTest@7d7082d8, testMethod = testHibernate@hibernateTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@659e733e testClass = hibernateTest, locations = '{file:WebRoot/WEB-INF/applicationContext.xml}', classes = '{}', activeProfiles = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader']]]
2012-12-18 22:39:06 GenericApplicationContext [INFO] Closing org.springframework.context.support.GenericApplicationContext@2acdb06e: startup date [Tue Dec 18 22:39:03 CST 2012]; root of context hierarchy
2012-12-18 22:39:06 DefaultListableBeanFactory [INFO] Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@132789d2: defining beans  [dataSource,sessionFactory,org.springframework.aop.config.internalAutoProxyCreator,org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#0,org.springframework.transaction.interceptor.TransactionInterceptor#0,org.springframework.transaction.config.internalTransactionAdvisor,transactionManager,user,message,role,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.internalPersistenceAnnotationProcessor,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root  of factory hierarchy

It only saved entity,no saved relationship. The reason looks like

Cglib2AopProxy [WARN] Unable to proxy method [public final void demo.myssh.dao.impl.hibernateTest.testHibernate()] because it is final: All calls to this method via a proxy will be routed directly to the proxy.

but I'm not very clear,s o how should I do, please.


Solution

  • There are a couple of things that I see. First in you applicationContext.xml remove the beans User and Role shown below. Since you will be persisting these bean you just need to create a new pojo.

    <bean id="user" class="demo.myssh.model.User"></bean>
    <bean id="role" class="demo.myssh.model.Role"></bean>
    

    Next in your test change the test to create a new pojo and then persist and remove the autowire for the User and Role on the test.

    @Test
    @Transactional
    public final void testHibernate() {
        session=sessionFactory.getCurrentSession();
        User user = new User();
        Role role = new Role();
        user.setEmail("aaf");
        role.setCode("ccf");
        user.getRoles().add(role);
        session.persist(user);
    }