10、Hibernate的对象检索策略

- 立即检索策略
- 延迟检索策略
- 左外连接检索策略
- 每种检索策略的适用范围
- 在程序中显式指定左外连接检索策略

表字段之间的对应关系

1、运行Session的方法

List customerLists = session.createQuery("from Customer as c").list();

运行以上方法时,Hibernate将先查询CUSTOMERS表中所有的记录,然后根据每条记录的ID,到ORDERS表中查询有参照关系的记录,Hibernate将依次执行以下select语句:
select * from CUSTOMERS;
select * from ORDERS where CUSTOMER_ID=1;
select * from ORDERS where CUSTOMER_ID=2;
select * from ORDERS where CUSTOMER_ID=3;
select * from ORDERS where CUSTOMER_ID=4;

内存中的关系图:

以上就是立即加载策略,默认的立即检索策略的缺点

1)select语句的数目太多,需要频繁的访问数据库,会影响检索的性能。如果需要查询n个Customer对象,那么必须执行n+1次select查询语句。这种检索策略没有利用SQL的连接查询功能,例如以上5条select语句完全可以通过1条select语句来完成:
select * from CUSTOMERS left outer join ORDERS on CUSTOMERS.ID = ORDERS.CUSTOMER_ID

2)以上select语句使用了SQL的左外连接查询功能,能够在一条selelct语句中查询出CUSTOMERS表的所有记录,以及匹配的ORDERS表的记录。
3)在应用逻辑只需要访问Customer对象,而不需要访问Order对象的场合,加载Order对象完全是多余的操作,这些多余的Order对象白白浪费了许多内存空间。

2、针对以上缺点,对于<set>元素,应该优先考虑使用延迟检索策略:
<set name="orders" inverse="true" lazy="true">
此时运行:
Customer customer = (Customer)session.get(Customer.class,new Long(1));
仅立即检索Customer对象,执行以下select语句:select * from CUSTOMERS where ID=1;

在一对多关联级别使用延迟检索策略

Customer对象的orders变量引用集合代理类实例,当应用程序第一次访问它,例如调用customer.getOrders().iterator()方法时,Hibernate会初始化这个集合代理类实例,在初始化过程中到数据库中检索所有与Customer关联的Order对象,执行以下select语句:
select * from ORDERS where CUSTOMER_ID=1;

3、访问没有被初始化的游离状态的集合代理类实例
Session session = sessionFactory.openSession();
tx = session.beginTransaction();
Customer customer = (Customer)session.get(Customer.class,new Long(1));
tx.commit();
session.close();
//抛出异常
Iterator orderIterator = customer.getOrders().iterator();

执行以上代码,会抛出以下异常:
ERROR LazyInitializer:63 - Exception initializing proxy

4、延迟检索策略

优点:由应用程序决定需要加载哪些对象,可以避免执行多余的select语句,以及避免加载应用程序不需要访问的对象。因此呢个、能提高检索的性能,并且能节省内存空间。
缺点:应用程序如果希望访问游离状态的代理类实例,必须保证它在持久化状态时已经被初始化。
适用范围:一对多或者多对多关联;应用程序不需要立即访问或者根本不会访问的对象。

5、在多对一关联级别使用左外连接检索策略
- 默认情况下,多对一关联使用左外连接检索策略。
- 如果把Order.hbm.xml文件的<many-to-one>元素的outer-join属性设为true,总是使用左外连接检索策略。
- 对于以下程序代码:Order order = (Order)session.get(Order.class,new Long(1));
   在运行session.get()方法时,Hibernate执行以下select语句:
select * from ORDERS left outer join CUSTOMERS on ORDERS.CUSTOMER_ID=CUSTOMERS.ID where ORDERS.ID=1;

6、左外连接检索策略
优点:对应用程序完全透明,不管对象处于持久化状态,还是游离状态,应用程序都可以方便的从一个对象导航到与它关联的对象;使用了外连接,select语句数目少。
缺点:可能会加载应用程序不需要访问的对象,白白浪费许多内存空间;复杂的数据库表连接也会影响检索性能。
适用范围:多对一或者一对一关联;应用程序需要立即访问的对象;数据库系统具有;良好的表连接性能。

在程序中显式指定左外连接检索策略
- 在影射文件中设定的检索策略是固定的,要么为延迟检索,要么为立即检索,要么为外连接检索。
- 但应用逻辑是多种多样的,有些情况下需要延迟检索,而有些情况下需要外连接检索。
- Hibernate允许在应用程序中覆盖映射文件中设定的检索策略,由应用程序在运行时决定检索对象图的深度。

在程序中显式指定左外连接检索策略
- 以下Session的方法都用于检索OID为1的Customer对象:
session.createQuery("from Customer as c where c.id=1");
session.createQuery("from Customer as c left join fetch c.orders where c.id=1");
- 在执行第一个方法时,将使用映射文件配置的检索策略。
- 在执行第二个方法时,在HQL语句中显式指定左外连接检索关联的Order对象,因此会覆盖映射文件配置的jiansuocelue。不管在Customer.hbm.xml文件中<set>元素的lazy属性是true还是false,Hibernate都会执行以下select语句:
select * from CUSTOMERS left outer join ORDERS on CUSTOMERS.ID=ORDERS.CUSTOMER_ID where CUSTOMERS.ID=1;

7、一对一映射:

1)主键关联。

两个类:

public class Student
{
	private String id;
	
	private String name;
	
	private IdCard idCard;

	public String getId()
	{
		return id;
	}

	public void setId(String id)
	{
		this.id = id;
	}

	public String getName()
	{
		return name;
	}

	public void setName(String name)
	{
		this.name = name;
	}

	public IdCard getIdCard()
	{
		return idCard;
	}

	public void setIdCard(IdCard idCard)
	{
		this.idCard = idCard;
	}
	
	
}


 

public class IdCard
{
	private String id;
	
	private int number;
	
	private Student student;

	public String getId()
	{
		return id;
	}

	public void setId(String id)
	{
		this.id = id;
	}

	public int getNumber()
	{
		return number;
	}

	public void setNumber(int number)
	{
		this.number = number;
	}

	public Student getStudent()
	{
		return student;
	}

	public void setStudent(Student student)
	{
		this.student = student;
	}
	
	
}


两个表:

CREATE TABLE `student` (
  `id` varchar(255) NOT NULL,
  `name` varchar(255) default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;


 

CREATE TABLE `idcard` (
  `id` varchar(255) NOT NULL,
  `number` int(11) default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

表的生成可以使用类和相应的配置文件来自动生成,具体程序:

import org.hibernate.cfg.Configuration;
import org.hibernate.tool.hbm2ddl.SchemaExport;

public class Createtable
{
	public static void main(String[] args)
	{
		SchemaExport export = new SchemaExport(new Configuration().configure());
		
		export.create(true,true);
	}
}


这里主要是借助于SchemaExport这个类。


对应的映射文件:

Student.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
	
	<class name="com.cdtax.hibernate.Student" table="student">
	
		<id name="id" column="id" type="string">
			<generator class="uuid"></generator>
		</id>
		
		<property name="name" column="name" type="string"></property>
		
		<one-to-one name="idCard" class="com.cdtax.hibernate.IdCard" cascade="all" ></one-to-one>
		
		
		
	</class>
</hibernate-mapping>


IdCard.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
	
	<class name="com.cdtax.hibernate.IdCard" table="idcard">
	
		<id name="id" column="id" type="string">
			<generator class="foreign">
				<param name="property">student</param>
			</generator>
		</id>
		
		<property name="number" column="number" type="integer"></property>
		
		<one-to-one name="student" class="com.cdtax.hibernate.Student" ></one-to-one>
		
	</class>
	
</hibernate-mapping>


hibernate.cfg.xml

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
          "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
          "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<!-- Generated by MyEclipse Hibernate Tools.                   -->
<hibernate-configuration>

    <session-factory>
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="connection.url">jdbc:mysql://localhost:3306/hibernate</property>
        <property name="connection.username">root</property>
        <property name="connection.password">qwerty123</property>
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        
        
        <property name="show_sql">true</property>
        
        <mapping resource="Student.hbm.xml"/>
        <mapping resource="IdCard.hbm.xml"/>
    
    </session-factory>

</hibernate-configuration>


测试文件:保存记录

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;

public class HibernateTest
{
	private static SessionFactory sessionFactory;

	static
	{
		try
		{
			sessionFactory = new Configuration().configure()
					.buildSessionFactory();
		} catch (Exception ex)
		{
			ex.printStackTrace();
		}
	}
	public static void main(String[] args)
	{
		Student student = new Student();
		student.setName("zhangsan");
		
		IdCard idCard = new IdCard();
		idCard.setNumber(987654);
		
		student.setIdCard(idCard);
		idCard.setStudent(student);
		
		Session session = sessionFactory.openSession();
		Transaction tx = null;
		
		try
		{
			tx = session.beginTransaction();
			session.save(student);
			tx.commit();
		}
		catch(Exception ex)
		{
			if(null != tx)
			{
				tx.rollback();
				
			}
			ex.printStackTrace();
		}
		finally
		{
			session.close();
		}
//		------------------------------------
		
		
	}
}


执行结果:

Hibernate: insert into student (name, id) values (?, ?)
Hibernate: insert into idcard (number, id) values (?, ?)

查询记录:

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;

public class HibernateTest
{
	private static SessionFactory sessionFactory;

	static
	{
		try
		{
			sessionFactory = new Configuration().configure()
					.buildSessionFactory();
		} catch (Exception ex)
		{
			ex.printStackTrace();
		}
	}
	public static void main(String[] args)
	{
		
		Session session = sessionFactory.openSession();
		Transaction tx = null;
		
		Student student = null;
		
		try
		{
			tx = session.beginTransaction();
			student = (Student) session.get(Student.class, "402881c04169de08014169de08e80001");
			tx.commit();
		}
		catch(Exception ex)
		{
			if(null != tx)
			{
				tx.rollback();
				
			}
			ex.printStackTrace();
		}
		finally
		{
			session.close();
		}
		
		System.out.println(student.getName());
		System.out.println(student.getIdCard().getNumber());
	}
}


执行结果:

Hibernate: select student0_.id as id0_2_, student0_.name as name0_2_, idcard1_.id as id1_0_, idcard1_.number as number1_0_, student2_.id as id0_1_, student2_.name as name0_1_ from student student0_ left outer join idcard idcard1_ on student0_.id=idcard1_.id left outer join student student2_ on idcard1_.id=student2_.id where student0_.id=?
zhangsan
987654
可以看出,我们在打印时已经关闭了session,student处于游离状态,而还能打印出System.out.println(student.getIdCard().getNumber());说明是立即加载(立即检索),同时,根据执行的select语句,使用了了左外连接。

如果我们修改Student.hbm.xml和IdCard.hbm.xml的<one-to-one>元素,增加一个fetch属性如下:

对于Student.hbm.xml:<one-to-one name="idCard" class="com.cdtax.hibernate.IdCard" cascade="all" fetch="select"></one-to-one>

对于IdCard.hbm.xml:<one-to-one name="student" class="com.cdtax.hibernate.Student" fetch="select" ></one-to-one>

再次执行以上查询,结果为:

Hibernate: select student0_.id as id0_0_, student0_.name as name0_0_ from student student0_ where student0_.id=?
Hibernate: select idcard0_.id as id1_0_, idcard0_.number as number1_0_ from idcard idcard0_ where idcard0_.id=?
zhangsan
987654

可以看出执行了两条select语句

我们是可以在配置文件中指定查询的方式的。

在一对一的关联中,默认使用立即检索,如果要使用延迟加载,需要在one-to-one元素中将constrained属性设置为true,并且将待加载的一方的class元素中的lazy属性设置为true(或者不去设置,因为该属性默认值就是true)。

Student.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
	
	<class name="com.cdtax.hibernate.Student" table="student">
	
		<id name="id" column="id" type="string">
			<generator class="uuid"></generator>
		</id>
		
		<property name="name" column="name" type="string"></property>
		
		<one-to-one name="idCard" class="com.cdtax.hibernate.IdCard" cascade="all" fetch="select" constrained="true"></one-to-one>
		
		
		
	</class>
</hibernate-mapping>


IdCard.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
	
	<class name="com.cdtax.hibernate.IdCard" table="idcard" lazy="true">
	
		<id name="id" column="id" type="string">
			<generator class="foreign">
				<param name="property">student</param>
			</generator>
		</id>
		
		<property name="number" column="number" type="integer"></property>
		
		<one-to-one name="student" class="com.cdtax.hibernate.Student" fetch="select" ></one-to-one>
		
	</class>
	
</hibernate-mapping>

一对一加载时默认使用左外连接,可以通过修改fetch属性为select修改成每次发送一条select语句的形式。

这是执行:

Hibernate: select student0_.id as id0_0_, student0_.name as name0_0_ from student student0_ where student0_.id=?
zhangsan
Exception in thread "main" org.hibernate.LazyInitializationException: could not initialize proxy - no Session
 at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:132)
 at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:174)
 at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190)
 at com.cdtax.hibernate.IdCard_$$_javassist_0.getNumber(IdCard_$$_javassist_0.java)
 at com.cdtax.hibernate.HibernateTest.main(HibernateTest.java:84)
说明是延迟加载了。

2)外键关联

一对一是一对多的特例,多的一方退化为一。

使用外键关联,修改IdCard.hbm.xml,将<one-to-one>修改为<many-to-one>,同时修改<id>

如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
	
	<class name="com.cdtax.hibernate.IdCard" table="idcard" lazy="true">
		<!--
		<id name="id" column="id" type="string">
			<generator class="foreign">
				<param name="property">student</param>
			</generator>
		</id>
		-->
		<id name="id" column="id" type="string">
			<generator class="uuid">
			</generator>
		</id>
		
		<property name="number" column="number" type="integer"></property>
		<!-- 
		<one-to-one name="student" class="com.cdtax.hibernate.Student" fetch="select" ></one-to-one>
		 -->
		 <many-to-one name="student" class="com.cdtax.hibernate.Student" column="student_id" unique="true"></many-to-one>
	</class>
	
</hibernate-mapping>

many-to-one元素需要增加一个unique属性,值为true,因为对于一般的many-to-one来说,属性应该是一个集合,而现在是一个单一的值,unique=true就是用来说明值是单一值,而非集合。这时idcard表结构改变,增加了一列:student_id,是外键,表的id使用uuid。

从新生成表如下:

use hibernate;

drop table if exists idcard;
drop table if exists student;

create table idcard (id varchar(255) not null, number integer, student_id varchar(255) unique, primary key (id));
create table student (id varchar(255) not null, name varchar(255), primary key (id));


使用保存测试:

public static void main(String[] args)
	{
		Student student = new Student();
		student.setName("zhangsan");
		
		IdCard idCard = new IdCard();
		idCard.setNumber(987654);
		
		student.setIdCard(idCard);
		idCard.setStudent(student);
		
		Session session = sessionFactory.openSession();
		Transaction tx = null;
		
		try
		{
			tx = session.beginTransaction();
			session.save(student);
			tx.commit();
		}
		catch(Exception ex)
		{
			if(null != tx)
			{
				tx.rollback();
				
			}
			ex.printStackTrace();
		}
		finally
		{
			session.close();
		}
}


执行结果:

Hibernate: insert into idcard (number, student_id, id) values (?, ?, ?)
Hibernate: insert into student (name, id) values (?, ?)
Hibernate: update idcard set number=?, student_id=? where id=?

为什么会出现一个update语句呢,这是因为在插入idcard时,student_id还没有值,在插入student表后,才生成一个id,也就是idcard表的外键student_id的值。所以在生成表的时候,没有指定idcard表的外键,如果生成表时指定了外键,那么在执行时,第一句插入语句就将出错,因为外键student_id为空了。

将student.hbm.xml修改一下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
	
	<class name="com.cdtax.hibernate.Student" table="student">
	
		<id name="id" column="id" type="string">
			<generator class="uuid"></generator>
		</id>
		
		<property name="name" column="name" type="string"></property>
		
		<one-to-one name="idCard" class="com.cdtax.hibernate.IdCard" cascade="all" fetch="select" ></one-to-one>
		
		
		
	</class>
</hibernate-mapping>


去掉了<one-to-one>中的constrained=true

执行结果:

Hibernate: insert into student (name, id) values (?, ?)
Hibernate: insert into idcard (number, student_id, id) values (?, ?, ?)

没有了update语句,同时插入的顺序也改变了。

外键关联本质上是一对多的蜕化形式,将many-to-one元素中增加unique="true"属性就变成了一对一。


8、有时会出现如下图的一种关系:

idcard与student是一对一的关系,而student与team是一对多的关系,而team有对应了其他的student,这就要设计好加载关系,否则查询一个学生的信息,有可能将所有的idcard、student和team都加载了。

先完成一个保存的例子:

首先是三个bean:Student、IdCard和Team

public class Student
{
	private String id;
	
	private String name;
	
	private IdCard idCard;
	
	private Team team;

	public Team getTeam()
	{
		return team;
	}

	public void setTeam(Team team)
	{
		this.team = team;
	}

	public String getId()
	{
		return id;
	}

	public void setId(String id)
	{
		this.id = id;
	}

	public String getName()
	{
		return name;
	}

	public void setName(String name)
	{
		this.name = name;
	}

	public IdCard getIdCard()
	{
		return idCard;
	}

	public void setIdCard(IdCard idCard)
	{
		this.idCard = idCard;
	}
	
	
}

public class IdCard
{
	private String id;
	
	private int number;
	
	private Student student;

	public String getId()
	{
		return id;
	}

	public void setId(String id)
	{
		this.id = id;
	}

	public int getNumber()
	{
		return number;
	}

	public void setNumber(int number)
	{
		this.number = number;
	}

	public Student getStudent()
	{
		return student;
	}

	public void setStudent(Student student)
	{
		this.student = student;
	}
	
	
}

import java.util.Set;

public class Team
{
	private String id;
	
	private String name;
	
	private Set<Student> students;

	public String getId()
	{
		return id;
	}

	public void setId(String id)
	{
		this.id = id;
	}

	public String getName()
	{
		return name;
	}

	public void setName(String name)
	{
		this.name = name;
	}

	public Set<Student> getStudents()
	{
		return students;
	}

	public void setStudents(Set<Student> students)
	{
		this.students = students;
	}
	
	
}

对应的三个hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
	
	<class name="com.cdtax.hibernate.Student" table="student">
	
		<id name="id" column="id" type="string">
			<generator class="uuid"></generator>
		</id>
		
		<property name="name" column="name" type="string"></property>
		
		<one-to-one name="idCard" class="com.cdtax.hibernate.IdCard" cascade="all" constrained="true"></one-to-one>
		
		<many-to-one name="team" class="com.cdtax.hibernate.Team" column="team_id" fetch="join" cascade="all"></many-to-one>
		
		
	</class>
</hibernate-mapping>

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
	
	<class name="com.cdtax.hibernate.IdCard" table="idcard" lazy="true">
		
		<id name="id" column="id" type="string">
			<generator class="foreign">
				<param name="property">student</param>
			</generator>
		</id>
	
		
		
		<property name="number" column="number" type="integer"></property>
		
		<one-to-one name="student" class="com.cdtax.hibernate.Student" cascade="none" ></one-to-one>

		
		 
	</class>
	
</hibernate-mapping>

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
	
	<class name="com.cdtax.hibernate.Team" table="team">
		
		<id name="id" column="id" type="string">
			<generator class="uuid">
			</generator>
		</id>
	
		
		
		<property name="name" column="name" type="string"></property>
		
		<set name="students" lazy="false" cascade="all" inverse="true">
			<key column="team_id"></key>
			<one-to-many class="com.cdtax.hibernate.Student"/>
		</set>

		 
	</class>
	
</hibernate-mapping>

然后是数据库表的生成

import org.hibernate.cfg.Configuration;
import org.hibernate.tool.hbm2ddl.SchemaExport;

public class Createtable
{
	public static void main(String[] args)
	{
		SchemaExport export = new SchemaExport(new Configuration().configure());
		
		export.create(true,false);
	}
}
结果:
alter table student drop foreign key FK8FFE823B554B65F3
alter table student drop foreign key FK8FFE823BB04F9E7
drop table if exists idcard
drop table if exists student
drop table if exists team
create table idcard (id varchar(255) not null, number integer, primary key (id))
create table student (id varchar(255) not null, name varchar(255), team_id varchar(255), primary key (id))
create table team (id varchar(255) not null, name varchar(255), primary key (id))
alter table student add index FK8FFE823B554B65F3 (id), add constraint FK8FFE823B554B65F3 foreign key (id) references idcard (id)
alter table student add index FK8FFE823BB04F9E7 (team_id), add constraint FK8FFE823BB04F9E7 foreign key (team_id) references team (id)

 然后编写测试:

import java.util.HashSet;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;

public class HibernateTest
{
	private static SessionFactory sessionFactory;
	
	static
	{
		try
		{
			sessionFactory = new Configuration().configure().buildSessionFactory();
		}
		catch(Exception ex)
		{
			ex.printStackTrace();
		}
	}
	
	public static void main(String[] args)
	{
		Student student = new Student();
		student.setName("zhangsan");
		
		IdCard idCard = new IdCard();
		idCard.setNumber(987654);
		
		student.setIdCard(idCard);
		idCard.setStudent(student);
		
		Team team = new Team();
		team.setName("team1");
		
		team.setStudents(new HashSet<Student>());
		team.getStudents().add(student);
		
		student.setTeam(team);
		
		Session session = sessionFactory.openSession();
		Transaction tx = null;
		
		try
		{
			tx = session.beginTransaction();
			session.save(student);
			tx.commit();
		}
		catch(Exception ex)
		{
			if(null != tx)
			{
				tx.rollback();
			}
			ex.printStackTrace();
		}
		finally
		{
			session.close();
		}
		
	}
}

这里只保存了student,而最终的执行结果是:

Hibernate: insert into idcard (number, id) values (?, ?)
Hibernate: insert into team (name, id) values (?, ?)
Hibernate: insert into student (name, team_id, id) values (?, ?, ?)

三个表中都插入了数据。

我们将测试程序改为查询,如下程序片段。

try
		{
			tx = session.beginTransaction();
//			session.save(student);
			Team team1 = (Team)session.get(Team.class,"402881c04227f188014227f18a030002");
			System.out.println(team1.getName());
			tx.commit();
		}

执行的结果:

Hibernate: select team0_.id as id2_0_, team0_.name as name2_0_ from team team0_ where team0_.id=?
Hibernate: select students0_.team_id as team3_1_, students0_.id as id1_, students0_.id as id0_0_, students0_.name as name0_0_, students0_.team_id as team3_0_0_ from student students0_ where students0_.team_id=?
team1

结果打印出来了,执行了两条select语句。

修改hbm.xml文件,如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
	
	<class name="com.cdtax.hibernate.IdCard" table="idcard" >
		
		<id name="id" column="id" type="string">
			<generator class="foreign">
				<param name="property">student</param>
			</generator>
		</id>
	
		
		
		<property name="number" column="number" type="integer"></property>
		
		<one-to-one name="student" class="com.cdtax.hibernate.Student" cascade="none" fetch="select"></one-to-one>

		
		 
	</class>
	
</hibernate-mapping>

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
	
	<class name="com.cdtax.hibernate.Team" table="team">
		
		<id name="id" column="id" type="string">
			<generator class="uuid">
			</generator>
		</id>
	
		
		
		<property name="name" column="name" type="string"></property>
		
		<set name="students" lazy="false" cascade="all" inverse="true" fetch="select">
			<key column="team_id"></key>
			<one-to-many class="com.cdtax.hibernate.Student"/>
		</set>

		 
	</class>
	
</hibernate-mapping>

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
	
	<class name="com.cdtax.hibernate.Student" table="student">
	
		<id name="id" column="id" type="string">
			<generator class="uuid"></generator>
		</id>
		
		<property name="name" column="name" type="string"></property>
		
		<one-to-one name="idCard" class="com.cdtax.hibernate.IdCard" cascade="all"  fetch="select"></one-to-one>
		
		<many-to-one name="team" class="com.cdtax.hibernate.Team" column="team_id" fetch="select" cascade="all"></many-to-one>
		
		
	</class>
</hibernate-mapping>

则执行的结果为:

Hibernate: select team0_.id as id2_0_, team0_.name as name2_0_ from team team0_ where team0_.id=?
Hibernate: select students0_.team_id as team3_1_, students0_.id as id1_, students0_.id as id0_0_, students0_.name as name0_0_, students0_.team_id as team3_0_0_ from student students0_ where students0_.team_id=?
Hibernate: select idcard0_.id as id1_0_, idcard0_.number as number1_0_ from idcard idcard0_ where idcard0_.id=?
team1

可以看到变成了三条select查询语句

关键就是fetch这个属性的值,对于有些查询语句,如果连接的层次过深(join过多),性能反而会下降,这时使用fetch=select,变成多条select语句能起到优化性能的作用

对于连接的深度,还可以通过在hibernate.cfg.xml的sessionfactory标签中增加<property name="max_fetch_depth">2</property>来进行限制(没有经过测试)

9、多对多映射

例如存在这样的需求,一个学生对应多门课程,一门课程可以对应对个学生,学生与课程之间就是多对多的关系。这时的映射需要一个中间表——连接表,其实多对多映射就是两个一对多的映射,如下图


建立两个类:

import java.util.Set;

public class Student
{
	private String id;
	
	private String name;

	private Set<Course> courses;

	public String getId()
	{
		return id;
	}

	public void setId(String id)
	{
		this.id = id;
	}

	public String getName()
	{
		return name;
	}

	public void setName(String name)
	{
		this.name = name;
	}

	public Set<Course> getCourses()
	{
		return courses;
	}

	public void setCourses(Set<Course> courses)
	{
		this.courses = courses;
	}
	
}

import java.util.Set;

public class Course
{
	private String id;
	
	private String name;
	
	private Set<Student> students;

	public String getId()
	{
		return id;
	}

	public void setId(String id)
	{
		this.id = id;
	}

	public String getName()
	{
		return name;
	}

	public void setName(String name)
	{
		this.name = name;
	}

	public Set<Student> getStudents()
	{
		return students;
	}

	public void setStudents(Set<Student> students)
	{
		this.students = students;
	}
	
}

建立相应的映射文件hbm.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
	
	<class name="com.cdtax.hibernate.Student" table="student">
	
		<id name="id" column="id" type="string">
			<generator class="uuid"></generator>
		</id>
		
		<property name="name" column="name" type="string"></property>
		
		<set name="courses" table="student_course" cascade="save-update">
		<!-- cascade不能使用all -->
			<key column="student_id"></key>
			<many-to-many class="com.cdtax.hibernate.Course" column="course_id"></many-to-many>
		</set>		
	</class>
</hibernate-mapping>

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
	
	<class name="com.cdtax.hibernate.Course" table="course">
	
		<id name="id" column="id" type="string">
			<generator class="uuid"></generator>
		</id>
		
		<property name="name" column="name" type="string"></property>
		
		<set name="students" table="student_course" cascade="save-update">
		<!-- cascade不能使用all -->
			<key column="course_id"></key>
			<many-to-many class="com.cdtax.hibernate.Student" column="student_id"></many-to-many>
		</set>		
	</class>
</hibernate-mapping>

这里的级联cascade为什么不能为all或者说为什么不能有delete,因为我删除一个学生,不能同时将学生对应的课程也删掉,课程别的学生可能也会用到;同理删除了一门课程,也不能将对应的学生都删掉,因为学生可能还选有其他课程。

创建相应的表:

drop table if exists course
drop table if exists student
drop table if exists student_course
create table course (id varchar(255) not null, name varchar(255), primary key (id))
create table student (id varchar(255) not null, name varchar(255), primary key (id))
create table student_course (student_id varchar(255) not null, course_id varchar(255) not null, primary key (course_id, student_id))

对映射文件的解释:

主要就是set的理解:

<set name="courses" table="student_course" cascade="save-update">
<!-- cascade不能使用all -->
<key column="student_id"></key>
<many-to-many class="com.cdtax.hibernate.Course" column="course_id"></many-to-many>
</set>

因为Student中的courses是集合,所以使用set标签,set的name是Student类中的属性名,table=“student_course”指出属性使用的连接表的名字,key子标签的column指出student类中保存的Curse在连接表中用那一列来表明,这里是student_id,就是在student_course表中应该有student_id一列,是连接表的外键。

<many-to-many>中的class指出成员变量courses的类型,column指出连接表中与course关联的列名。如下图:


对应的course中的集合set,students


在hibernate.cfg.xml中配置好资源

写一个测试类:

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;

public class HibernateTest
{
	private static SessionFactory sessionFactory;
	
	static
	{
		try
		{
			sessionFactory = new Configuration().configure().buildSessionFactory();
		}
		catch(Exception ex)
		{
			ex.printStackTrace();
		}
	}
	
	public static void main(String[] args)
	{
		Student student = new Student();
		student.setName("wangwu");
		
		Course course = new Course();
		course.setName("music");
		
		student.setCourses(new HashSet<Course>());
		course.setStudents(new HashSet<Student>());
		
		student.getCourses().add(course);
		course.getStudents().add(student);
		
		Session session = sessionFactory.openSession();
		Transaction tx = null;
		
		try
		{
			tx = session.beginTransaction();
			
			session.save(student);
			tx.commit();
		}
		catch(Exception ex)
		{
			if(null != tx)
			{
				tx.rollback();
			}
			ex.printStackTrace();
		}
		finally
		{
			session.close();
		}
		
	}
}

运行结果:

Hibernate: insert into student (name, id) values (?, ?)
Hibernate: insert into course (name, id) values (?, ?)
Hibernate: insert into student_course (student_id, course_id) values (?, ?)
Hibernate: insert into student_course (course_id, student_id) values (?, ?)
org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:94)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:266)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:171)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1028)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:366)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:137)
at com.cdtax.hibernate.HibernateTest.main(HibernateTest.java:50)
Caused by: java.sql.BatchUpdateException: Duplicate entry '402881c0422b844501422b8446600002-402881c0422b844501422b844660000' for key 1
at com.mysql.jdbc.PreparedStatement.executeBatchSerially(PreparedStatement.java:1269)
at com.mysql.jdbc.PreparedStatement.executeBatch(PreparedStatement.java:955)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:268)
... 8 more

出现异常,原因是Duplicate entry 。。。重复的实体,为什么呢,这是因为默认的set标签表明都是自己来维护对应关联关系,保存student时,保存在student表中,同时还要保存student_course表,因为级联的原因,同时要保存course,而course保存时,在保存course表的同时,他的set也是采用了默认值,即也是自己来维护关联关系,也要写student_course表,所以重复了,修改一下,将测试程序中的:

course.getStudents().add(student);这一句注释掉,

执行:

Hibernate: insert into student (name, id) values (?, ?)
Hibernate: insert into course (name, id) values (?, ?)
Hibernate: insert into student_course (student_id, course_id) values (?, ?)

插入成功;

再次修改:将Course.hbm.xml修改如下:

<set name="students" table="student_course" cascade="save-update" inverse="true">
		<!-- cascade不能使用all -->
			<key column="course_id"></key>
			<many-to-many class="com.cdtax.hibernate.Student" column="student_id"></many-to-many>
		</set>	

增加一个inverse="true",即指明维护反转,由对方维护,再次执行,执行成功;

再次修改,将

course.getStudents().add(student);这一句注释去掉,打开,执行,成功。

查询测试:修改HibernateTest

try
		{
			tx = session.beginTransaction();
			
			Student student1 = (Student)session.get(Student.class, "402881c0422b4a2c01422b4a2d730001");
			
			Set<Course> set = student1.getCourses();
			
			for(Iterator<Course> iter = set.iterator();iter.hasNext();)
			{
				System.out.println(iter.next().getName());
			}
			tx.commit();
		}

结果:

Hibernate: select student0_.id as id0_0_, student0_.name as name0_0_ from student student0_ where student0_.id=?
Hibernate: select courses0_.student_id as student1_1_, courses0_.course_id as course2_1_, course1_.id as id2_0_, course1_.name as name2_0_ from student_course courses0_ left outer join course course1_ on courses0_.course_id=course1_.id where courses0_.student_id=?
music

两条查询语句,查询了student和course

继续修改:

try
		{
			tx = session.beginTransaction();
			
			Student student1 = (Student)session.get(Student.class, "402881c0422b4a2c01422b4a2d730001");
			Course course1 = (Course)session.get(Course.class, "402881c0422b49d901422b49da320002");
			
			student1.getCourses().add(course1);
			course1.getStudents().add(student1);
			

			tx.commit();
		}
执行结果:
Hibernate: select student0_.id as id0_0_, student0_.name as name0_0_ from student student0_ where student0_.id=?

Hibernate: select course0_.id as id2_0_, course0_.name as name2_0_ from course course0_ where course0_.id=?
Hibernate: select courses0_.student_id as student1_1_, courses0_.course_id as course2_1_, course1_.id as id2_0_, course1_.name as name2_0_ from student_course courses0_ left outer join course course1_ on courses0_.course_id=course1_.id where courses0_.student_id=?
Hibernate: select students0_.course_id as course2_1_, students0_.student_id as student1_1_, student1_.id as id0_0_, student1_.name as name0_0_ from student_course students0_ left outer join student student1_ on students0_.student_id=student1_.id where students0_.course_id=?
Hibernate: insert into student_course (student_id, course_id) values (?, ?)

不需要刻意调用update方法,就能更新

继续修改,执行删除

		try
		{
			tx = session.beginTransaction();
			
			Student student1 = (Student)session.get(Student.class, "402881c0422b4a2c01422b4a2d730001");
			Course course1 = (Course)session.get(Course.class, "402881c0422b49d901422b49da320002");
						
			student1.getCourses().remove(course1);
			
			tx.commit();
		}
执行结果:

Hibernate: select student0_.id as id0_0_, student0_.name as name0_0_ from student student0_ where student0_.id=?
Hibernate: select course0_.id as id2_0_, course0_.name as name2_0_ from course course0_ where course0_.id=?
Hibernate: select courses0_.student_id as student1_1_, courses0_.course_id as course2_1_, course1_.id as id2_0_, course1_.name as name2_0_ from student_course courses0_ left outer join course course1_ on courses0_.course_id=course1_.id where courses0_.student_id=?
Hibernate: delete from student_course where student_id=? and course_id=?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值