hibernate关联映射之一对多

看了一对一的实现之后,我们来看一下hibernate中一对多的实现,实际上还是不难的,只是有些概念第一次用时比较难理解。

废话不多说,直接上代码:

先看一下实体类:

public class Address implements Serializable{

	private static final long serialVersionUID = 1L;

	private int id;
	private String address;
	private String zipcode;
	private String tel;
	private String type;
	private TUser user;
       //省略Get/Set方法
}
public class TUser implements Serializable{

	private static final long serialVersionUID = 1L;

	private int id;
	private int age;
	private String name;
	private Set<Address> addresses = new HashSet<Address>();
        //省略Get/Set方法
}

  看完实体类,我们再来看一下映射文件:

<hibernate-mapping package="org.hibernate.tutorial.domain6">
	<class name="Address" table="t_address" dynamic-insert="false" dynamic-update="false">
		<id name="id" column="id" type="java.lang.Integer">
			<generator class="native" />
		</id>
		<property name="address" column="address" type="java.lang.String" />
		<property name="zipcode" column="zipcode" type="java.lang.String" />
		<property name="tel" column="tel" type="java.lang.String"/>
		<property name="type" column="type" type="java.lang.String" />
		
		<many-to-one name="user" class="TUser"
			column="user_id" not-null="true"></many-to-one>

	</class>
</hibernate-mapping>

  再看另外一个TUser的映射文件,这个才是重要的,我们接下来要讲的重点,要仔细看:

<hibernate-mapping package="org.hibernate.tutorial.domain6">
	<class name="TUser" table="t_user" dynamic-insert="true" dynamic-update="true">
		<id name="id" column="id">
			<generator class="native" />
		</id>
		<property name="name" type="java.lang.String" column="name"/>
		<property name="age" type="java.lang.Integer" column="age"/>
		<set name="addresses" cascade="all" table="t_address" inverse="true">
			<key column="user_id" />
			<one-to-many class="Address"/>
		</set>
	</class>
</hibernate-mapping>

  注意看,我们这里用了一个inverse="true",这个是什么意思呢?很多人在这里会有疑问。

我们先把它删了,修改后如下:

<hibernate-mapping package="org.hibernate.tutorial.domain6">
	<class name="TUser" table="t_user" dynamic-insert="true" dynamic-update="true">
		<id name="id" column="id">
			<generator class="native" />
		</id>
		<property name="name" type="java.lang.String" column="name"/>
		<property name="age" type="java.lang.Integer" column="age"/>
		<set name="addresses" cascade="all" table="t_address">
			<key column="user_id" />
			<one-to-many class="Address"/>
		</set>
	</class>
</hibernate-mapping>

我们用一个测试类来测试一下插入:

public static void main(String[] args) {

		Configuration cfg = new Configuration().configure();
		SessionFactory sessionFactory = cfg.buildSessionFactory();
		Session session = sessionFactory.openSession();
		
		session.beginTransaction();
		
		TUser user = new TUser();
		
		Address address = new Address();
		address.setAddress("Test1");
		address.setTel("123123");
		address.setType("14423213");
		address.setZipcode("4444");
		
		address.setUser(user);
		user.getAddresses().add(address);
		
		session.save(user);
		
		session.getTransaction().commit();
		session.close();
		
	}

  这里我们进行插入的时候是没问题的,我们把address.setUser(user)去掉后看一下错误:

org.hibernate.PropertyValueException: not-null property references a null or transient value: org.hibernate.tutorial.domain6.Address.user

  这是指我们向非空的字段插入了一个空的值,这是因为我们在Address里面限定了user必须非空,而我们这里没有进行set入,就会出现这样的错误。

而我们说的inverse="true"的情况就是避免了单向关联时的这个错误,单向关联时首先会进行一条insert操作:

我们直接看一下hibernate打印出的语句:

Hibernate: insert into t_user (age) values (?)
Hibernate: insert into t_address (address, zipcode, tel, type, user_id) values (?, ?, ?, ?, ?)
Hibernate: update t_address set user_id=? where id=?

  它首先会插入两条语句,然后再根据第一条语句的ID去更新第二条语句的user_id,我们这里进行了setUser,所以没问题,如果把那句删了就出问题了,hibernate尝试把null插入赋给user_id,这是肯定会出错的。

这是我们没有用inverse="true"的情况,需要由User来维护需要插入给Address的user_id,所以它需要先插入一条user确定user的id,然后再插入一条address,再根据user的id来更新address的user_id。逻辑很正常。

但当我们加上inverse="true"之后,我们再重新运行测试类,可以看到打印的语句为:

Hibernate: insert into t_user (age) values (?)
Hibernate: insert into t_address (address, zipcode, tel, type, user_id) values (?, ?, ?, ?, ?)

  我们看到三条语句变成了两条。hibernate在第二条语句中直接把user_id插入t_user表,即User的关联表。很容易理解吧,inverse="true"就是让对方来管理跟自己关联的属性,这里表明Address管理user属性,它在t_user插入后然后把id取得,并作为user_id插入到t_address表中。

如果你的项目出现上面的那个异常,首先检查一下是否用了inverse="true",默认值是inverse="false"。

猜你喜欢

转载自cxshun.iteye.com/blog/1058668