JavaEE学习笔记——2、Hibernate基础

==============================ORM的基本知识==============================

目前主流数据库是关系型数据库(RDB),而Java则是面向对象的编程语言(OOPL),要结合两者相当麻烦。

ORM是为了解决这一问题而产生的概念。


OOPL的优势:

面向对象的建模、操作

多态、继承

摒弃难以理解的过程

建安易用,易理解


RDB的优势:

大量数据查找、排序

集合数据连接操作、映射

数据库访问的并发、事务

数据库的约束、隔离


ORM,Object/Relation Mapping可理解为一种规范,它概述了这类框架的特征:完成OOPL到RDB的映射。

当ORM框架完成映射后,即可以利用OOPL的简单易用,又能使用RDB的技术优势。


其实JavaEE规范里的JPA规范就是一种ORM规范,只是JPA并不提供任何ORM实现,只提供了系列的编程接口。


==============================ORM和Hibernate的关系==============================


Hibernate是一个ORM框架,管理Java类到数据库表的映射,还提供查询和获取数据的方法,大幅度缩短使用JDBC处理数据持久化的时间。


==============================Hibernate的基本映射思想==============================


所有ORM工具大致上都遵循相同的映射思路:数据表映射类、数据表的行映射对象(即实例)、数据表的列(字段)映射对象的属性。


==============================Hibernate入门知识==============================

Hibernate被称为低侵入式设计,因为它采用POJO作为PO,不要求持久化类继承任何父类,或者实现任何接口。

Hibernate底层依然是基于JDBC的,因此应用程序使用它少不了JDBC驱动。

所有ORM框架中有一个非常重要的媒介:PO,通过该对象可对数据执行CRUD操作,以OO的方式操作DB。

数据源是一种提高数据库连接性能的常规手段,hibernate推荐采用c3p0数据源管理数据库连接。

在官网上下载Hibernate的压缩包后,里面的hibernate.jar和lib下的required、jpa子目录下所有jar包是必须的。

一个简单的示例:

//domain包下的POJO
public class News {
	private Integer id;
	private String title;
	private String content;

	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}

	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}

	public String getContent() {
		return content;
	}
	public void setContent(String content) {
		this.content = content;
	}
}


<?xml version="1.0" encoding="UTF-8"?>
<!-- 指定Hibernate3映射文件的DTD信息 -->
<!DOCTYPE hibernate-mapping PUBLIC
	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
	"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- hibernate mapping是映射文件的根元素 -->
<hibernate-mapping package="basicusage.chapter1">
	<!-- 每个class元素对应一个持久化对象 -->
	<class name="News" table="t_newss">
		<!-- id元素定义持久化类的标识属性 -->
		<id name="id">
			<!-- 指定主键生成策略 -->
			<generator class="identity" />
		</id>
		<!-- property元素定义常规属性 -->
		<property name="title" />
		<property name="content" />
	</class>
</hibernate-mapping>

PO = POJO + hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- 指定Hibernate配置文件的DTD信息 -->
<!DOCTYPE hibernate-configuration PUBLIC
	"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
	"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<!-- hibernate-configuration是连接配置文件的根元素 -->
<hibernate-configuration>
	<session-factory>
		<!-- 指定连接数据库所用的驱动 -->
		<property name="contention.driver_class">com.mysql.jdbc.Driver</property>
		<!-- 指定连接数据库的url,hibernate连接的数据库名 -->
		<property name="connection.url">jdbc:mysql://192.168.56.111:3306/hibernateexercise</property>
		<!-- 指定连接数据库的用户名 -->
		<property name="connection.username">root</property>
		<!-- 指定连接数据库的密码 -->
		<property name="connection.password">123321</property>
		<!-- 指定连接池的最大连接数 -->
		<property name="hibernate.c3p0.max_size">20</property>
		<!-- 指定连接池的最小连接数 -->
		<property name="hibernate.c3p0.max_size">1</property>
		<!-- 指定连接池的超时时间 -->
		<property name="hibernate.c3p0.timeout">5000</property>
		<!-- 指定连接池的最大缓存多少个Statement对象 -->
		<property name="hibernate.c3p0.max_statements">100</property>
		<property name="hibernate.c3p0.idle_test_period">3000</property>
		<property name="hibernate.c3p0.acquire_increment">2</property>
		<property name="hibernate.c3p0.validate">true</property>
		<!-- 指定数据库方言 -->
		<property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
		<!-- 根据需要自动创建数据库表 -->
		<property name="hibernate.hbm2ddl.auto">update</property>
		
		<!-- 显示SQL -->
		<property name="hibernate.show_sql">true</property>
		<!-- 格式化良好地显示SQL -->
		<property name="hibernate.format_sql">true</property>
		<!-- 在SQL中添加有助于调试的注释 -->
		<property name="hibernate.use_sql_comments">false</property>
		
		<!-- 罗列所有的映射文件 -->
		<mapping resource="basicusage/chapter1/News.hbm.xml" />
	</session-factory>
</hibernate-configuration>

持久化操作代码:

public class NewsManager
{
	public static void main(String[] args) throws Exception
	{
		Configuration conf = new Configuration()
		// 下面的方法默认加载hibernate.cfg.xml
				.configure();
		SessionFactory sf = conf.buildSessionFactory();
		Session sess = sf.openSession();
		// 开始事务
		Transaction tx = sess.beginTransaction();
		News n = new News();
		n.setTitle("create title");
		n.setContent("create content");
		sess.save(n);
		// 提交事务
		tx.commit();
		sess.close();
		sf.close();
	}
}

hibernate持久化通常操组步骤:
1、开发持久化类,由POJO加映射文件组成。
2、获取Configuration。
3、获取SessionFactory。
4、获取Session,打开事务。
5、用面向对象的方式操作数据库。
6、关闭事务,关闭Session。

对Hibernate的数据访问进行优化时,需要了解Hibernate底层SQL操作,这是因为数据访问对绝大部分应用来说,是一个巨大、耗时的操作。


==============================使用Eclipse开发Hibernate应用==============================


Eclipse.hibernate工具http://www.jboss.org/tools/download/stable

该工具可以通过数据库现存表的结构反向生成对应的实体类。


==============================Hibernate的体系和核心API==============================


现在我们知道了一个概念Hibernate Session,只有处于Session管理下的POJO才具有持久化操作能力。当应用程序对于处于Session管理下的POJO实例执行操作时,Hibernate将这种面向对象的操作转换成了持久化操作能力。

通过上图能够发现HIbernate需要一个hibernate.properties文件,该文件用于配置Hibernate和数据库连接的信息。还需要一个XML文件,该映射文件确定了持久化类和数据表、数据列之间的想对应关系。

除了使用hibernate.properties文件,还可以采用另一种形式的配置文件: *.cfg.xml文件。在实际应用中,采用XML配置文件的方式更加广泛,两种配置文件的实质是一样的。

Hibernate的持久化解决方案将用户从赤裸裸的JDBC访问中释放出来,用户无需关注底层的JDBC操作,而是以面向对象的方式进行持久层操作。底层数据连接的获得、数据访问的实现、事务控制都无需用户关心。这是一种“全面解决”的体系结构方案,将应用层从底层的JDBC/JTA API中抽象出来。通过配置文件来管理底层的JDBC连接,让Hibernate解决持久化访问的实现。


(1)SessionFactory这是Hibernate的关键对象,它是单个数据库映射关系经过编译后的内存镜像,它也是线程安全的它是生成Session的工厂,本身要应用到ConnectionProvider,该对象可以在进程和集群的级别上,为那些事务之间可以重用的数据提供可选的二级缓存。

(2)Session:它是应用程序和持久存储层之间交互操作的一个单线程对象。它也是Hibernate持久化操作的关键对象,所有的持久化对象必须在Session的管理下才能够进行持久化操作。此对象的生存周期很短,其隐藏了JDBC连接,也是Transaction 的工厂。Session对象有一个一级缓存,现实执行Flush之前,所有的持久化操作的数据都在缓存中Session对象处。

(3)持久化对象:系统创建的POJO实例一旦与特定Session关联,并对应数据表的指定记录,那该对象就处于持久化状态,这一系列的对象都被称为持久化对象。程序中对持久化对象的修改,都将自动转换为持久层的修改。持久化对象完全可以是普通的Java Beans/POJO,唯一的特殊性是它们正与Session关联着。

(4)瞬态对象和脱管对象:系统进行new关键字进行创建的Java 实例,没有Session 相关联,此时处于瞬态。瞬态实例可能是在被应用程序实例化后,尚未进行持久化的对象。如果一个曾今持久化过的实例,但因为Session的关闭而转换为脱管状态。

(5)事务(Transaction):代表一次原子操作,它具有数据库事务的概念。但它通过抽象,将应用程序从底层的具体的JDBC、JTA和CORBA事务中隔离开。在某些情况下,一个Session 之内可能包含多个Transaction对象。虽然事务操作是可选的,但是所有的持久化操作都应该在事务管理下进行,即使是只读操作。

(6)连接提供者(ConnectionProvider):它是生成JDBC的连接的工厂,同时具备连接池的作用。他通过抽象将底层的DataSource和DriverManager隔离开。这个对象无需应用程序直接访问,仅在应用程序需要扩展时使用。

(7)事务工厂(TransactionFactory):他是生成Transaction对象实例的工厂。该对象也无需应用程序的直接访问。


==============================Hibernate的配置文件==============================


除了通过xml配置外,还可以创建Configuration对象,通过属性设置的方式配置,不过实际开发中并不使用。

public class NewsManager
{
	public static void main(String[] args) throws Exception
	{
		//实例化Configuration,不加载任何配置文件
		Configuration conf = new Configuration()
			//通过addClass方法添加持久化类
			.addClass(domain.News.class)
			//通过setProperty设置Hibernate的连接属性。
			.setProperty("hibernate.connection.driver_class" , "com.mysql.jdbc.Driver")
			.setProperty("hibernate.connection.url" , "jdbc:mysql://localhost/hibernate")
			.setProperty("hibernate.connection.username" , "root")
			.setProperty("hibernate.connection.password" , "32147")
			.setProperty("hibernate.c3p0.max_size" , "20")
			.setProperty("hibernate.c3p0.min_size" , "1")
			.setProperty("hibernate.c3p0.timeout" , "5000")
			.setProperty("hibernate.c3p0.max_statements" , "100")
			.setProperty("hibernate.c3p0.idle_test_period" , "3000")
			.setProperty("hibernate.c3p0.acquire_increment" , "2")
			.setProperty("hibernate.c3p0.validate" , "true")
			.setProperty("hibernate.dialect" , "org.hibernate.dialect.MySQLInnoDBDialect")
			.setProperty("hibernate.hbm2ddl.auto" , "create");
		//以Configuration创建SessionFactory
		SessionFactory sf = conf.buildSessionFactory();
		//实例化Session
		Session sess = sf.openSession();
		//开始事务
		Transaction tx = sess.beginTransaction();
		//创建消息实例
		News n = new News();
		//设置消息标题和消息内容
		n.setTitle("it is title");
		n.setContent("it is content");
		//保存消息
		sess.save(n);
		//提交事务
		tx.commit();
		//关闭Session
		sess.close();
	}
}

Hibernate\hibernate-distribution-3.6.10.Final\project\etc目录下有配置文件模板

hibernate提供了连接池配置hibernate.connection.pool_size,但自带的连接池仅有测试价值,实际项目中使用c3p0或proxool。


==============================持久化类的基本要求==============================


基本规则:

1、一个无参构造器,这是为了让Hibernate能使用Constructor.newInstance()来创建持久化类的实例。

2、提供一个标识属性,通常是主键字段,或者联合主键。

3、为持久化类的每个属性提供setter和getter方法,使之具有JavaBean风格。

4、使用非final的类,如果PO没有实现任何借口的话,Hibernate使用CGLIB生成代理,该代理对象是PO子类的实例。如果使用了final类,则无法生成CGLIB代理,将无法进行性能优化。

5、重写equals()和hashCode()方法。


==============================持久化对象的状态==============================


Transient(瞬态):对象由new创建,尚未与Session关联。

Persistent(持久化):持久化实例在DB中有对应的记录,拥有一个持久化标识(identifier)。

Detached(脱管):某个实例曾处于持久化状态,但随后与之关联的Session被关闭。

改变PO状态的方法:

save()和persist(),Hibernate之所以提供与save功能几乎类似的persist方法,一方面是为了照顾JPA的用法习惯,另一方面,它们之间有一个区别:save会返回PO的标识属性值,persist则没有返回值,因为save会立即将PO对应的数据插入DB,而persist则保证当它在一个事务外部被调用时,并不立即转换成insert语句,这个功能很有用,尤其当我们封装一个长会话流程的时候。

load()和get(),这两方法主要区别在于是否延迟加载,load具有延迟加载功能,它不会立即访问DB,当试图加载的记录不存在时,load可能返回一个未初始化的代理对象;而get总是立即访问DB,当试图加载的记录不存在时,返回null。

update()和updateOrSave(),前者保存应用对PO所做的修改,若不清楚该PO是否被持久化过,则使用后者进行自动判断。

merge()也可将应用对脱管对象所做的修改保存到DB,区别于update的是,merge不会持久化给定对象,举例来说:update(a)后,a对象将变为持久化状态,但merge(a)后,a对象不会变为持久化状态,a对象依然不被关联到session。

当使用merge保存应用对脱管对象所做的修改时,如果Session中存在相同持久化标识的PO,merge方法里提供对象的状态将覆盖原有PO的状态,如果Session中没有相应的PO,则尝试从DB中加载,或创建型额PO,最后返回该PO。

当使用load或get方法加载PO时,还可指定一个“锁模式”(LockMode),通过lock()方法。

Notice:Hibernate本身不提供直接执行update或delete语句的API,Hibernate提供的是一种OO的状态管理。


==============================Hibernate的基本映射==============================


映射文件以.hbm.xml结尾,每个Hibernate映射文件的基本结构都是相同的。


==============================List、Set、Map等级和属性映射==============================


List:

public class Person
{
	//标识属性
	private Integer id;
	//普通属性name
	private String name;
	//普通属性age
	private int age;
	//集合属性,保留该对象关联的学校
	private List<String> schools = new ArrayList<String>();
	
	//id属性的setter和getter方法
	public void setId(Integer id)
	{
		this.id = id;
	}
	public Integer getId()
	{
		return this.id;
	}
	
	//name属性的setter和getter方法
	public void setName(String name)
	{
		this.name = name;
	}
	public String getName()
	{
		return this.name;
	}
	
	//age属性的setter和getter方法
	public void setAge(int age)
	{
		this.age = age;
	}
	public int getAge()
	{
		return this.age;
	}
	
	//schools属性的setter和getter方法
	public void setSchools(List<String> schools)
	{
		this.schools = schools;
	}
	public List<String> getSchools()
	{
		return this.schools;
	}
}

<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE hibernate-mapping PUBLIC 
	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
	"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.crazyit.app.domain">
	<class name="Person" table="person_inf">
		<!-- 映射标识属性 -->
		<id name="id" column="person_id">
			<!-- 指定主键生成器策略 -->
			<generator class="identity"/>
		</id>
		<!-- 映射普通属性 -->
		<property name="name" type="string"/>
		<property name="age" type="int"/>
		<!-- 映射List集合属性 -->
		<list name="schools" table="school">
			<!-- 映射集合属性数据表的外键列 -->
			<key column="person_id" not-null="true"/>
			<!-- 映射集合属性数据表的集合索引列 -->
			<list-index column="list_order"/>
			<!-- 映射保存集合元素的数据列 -->
			<element type="string" column="school_name"/>
		</list>
	</class>
</hibernate-mapping>

public class PersonManager
{
	public static void main(String[] args)
	{
		PersonManager mgr = new PersonManager();
		mgr.createAndStorePerson();
		HibernateUtil.sessionFactory.close();
	}
	//创建并保存Person对象
	private void createAndStorePerson()
	{
		//打开线程安全的session对象
		Session session = HibernateUtil.currentSession();
		//打开事务
		Transaction tx = session.beginTransaction();
		//创建Person对象
		Person yeeku = new Person();
		//为Person对象设置属性
		yeeku.setAge(29);
		yeeku.setName("crazyit.org");
		//创建List集合
		List<String> schools = new ArrayList<String>();
		schools.add("小学");
		schools.add("中学");
		//设置List集合属性
		yeeku.setSchools(schools);
		session.save(yeeku);
		tx.commit();
		HibernateUtil.closeSession();
	}
}



Array:

public class Person
{
	//标识属性
	private Integer id;
	//普通属性name
	private String name;
	//普通属性age
	private int age;
	//数组属性,保留该对象关联的学校
	private String[] schools;

	//id属性的setter和getter方法
	public void setId(Integer id)
	{
		this.id = id;
	}
	public Integer getId()
	{
		return this.id;
	}
	
	//name属性的setter和getter方法
	public void setName(String name)
	{
		this.name = name;
	}
	public String getName()
	{
		return this.name;
	}
	
	//age属性的setter和getter方法
	public void setAge(int age)
	{
		this.age = age;
	}
	public int getAge()
	{
		return this.age;
	}
	
	//schools属性的setter和getter方法
	public void setSchools(String[] schools)
	{
		this.schools = schools;
	}
	public String[] getSchools()
	{
		return this.schools;
	}
}

<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE hibernate-mapping PUBLIC 
	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
	"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.crazyit.app.domain">
	<class name="Person" table="person_inf">
		<!-- 映射标识属性 -->
		<id name="id" column="person_id">
			<!-- 指定主键生成器策略 -->
			<generator class="identity"/>
		</id>
		<!-- 映射普通属性 -->
		<property name="name" type="string"/>
		<property name="age" type="int"/>
		<!-- 映射数组属性 -->
		<array name="schools" table="school">
			<!-- 映射数组属性数据表的外键列 -->
			<key column="person_id" not-null="true"/>
			<!-- 映射数组属性数据表的数组索引列 -->
			<list-index column="list_order"/>
			<!-- 映射保存数组元素的数据列 -->
			<element type="string" column="school_name"/>
		</array>
	</class>
</hibernate-mapping>

public class PersonManager
{
	public static void main(String[] args)
	{
		PersonManager mgr = new PersonManager();
		mgr.createAndStorePerson();
		HibernateUtil.sessionFactory.close();
	}
	//创建并保存Person对象
	private void createAndStorePerson()
	{
		//打开线程安全的session对象
		Session session = HibernateUtil.currentSession();
		//打开事务
		Transaction tx = session.beginTransaction();
		//创建Person对象
		Person yeeku = new Person();
		//为Person对象设置属性
		yeeku.setAge(29);
		yeeku.setName("crazyit.org");
		//创建数组对象
		String[] schools = new String[]
			{"小学" , "中学"};
		//设置数组属性
		yeeku.setSchools(schools);
		session.save(yeeku);
		tx.commit();
		HibernateUtil.closeSession();
	}
}



Set:

public class Person
{
	//标识属性
	private Integer id;
	//普通属性name
	private String name;
	//普通属性age
	private int age;
	//集合属性,保留该对象关联的学校
	private Set<String> schools = 
		new HashSet<String>();
	
	//id属性的setter和getter方法
	public void setId(Integer id)
	{
		this.id = id;
	}
	public Integer getId()
	{
		return this.id;
	}
	
	//name属性的setter和getter方法
	public void setName(String name)
	{
		this.name = name;
	}
	public String getName()
	{
		return this.name;
	}
	
	//age属性的setter和getter方法
	public void setAge(int age)
	{
		this.age = age;
	}
	public int getAge()
	{
		return this.age;
	}
	
	//schools属性的setter和getter方法
	public void setSchools(Set<String> schools)
	{
		this.schools = schools;
	}
	public Set<String> getSchools()
	{
		return this.schools;
	}
}

<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE hibernate-mapping PUBLIC 
	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
	"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.crazyit.app.domain">
	<class name="Person" table="person_inf">
		<!-- 映射标识属性 -->
		<id name="id" column="person_id">
			<!-- 指定主键生成器策略 -->
			<generator class="identity"/>
		</id>
		<!-- 映射普通属性 -->
		<property name="name" type="string"/>
		<property name="age" type="int"/>
		<!-- 映射Set集合属性 -->
		<set name="schools" table="school">
			<!-- 映射集合属性数据表的外键列 -->
			<key column="person_id" not-null="true"/>
			<!-- 映射保存集合元素的数据列,增加非空约束 -->
			<element type="string" column="school_name"
				not-null="true"/>
		</set>
	</class>
</hibernate-mapping>

public class PersonManager
{
	public static void main(String[] args)
	{
		PersonManager mgr = new PersonManager();
		mgr.createAndStorePerson();
		HibernateUtil.sessionFactory.close();
	}
	
	private void createAndStorePerson()
	{
		Session session = HibernateUtil.currentSession();
		Transaction tx = session.beginTransaction();
		//创建Person对象
		Person yeeku = new Person();
		//为Person对象设置属性
		yeeku.setAge(29);
		yeeku.setName("crazyit.org");
		//创建Set集合
		Set<String> s = new HashSet<String>();
		s.add("小学");
		s.add("中学");
		//设置Set集合属性
		yeeku.setSchools(s);
		session.save(yeeku);
		tx.commit();
		HibernateUtil.closeSession();
	}
}



bag:

public class Person
{
	//标识属性
	private Integer id;
	//普通属性name
	private String name;
	//普通属性age
	private int age;
	//集合属性,保留该对象关联的学校
	private Collection<String> schools =
		new ArrayList<String>();
	
	//id属性的setter和getter方法
	public void setId(Integer id)
	{
		this.id = id;
	}
	public Integer getId()
	{
		return this.id;
	}
	
	//name属性的setter和getter方法
	public void setName(String name)
	{
		this.name = name;
	}
	public String getName()
	{
		return this.name;
	}
	
	//age属性的setter和getter方法
	public void setAge(int age)
	{
		this.age = age;
	}
	public int getAge()
	{
		return this.age;
	}
	
	//schools属性的setter和getter方法
	public void setSchools(Collection<String> schools)
	{
		this.schools = schools;
	}
	public Collection<String> getSchools()
	{
		return this.schools;
	}
}

<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE hibernate-mapping PUBLIC 
	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
	"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.crazyit.app.domain">
	<class name="Person" table="person_inf">
		<!-- 映射标识属性 -->
		<id name="id" column="person_id">
			<!-- 指定主键生成器策略 -->
			<generator class="identity"/>
		</id>
		<!-- 映射普通属性 -->
		<property name="name" type="string"/>
		<property name="age" type="int"/>
		<!-- 使用bag元素映射集合属性 -->
		<bag name="schools" table="school">
			<!-- 映射集合属性数据表的外键列 -->
			<key column="person_id" not-null="true"/>
			<!-- 映射保存集合元素的数据列 -->
			<element type="string" column="school_name"
				not-null="true"/>
		</bag>
	</class>
</hibernate-mapping>

public class PersonManager
{
	public static void main(String[] args)
	{
		PersonManager mgr = new PersonManager();
		mgr.createAndStorePerson();
		HibernateUtil.sessionFactory.close();
	}
	//创建并保存Person对象
	private void createAndStorePerson()
	{
		//打开线程安全的session对象
		Session session = HibernateUtil.currentSession();
		//打开事务
		Transaction tx = session.beginTransaction();
		//创建Person对象
		Person yeeku = new Person();
		//为Person对象设置属性
		yeeku.setAge(29);
		yeeku.setName("crazyit.org");
		//创建List集合
		Collection<String> schools =
			new ArrayList<String>();
		schools.add("小学");
		schools.add("中学");
		//设置Collection集合属性
		yeeku.setSchools(schools);
		session.save(yeeku);
		tx.commit();
		HibernateUtil.closeSession();
	}   
}



Map:

public class Person
{
	//标识属性
	private Integer id;
	//普通属性name
	private String name;
	//普通属性age
	private int age;
	//集合属性,保留该对象关联的考试成绩
	private Map<String ,Float> scores
		= new HashMap<String ,Float>();

	//id属性的setter和getter方法
	public void setId(Integer id)
	{
		this.id = id;
	}
	public Integer getId()
	{
		return this.id;
	}
	
	//name属性的setter和getter方法
	public void setName(String name)
	{
		this.name = name;
	}
	public String getName()
	{
		return this.name;
	}
	
	//age属性的setter和getter方法
	public void setAge(int age)
	{
		this.age = age;
	}
	public int getAge()
	{
		return this.age;
	}
	
	//scores属性的setter和getter方法
	public void setScores(Map<String ,Float> scores)
	{
		this.scores = scores;
	}
	public Map<String ,Float> getScores()
	{
		return this.scores;
	}
}

<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE hibernate-mapping PUBLIC 
	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
	"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.crazyit.app.domain">
	<class name="Person" table="person_inf">
		<!-- 映射标识属性 -->
		<id name="id" column="person_id">
			<!-- 指定主键生成器策略 -->
			<generator class="identity"/>
		</id>
		<!-- 映射普通属性 -->
		<property name="name" type="string"/>
		<property name="age" type="int"/>
		<!-- 映射Map集合属性 -->
		<map name="scores" table="score">
			<!-- 映射集合属性数据表的外键列 -->
			<key column="person_id" not-null="true"/>
			<!-- 映射集合属性数据表的Map key列 -->
			<map-key column="subject" type="string"/>
			<!-- 映射保存集合元素的数据列 -->
			<element column="grade" type="float"/>
		</map>
	</class>
</hibernate-mapping>

public class PersonManager
{
	public static void main(String[] args)
	{
		PersonManager mgr = new PersonManager();
		mgr.createAndStorePerson();
		HibernateUtil.sessionFactory.close();
	}
	
	private void createAndStorePerson()
	{
		//打开线程安全的session对象
		Session session = HibernateUtil.currentSession();
		//打开事务
		Transaction tx = session.beginTransaction();
		//创建Person对象
		Person yeeku = new Person();
		//为Person对象设置属性
		yeeku.setAge(29);
		yeeku.setName("crazyit.org");
		//创建Map集合
		Map<String ,Float> m = new HashMap<String ,Float>();
		m.put("语文" , 67f);
		m.put("英文" , 45f);
		//设置Map集合属性
		yeeku.setScores(m);
		session.save(yeeku);
		tx.commit();
		HibernateUtil.closeSession();
	}
}


性能分析

对于集合属性,通常推荐使用延迟加载。

集合可分成如下两类:有序(根据key或index访问)、无序(只能遍历元素)。

有序集合都拥有一个由外键列和集合元素索引列组成的联合主键,因此,集合属性的更新或删除是非常高效的——主键已被有效索引。

无序集合的主键由<key.../>和其他元素字段构成,或者根本没有主键。DB很难对复杂主键进行索引,即使可以建立索引,性能也非常差。

在这种情况下,<bag.../>映射是最差的,因为它允许有重复的元素值,也没有索引字段,如果试图修改这种集合属性,Hibernate会先删除全部,再重新创建。

对于多对多关联、值数据的集合而言,有序集合类比Set多一个好处:因为Set集合内部结构的原因,所以如果“改变”Set集合的某个元素,Hibernate不会立即update该元素对应的数据行,因此,只有insert或delete时“改变”才有效。

在Hibernate中,Set应该是最通用的集合类型,这是因为“Set集合”的语义最贴近关系模型的联系关系,因此Hibernate的关联映射都是采用<set.../>元素(或<bag../>元素)映射的。而且,在设计良好的Hibernate领域中,1-N关联的1的一端通常带有inverse="true",对于这种关联映射,1的一端不再控制关联关系,所有更新操作将会在N的一端进行处理,对于这种情况,无须考虑其集合的更新性能。

一旦指定inverse="true"属性,使用<list.../>和<bag.../>映射属性将有较好的性能,因为Collection.add()和Collection.addAll()方法总是返回true。而Set集合需要保证元素不能重复,这就会进行插入前比较。


==============================数据库对象映射==============================


==============================组合属性映射==============================


==============================集合元素为复合类型的映射==============================


==============================复合主键映射==============================


==============================使用JPA Annotation管理映射信息==============================


早期Hibernate使用XML映射文件管理实体类与表之间的映射关系,而JPA规范则推荐使用更简单、易用的Annotation来管理实体类与数据表之间的映射关系,这样就避免了一个实体需要同时维护两份文件(类与xml配置)。

@Entity
@Table(name="person_table")
public class Person
{
	/* 指定使用复合主键类是Name */
	@EmbeddedId
	@AttributeOverrides({
		@AttributeOverride(name="first"
			, column=@Column(name="person_first")),
		@AttributeOverride(name="last"
			, column=@Column(name="person_last" , length=20))
	})
	private Name name;
	//普通属性
	@Column(name="person_email")
	private String email;
	@Embedded
	@AttributeOverrides({
		@AttributeOverride(name="name" 
			, column=@Column(name="cat_name" , length=35)),
		@AttributeOverride(name="color" 
			, column=@Column(name="cat_color"))
	})
	//组件属性,代表此人拥有的宠物
	private Cat pet;

	//name属性的setter和getter方法
	public void setName(Name name)
	{
		this.name = name;
	}
	public Name getName()
	{
		return this.name;
	}
	
	//email属性的setter和getter方法
	public void setEmail(String email)
	{
		this.email = email;
	}
	public String getEmail()
	{
		return this.email;
	}
	
	//pet属性的setter和getter方法
	public void setPet(Cat pet)
	{
		this.pet = pet;
	}
	public Cat getPet()
	{
		return this.pet;
	}
}

@Entity用于标注该类是一个PO,@EmbeddedId用于标注符合类型的标识属性,而@Embedded用于标注一个组件属性,也就是说Person的name属性就是一个复合类型的标识属性;pet属性是一个组件属性。

//修饰组件属性类
@Embeddable
public class Name
	implements java.io.Serializable
{
	private String first;
	private String last;
	//无参数的构造器
	public Name()
	{
	}
	//初始化全部属性的构造器
	public Name(String first , String last)
	{
		this.first = first;
		this.last = last;
	}
	
	//first属性的setter和getter方法
	public void setFirst(String first)
	{
		this.first = first;
	}
	public String getFirst()
	{
		return this.first;
	}
	
	//last属性的setter和getter方法
	public void setLast(String last)
	{
		this.last = last;
	}
	public String getLast()
	{
		return this.last;
	}

	//提供重写的equals方法
	public boolean equals(Object obj)
	{
		if (this == obj)
		{
			return true;
		}
		if (obj.getClass() == Name.class)
		{
			Name target = (Name)obj;
			if (target.getFirst().equals(first)
				&& target.getLast().equals(last))
			{
				return true;
			}
		}
		return false;
	}

	//提供重写的hashCode方法
	public int hashCode()
	{
		return first.hashCode() + last.hashCode() * 17;
	}
}

上面的Name类需要作为标识属性的类型,因此一样需要实现Serializable接口,并重写hashCode和equals方法。

至于Person类所包含的组件属性pet,它所属的Cat类也只要简单地使用@Embeddable修饰即可。

//修饰组件属性类
@Embeddable
public class Cat
{
	private String name;
	private String color;
	
	//无参数的构造器
	public Cat()
	{
	}
	//初始化全部属性的构造器
	public Cat(String name , String color)
	{
		this.name = name;
		this.color = color;
	}
	
	//name属性的setter和getter方法
	public void setName(String name)
	{
		this.name = name;
	}
	public String getName()
	{
		return this.name;
	}
	
	//color属性的setter和getter方法
	public void setColor(String color)
	{
		this.color = color;
	}
	public String getColor()
	{
		return this.color;
	}
}

一旦在实体类中通过上面的Annotation进行标注之后,Hibernate已经能够理解实体类与表之间的映射关系了,也就不再需要.hbm.xml的映射文件了。

<!-- 原有的Person.hbm.xml需作更改 -->
<mapping class="org.crazyit.app.domain.Person"/>

public class PersonManager
{
	public static void main(String[] args)
	{
		PersonManager mgr = new PersonManager();
		mgr.createAndStorePerson();
		HibernateUtil.sessionFactory.close();
	}
	private void createAndStorePerson()
	{
		Session session = HibernateUtil.currentSession();
		Transaction tx = session.beginTransaction();
		//创建Person对象
		Person yeeku = new Person();
		yeeku.setName(new Name("crazyit.org", "疯狂Java联盟"));
		yeeku.setEmail("test@crazyit.org");
		yeeku.setPet(new Cat("Garfield", "黄色"));
		session.save(yeeku);
		tx.commit();
		HibernateUtil.closeSession();
	}
}

public class HibernateUtil
{
	public static final SessionFactory sessionFactory;
	
	static
	{
		try
		{
			//采用默认的hibernate.cfg.xml来启动一个Configuration的实例
			Configuration configuration = new Configuration()
				.configure();
			//由Configuration的实例来创建一个SessionFactory实例
			sessionFactory = configuration.buildSessionFactory();
		}
		catch (Throwable ex)
		{
			System.err.println("Initial SessionFactory creation failed." + ex);
			throw new ExceptionInInitializerError(ex);
		}
	}
	
	//ThreadLocal可以隔离多个线程的数据共享,因此不再需要对线程同步	
	public static final ThreadLocal<Session> session
		= new ThreadLocal<Session>();
	
	public static Session currentSession()
		throws HibernateException
	{
		Session s = session.get();
		//如果该线程还没有Session,则创建一个新的Session
		if (s == null)
		{
			s = sessionFactory.openSession();
			//将获得的Session变量存储在ThreadLocal变量session里
			session.set(s);
		}
		return s;
	}
	
	public static void closeSession()
		throws HibernateException 
	{
		Session s = session.get();
		if (s != null)
			s.close();
		session.set(null);
	}
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值