目前主流数据库是关系型数据库(RDB),而Java则是面向对象的编程语言(OOPL),要结合两者相当麻烦。
ORM是为了解决这一问题而产生的概念。
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);
}
}