- 浏览: 568213 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (267)
- 随笔 (4)
- Spring (13)
- Java (61)
- HTTP (3)
- Windows (1)
- CI(Continuous Integration) (3)
- Dozer (1)
- Apache (11)
- DB (7)
- Architecture (41)
- Design Patterns (11)
- Test (5)
- Agile (1)
- ORM (3)
- PMP (2)
- ESB (2)
- Maven (5)
- IDE (1)
- Camel (1)
- Webservice (3)
- MySQL (6)
- CentOS (14)
- Linux (19)
- BI (3)
- RPC (2)
- Cluster (9)
- NoSQL (7)
- Oracle (25)
- Loadbalance (7)
- Web (5)
- tomcat (1)
- freemarker (1)
- 制造 (0)
最新评论
-
panamera:
如果设置了连接需要密码,Dynamic Broker-Clus ...
ActiveMQ 集群配置 -
panamera:
请问你的最后一种模式Broker-C节点是不是应该也要修改持久 ...
ActiveMQ 集群配置 -
maosheng:
longshao_feng 写道楼主使用 文件共享 模式的ma ...
ActiveMQ 集群配置 -
longshao_feng:
楼主使用 文件共享 模式的master-slave,produ ...
ActiveMQ 集群配置 -
tanglanwen:
感触很深,必定谨记!
少走弯路的十条忠告
Hibernate基础代码包括:
1.POJO
POJO 在Hibernate 语义中理解为数据库表所对应的Domain Object。这里的POJO
就是所谓的“Plain Ordinary Java Object”,字面上来讲就是无格式普通Java 对象,简单的可以理解为一个不包含逻辑代码的值对象(Value Object 简称VO)。
一个典型的POJO:
package org.hibernate.sample;
public class TUser implements Serializable {
private String name;
private TGroup group;
private Set addresses;
public User(String name) {
this.name = name;
}
/** default constructor */
public User() {
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public TGroup getGroup() {
return group;
}
public void setGroup(TGroup group) {
this.group = group;
}
public Set getAddresses() {
return addresses;
}
public void setAddresses(Set addresses) {
this.addresses = addresses;
}
}
注意:在编写代码的时候请,对将POJO的getter/setter方法设定为public,如果设定为private,Hibernate将无法对属性的存取进行优化,只能转而采用传统的反射机制进行操作,这将导致大量的性能开销。
2.Hibernate 映射文件
Hibernate 从本质上来讲是一种“对象-关系型数据映射”(Object Relational Mapping 简称ORM)。前面的POJO在这里体现的就是ORM中Object层的语义,而映射(Mapping)文件则是将对象(Object)与关系型数据(Relational)相关联的纽带,在Hibernate中,映射文件通常以“.hbm.xml”作为后缀。
配置文件名默认为“hibernate.cfg.xml”,Hibernate 初始化期间会自动在CLASSPATH 中寻找这个文件,并读取其中的配置信息,为后期数据库操作做好准备。
配置文件应部署在CLASSPATH 中,对于Web 应用而言,配置文件应放置在在\WEB-INF\classes 目录下。
一个典型的hibernate.cfg.xml配置文件如下:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration
PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-2.0.
dtd">
<hibernate-configuration>
<!—- SessionFactory 配置-->
<session-factory>
<!—- 数据库URL -->
<property name="hibernate.connection.url">
jdbc:oracle:thin:@192.168.4.241:1521:orcl
</property>
<!—- 数据库JDBC驱动-->
<property name="hibernate.connection.driver_class">
oracle.jdbc.driver.OracleDriver
</property>
<!—- 数据库用户名-->
<property name="hibernate.connection.username">
User
</property>
<!—- 数据库用户密码-->
<property name="hibernate.connection.password">
Mypass
</property>
<!--dialect ,每个数据库都有其对应的Dialet以匹配其平台特性-->
<property name="dialect">
org.hibernate.dialect.OracleDialect
</property>
<!—- 是否将运行期生成的SQL输出到日志以供调试-->
<property name="hibernate.show_sql">
True
</property>
<!—- 是否使用数据库外连接-->
<property name="hibernate.use_outer_join">
True
</property>
<!—- 事务管理类型,这里我们使用JDBC Transaction -->
<property name="hibernate.transaction.factory_class">
net.sf.hibernate.transaction.JDBCTransactionFactory
</property>
<!—映射文件配置,注意配置文件名必须包含其相对于根的全路径-->
<mapping resource="conf/hibernate/TUser.hbm.xml"/>
<mapping resource="conf/hibernate/TGroup.hbm.xml"/>
</session-factory>
</hibernate-configuration>
TUser.hbm.xml配置文件如下:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration
PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-2.0.
dtd">
<hibernate-mapping>
<class
name="org.hibernate.sample.TUser"
table="t_user"
>
<property
name="name"
type="java.lang.String"
column="name"
not-null="true"
length="50"
>
</class>
</hibernate-mapping>
上面我们已经完成了Hiberante 的基础代码,现在先从一段最简单的代码入手,感受一下Hibernate所提供的强大功能。
下面这段代码是一个JUnit TestCase,演示了TUser 对象的保存和读取。
public class HibernateTest extends TestCase {
Session session = null;
/**
* JUnit中setUp方法在TestCase初始化的时候会自动调用
* 一般用于初始化公用资源
*此例中,用于初始化Hibernate Session
*/
protected void setUp(){
try {
/**
*采用hibernate.properties配置文件的初始化代码:
* Configuration config = new Configuration();
* config.addClass(TUser.class);
*/
//采用hibernate.cfg.xml配置文件
//请注意初始化Configuration时的差异:
// 1.Configuration的初始化方式
// 2.xml文件中已经定义了Mapping文件,因此无需再Hard Coding导入POJO文件的定义
Configuration config = new Configuration().configure();
SessionFactory sessionFactory =
config.buildSessionFactory();
session = sessionFactory.openSession();
} catch (HibernateException e) {
e.printStackTrace();
}
}
/**
*与setUp方法相对应,JUnit TestCase执行完毕时,会自动调用tearDown方法
*一般用于资源释放,此例中,用于关闭在setUp方法中打开的Hibernate Session
*/
protected void tearDown(){
try {
session.close();
} catch (HibernateException e) {
e.printStackTrace();
}
}
/**
* 对象持久化(Insert)测试方法
* JUnit中,以”test”作为前缀的方法为测试方法,将被JUnit自动添加到测试计划中运行
*/
public void testInsert(){
try {
TUser user = new TUser();
user.setName("Emma");
session.save(user);
session.flush();
} catch (HibernateException e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
}
/**
* 对象读取(Select)测试
* 请保证运行之前数据库中已经存在name=’Erica’的记录
*/
public void testSelect(){
String hql=
" from TUser where name='Erica'";
try {
List userList = session.find(hql);
TUser user =(TUser)userList.get(0);
Assert.assertEquals(user.getName(),"Erica");
} catch (HibernateException e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
}
}
可以看到,程序中通过少量代码实现了Java 对象和数据库数据的同步,同时借助Hibernate的有力支持,轻松实现了对象到关系型数据库的映射。
相对传统的JDBC数据访问模式,这样的实现无疑更符合面向对象的思想,同时也大大提高了开发效率。
上面的代码中引入了几个Hibernate基础语义:
1.Configuration
2.SessionFactory
3.Session
Configuration:
正如其名,Configuration 类负责管理Hibernate 的配置信息。Hibernate 运行时需要
获取一些底层实现的基本信息,其中几个关键属性包括:
1.数据库 URL
2.数据库用户
3.数据库用户密码
4.数据库JDBC驱动类
5.数据库 dialect,用于对特定数据库提供支持,其中包含了针对特定数据库特性
的实现,如Hibernate数据类型到特定数据库数据类型的映射等。
使用Hibernate 必须首先提供这些基础信息以完成初始化工作,为后继操作做好准
备。这些属性在hibernate配置文件(hibernate.cfg.xml)中加以设定。
当我们调用:Configuration config = new Configuration().configure();
时,Hibernate会自动在当前的CLASSPATH 中搜寻hibernate.cfg.xml文件并将其读取到内存中作为后继操作的基础配置。Configuration 类一般只有在获取SessionFactory
时需要涉及,当获取SessionFactory 之后,由于配置信息已经由Hibernate 维护并绑定
在返回的SessionFactory之上,因此一般情况下无需再对其进行操作。
我们也可以指定配置文件名,如果不希望使用默认的hibernate.cfg.xml文件作为配
置文件的话:
File file = new File("c:\\sample\\myhibernate.xml");
Configuration config = new Configuration().configure(file);
SessionFactory:
SessionFactory 负责创建Session 实例。我们可以通过Configuation 实例构建
SessionFactory,Configuration config = new Configuration().configure();
SessionFactory sessionFactory = config.buildSessionFactory();
Configuration实例config会根据当前的配置信息,构造SessionFactory实例并返回。SessionFactory 一旦构造完毕,即被赋予特定的配置信息。也就是说,之后config 的任何变更将不会影响到已经创建的SessionFactory 实例(sessionFactory)。如果需要使用基于改动后的config 实例的SessionFactory,需要从config 重新构建一个SessionFactory实例。
Session:
Session是持久层操作的基础,相当于JDBC中的Connection。
Session实例通过SessionFactory实例构建:
Configuration config = new Configuration().configure();
SessionFactory sessionFactory = config.buildSessionFactory();
Session session = sessionFactory.openSession();
之后我们就可以调用Session所提供的save、find、flush等方法完成持久层操作:
Find:
String hql= " from TUser where name='Erica'";
List userList = session.find(hql);
Save:
TUser user = new TUser();
user.setName("Emma");
session.save(user);
session.flush();
最后调用Session.flush方法强制数据库同步,这里即强制Hibernate将user实例立即同步到数据库中。如果在事务中则不需要flush方法,在事务提交的时候,hibernate自动会执行flush方法,另外当Session关闭时,也会自动执行flush方法。
Hibernate数据关联:
一对一关联:
Hibernate中的一对一关联由“one-to-one”节点定义,每个用户对应一个组,这在我们的系统中反映为TUser 到TGroup 的one-to-one 关系。其中TUser 是主控方,TGroup是被动方。
one-to-one关系定义比较简单,只需在主控方加以定义。这里,我们的目标是由TUser 对象获取其对应的TGroup 对象。因此TUser 对象是主控方,为了实现一对一关系,我们在TUser 对象的映射文件TUser.hbm.xml 中加入one-to-one节点,对TGroup对象进行一对一关联:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration
PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-2.0.
dtd">
<hibernate-mapping>
<class
name="org.hibernate.sample.TUser"
table="t_user"
dynamic-update="true"
dynamic-insert="true"
>
……
<one-to-one
name="group"
class="org.hibernate.sample.TGroup"
cascade="none"
outer-join="auto"
constrained="false"
/>
……
</class>
</hibernate-mapping>
一对多关联:
一对多关系在系统实现中也很常见。典型的例子就是父亲与孩子的关系。这个示例中,每个用户(TUser)都关联到多个地址(TAddress),如一个用户可能拥有办公室地址、家庭地址等多个地址属性。这样,在系统中,就反应为一个“一对多”关联。
一对多关系分为单向一对多关系和双向一对多关系。
单向一对多关系只需在“一”方进行配置,双向一对多关系需要在关联双方均加以配置。
单向一对多关系:
对于主控方(TUser),TUser.hbm.xml配置文件:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration
PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-2.0.
dtd">
<hibernate-mapping>
<class
name="org.hibernate.sample.TUser"
table="t_user"
dynamic-update="true"
dynamic-insert="true"
>
……
<set
name="addresses"
table="t_address"
lazy="false"
<!--inverse=false的一方(主控方)负责维护关联关系。-->
inverse="false"
cascade="all"
sort="unsorted"
order-by="zipcode asc"
>
<key
column="user_id"
>
</key>
<one-to-many
class="org.hibernate.sample.TAddress"
/>
</set>
……
</class>
</hibernate-mapping>
通过单向一对多关系进行关联相对简单,但是存在一个问题。由于是单向关联,为了保持关联关系,我们只能通过主控方对被动方进行级联更新。且如果被关联方的关联字段为“NOT NULL”,当Hibernate创建或者更新关联关系时,还可能出现约束违例。
例如我们想为一个已有的用户“Erica”添加一个地址对象:
......
Transaction tx = session.beginTransaction();
TAddress addr = new TAddress();
addr.setTel("1123");
addr.setZipcode("233123");
addr.setAddress("Hongkong");
user.getAddresses().add(addr);
session.save(user);//通过主控对象级联更新
tx.commit();
......
为了完成这个操作,Hibernate会分两步(两条SQL)来完成新增t_address记录的操作:
1. save(user)时:
insert into t_address (user_id, address, zipcode, tel)
values (null, "Hongkong", "233123", "1123")
2. tx.commit()时
update t_address set user_id=”1”, address="Hongkong",
zipcode="233123", tel="1123" where id=2
第一条SQL用于插入新的地址记录。
第二条SQL用于更新t_address,将user_id设置为其关联的user对象的id值。
问题就出在这里,数据库中,我们的t_address.user_id字段为“NOT NULL”型,当Hibernate执行第一条语句创建t_address记录时,试图将user_id字段的值设为null,于是引发了一个约束违例异常。
因为关联方向是单向,关联关系由TUser对象维持,而被关联的addr对象本身并不知道自己与哪个TUser对象相关联,也就是说,addr对象本身并不知道user_id应该设为什么数值。因此,在保存addr时,只能先在关联字段插入一个空值。之后,再由TUser对象将自身的id值赋予关联字段addr.user_id,这个赋值操作导致addr对象属性发生变动,在事务提交时,hibernate会发现这一改变,并通过update sql将变动后的数据保存到数据库。第一个步骤中,企图向数据库的非空字段插入空值,因此导致了约束违例。
由于Hibernate实现机制中,采用了两条SQL进行一次数据插入操作,相对单条insert,几乎是两倍的性能开销,效率较低,因此,对于性能敏感的系统而言,这样的解决方案所带来的开销可能难以承受。
双向一对多关系的出现则解决了这个问题。它除了避免约束违例和提高性能的好处之外,还带来另外一个优点,由于建立了双向关联,我们可以在关联双方中任意一方访问关联的另一方。
双向一对多关系:
双向一对多关系,实际上是“单向一对多关系”与“多对一关系”的组合。也就是说我们必须在主控方配置单向一对多关系的基础上,在被控方配置多对一关系与其对应。
对于被控方(TUser),TUser.hbm.xml配置文件:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration
PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-2.0.
dtd">
<hibernate-mapping>
<class
name="org.hibernate.sample.TUser"
table="t_user"
dynamic-update="true"
dynamic-insert="true"
>
……
<set
name="addresses"
table="t_address"
lazy="false"
inverse="true"
cascade="all"
sort="unsorted"
order-by="zipcode asc"
>
<key
column="user_id"
>
</key>
<one-to-many
class="org.hibernate.sample.TAddress"
/>
</set>
</class>
</hibernate-mapping>
注意:,inverse被设为“true”,这意味着TUser不再作为主控方,而是将关联关系的维护工作交给关联对象org.hibernate.sample.TAddress 来完成。这样TAddress对象在持久化过程中,就可以主动获取其关联的TUser对象的id,并将其作为自己的user_id,之后执行一次insert操作即可完成全部工作。
在 one-to-many 关系中,将many 一方设为主动方(inverse=false)将有助性能的改善。
对于主控方(TAddress),TAddress.hbm.xml配置文件:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration
PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-2.0.
dtd">
<hibernate-mapping>
<class
name="org.hibernate.sample.TAddress"
table="t_address"
dynamic-update="false"
dynamic-insert="false"
>
……
<many-to-one
name="user"
class="org.hibernate.sample.TUser"
cascade="none"
outer-join="auto"
update="true"
insert="true"
access="property"
column="user_id"
not-null="true"
/>
</class>
</hibernate-mapping>
注意:在 TAddress 对象中新增一个TUser field “user”,并为其添加对应的getter/setter 方法。同时删除原有的user_id 属性及其映射配置,否则运行期会报字段重复映射错误。
......
Transaction tx = session.beginTransaction();
TAddress addr = new TAddress();
addr.setTel("1123");
addr.setZipcode("233123");
addr.setAddress("Hongkong");
addr.setUser(user);//设置关联的TUser对象
user.getAddresses().add(addr);
session.save(user);//级联更新
tx.commit();
......
观察Hibernate执行过程中调用的SQL语句:
insert into t_address (user_id, address, zipcode, tel) values
(1, 'Hongkong', '233123', '1123')
正如我们所期望的,保存工作通过单条Insert语句的执行来完成。
多对多关联:
Hibernate关联关系中相对比较特殊的就是多对多关联,多对多关联与一对一关联和一对多关联不同,多对多关联需要另外一张映射表用于保存多对多映射信息。
由于多对多关联的性能不佳(由于引入了中间表,一次读取操作需要反复数次查询),因此在设计中应该避免大量使用。同时,在对多对关系中,应根据情况,采取延迟加载(Lazy Loading 参见后续章节)机制来避免无谓的性能开销。
这里我们以Group和Role之间的映射为例:
TGroup.hbm.xml中关于多对多关联的配置片断:
<hibernate-mapping>
<class
name="org.hibernate.sample.TGroup"
table="t_group"
dynamic-update="false"
dynamic-insert="false"
>
……
<set
name="roles"
table="t_group_role" ①
lazy="false"
inverse="false"
cascade="save-update" ②
>
<key
column="group_id" ③
>
</key>
<many-to-many
class="org.hibernate.sample.TRole"
column="role_id" ④
/>
</set>
</class>
</hibernate-mapping>
注意:
① 这里为t_group 和t_role之间的映射表。
② 一般情况下,cascade应该设置为“save-update”,对于多对多逻辑而言,很少出现删除一方需要级联删除所有关联数据的情况,如删除一个Group,一般不会删除其中包含的Role(这些Role 可能还被其他的Group所引用)。反之删除Role一般也不会删除其所关联的所有Group。
③ 映射表中对于t_group表记录的标识字段。
④ 映射表中对于t_role表记录的标识字段。
TRole.hbm.xml中关于多对多关联的配置片断:
<hibernate-mapping>
<class
name="org.hibernate.sample.TRole"
table="t_role"
dynamic-update="false"
dynamic-insert="false"
>
……
<set
name="groups"
table="t_group_role"
lazy="false"
inverse="true"
cascade="save-update"
sort="unsorted"
>
<key
column="role_id"
>
</key>
<many-to-many
class="org.hibernate.sample.TGroup"
column="group_id"
outer-join="auto"
/>
</set>
</class>
</hibernate-mapping>
多对多关系中,由于关联关系是两张表相互引用,因此在保存关联状态时必须对双方同时保存。
public void testPersist(){
TRole role1 = new TRole();
role1.setName("Role1");
TRole role2 = new TRole();
role2.setName("Role2");
TRole role3 = new TRole();
role3.setName("Role3");
TGroup group1 = new TGroup();
group1.setName("group1");
TGroup group2 = new TGroup();
group2.setName("group2");
TGroup group3 = new TGroup();
group3.setName("group3");
group1.getRoles().add(role1);
group1.getRoles().add(role2);
group2.getRoles().add(role2);
group2.getRoles().add(role3);
group3.getRoles().add(role1);
group3.getRoles().add(role3);
role1.getGroups().add(group1);
role1.getGroups().add(group3);
role2.getGroups().add(group1);
role2.getGroups().add(group2);
role3.getGroups().add(group2);
role3.getGroups().add(group3);
try {
Transaction tx = session.beginTransaction();
//多对多关系必须同时对关联双方进行保存
session.save(role1);
session.save(role2);
session.save(role3);
session.save(group1);
session.save(group2);
session.save(group3);
tx.commit();
} catch (Exception e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
}
上面的代码创建3个TGroup对象和3个TRole对象,并形成了多对多关系。
1.POJO
POJO 在Hibernate 语义中理解为数据库表所对应的Domain Object。这里的POJO
就是所谓的“Plain Ordinary Java Object”,字面上来讲就是无格式普通Java 对象,简单的可以理解为一个不包含逻辑代码的值对象(Value Object 简称VO)。
一个典型的POJO:
package org.hibernate.sample;
public class TUser implements Serializable {
private String name;
private TGroup group;
private Set addresses;
public User(String name) {
this.name = name;
}
/** default constructor */
public User() {
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public TGroup getGroup() {
return group;
}
public void setGroup(TGroup group) {
this.group = group;
}
public Set getAddresses() {
return addresses;
}
public void setAddresses(Set addresses) {
this.addresses = addresses;
}
}
注意:在编写代码的时候请,对将POJO的getter/setter方法设定为public,如果设定为private,Hibernate将无法对属性的存取进行优化,只能转而采用传统的反射机制进行操作,这将导致大量的性能开销。
2.Hibernate 映射文件
Hibernate 从本质上来讲是一种“对象-关系型数据映射”(Object Relational Mapping 简称ORM)。前面的POJO在这里体现的就是ORM中Object层的语义,而映射(Mapping)文件则是将对象(Object)与关系型数据(Relational)相关联的纽带,在Hibernate中,映射文件通常以“.hbm.xml”作为后缀。
配置文件名默认为“hibernate.cfg.xml”,Hibernate 初始化期间会自动在CLASSPATH 中寻找这个文件,并读取其中的配置信息,为后期数据库操作做好准备。
配置文件应部署在CLASSPATH 中,对于Web 应用而言,配置文件应放置在在\WEB-INF\classes 目录下。
一个典型的hibernate.cfg.xml配置文件如下:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration
PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-2.0.
dtd">
<hibernate-configuration>
<!—- SessionFactory 配置-->
<session-factory>
<!—- 数据库URL -->
<property name="hibernate.connection.url">
jdbc:oracle:thin:@192.168.4.241:1521:orcl
</property>
<!—- 数据库JDBC驱动-->
<property name="hibernate.connection.driver_class">
oracle.jdbc.driver.OracleDriver
</property>
<!—- 数据库用户名-->
<property name="hibernate.connection.username">
User
</property>
<!—- 数据库用户密码-->
<property name="hibernate.connection.password">
Mypass
</property>
<!--dialect ,每个数据库都有其对应的Dialet以匹配其平台特性-->
<property name="dialect">
org.hibernate.dialect.OracleDialect
</property>
<!—- 是否将运行期生成的SQL输出到日志以供调试-->
<property name="hibernate.show_sql">
True
</property>
<!—- 是否使用数据库外连接-->
<property name="hibernate.use_outer_join">
True
</property>
<!—- 事务管理类型,这里我们使用JDBC Transaction -->
<property name="hibernate.transaction.factory_class">
net.sf.hibernate.transaction.JDBCTransactionFactory
</property>
<!—映射文件配置,注意配置文件名必须包含其相对于根的全路径-->
<mapping resource="conf/hibernate/TUser.hbm.xml"/>
<mapping resource="conf/hibernate/TGroup.hbm.xml"/>
</session-factory>
</hibernate-configuration>
TUser.hbm.xml配置文件如下:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration
PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-2.0.
dtd">
<hibernate-mapping>
<class
name="org.hibernate.sample.TUser"
table="t_user"
>
<property
name="name"
type="java.lang.String"
column="name"
not-null="true"
length="50"
>
</class>
</hibernate-mapping>
上面我们已经完成了Hiberante 的基础代码,现在先从一段最简单的代码入手,感受一下Hibernate所提供的强大功能。
下面这段代码是一个JUnit TestCase,演示了TUser 对象的保存和读取。
public class HibernateTest extends TestCase {
Session session = null;
/**
* JUnit中setUp方法在TestCase初始化的时候会自动调用
* 一般用于初始化公用资源
*此例中,用于初始化Hibernate Session
*/
protected void setUp(){
try {
/**
*采用hibernate.properties配置文件的初始化代码:
* Configuration config = new Configuration();
* config.addClass(TUser.class);
*/
//采用hibernate.cfg.xml配置文件
//请注意初始化Configuration时的差异:
// 1.Configuration的初始化方式
// 2.xml文件中已经定义了Mapping文件,因此无需再Hard Coding导入POJO文件的定义
Configuration config = new Configuration().configure();
SessionFactory sessionFactory =
config.buildSessionFactory();
session = sessionFactory.openSession();
} catch (HibernateException e) {
e.printStackTrace();
}
}
/**
*与setUp方法相对应,JUnit TestCase执行完毕时,会自动调用tearDown方法
*一般用于资源释放,此例中,用于关闭在setUp方法中打开的Hibernate Session
*/
protected void tearDown(){
try {
session.close();
} catch (HibernateException e) {
e.printStackTrace();
}
}
/**
* 对象持久化(Insert)测试方法
* JUnit中,以”test”作为前缀的方法为测试方法,将被JUnit自动添加到测试计划中运行
*/
public void testInsert(){
try {
TUser user = new TUser();
user.setName("Emma");
session.save(user);
session.flush();
} catch (HibernateException e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
}
/**
* 对象读取(Select)测试
* 请保证运行之前数据库中已经存在name=’Erica’的记录
*/
public void testSelect(){
String hql=
" from TUser where name='Erica'";
try {
List userList = session.find(hql);
TUser user =(TUser)userList.get(0);
Assert.assertEquals(user.getName(),"Erica");
} catch (HibernateException e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
}
}
可以看到,程序中通过少量代码实现了Java 对象和数据库数据的同步,同时借助Hibernate的有力支持,轻松实现了对象到关系型数据库的映射。
相对传统的JDBC数据访问模式,这样的实现无疑更符合面向对象的思想,同时也大大提高了开发效率。
上面的代码中引入了几个Hibernate基础语义:
1.Configuration
2.SessionFactory
3.Session
Configuration:
正如其名,Configuration 类负责管理Hibernate 的配置信息。Hibernate 运行时需要
获取一些底层实现的基本信息,其中几个关键属性包括:
1.数据库 URL
2.数据库用户
3.数据库用户密码
4.数据库JDBC驱动类
5.数据库 dialect,用于对特定数据库提供支持,其中包含了针对特定数据库特性
的实现,如Hibernate数据类型到特定数据库数据类型的映射等。
使用Hibernate 必须首先提供这些基础信息以完成初始化工作,为后继操作做好准
备。这些属性在hibernate配置文件(hibernate.cfg.xml)中加以设定。
当我们调用:Configuration config = new Configuration().configure();
时,Hibernate会自动在当前的CLASSPATH 中搜寻hibernate.cfg.xml文件并将其读取到内存中作为后继操作的基础配置。Configuration 类一般只有在获取SessionFactory
时需要涉及,当获取SessionFactory 之后,由于配置信息已经由Hibernate 维护并绑定
在返回的SessionFactory之上,因此一般情况下无需再对其进行操作。
我们也可以指定配置文件名,如果不希望使用默认的hibernate.cfg.xml文件作为配
置文件的话:
File file = new File("c:\\sample\\myhibernate.xml");
Configuration config = new Configuration().configure(file);
SessionFactory:
SessionFactory 负责创建Session 实例。我们可以通过Configuation 实例构建
SessionFactory,Configuration config = new Configuration().configure();
SessionFactory sessionFactory = config.buildSessionFactory();
Configuration实例config会根据当前的配置信息,构造SessionFactory实例并返回。SessionFactory 一旦构造完毕,即被赋予特定的配置信息。也就是说,之后config 的任何变更将不会影响到已经创建的SessionFactory 实例(sessionFactory)。如果需要使用基于改动后的config 实例的SessionFactory,需要从config 重新构建一个SessionFactory实例。
Session:
Session是持久层操作的基础,相当于JDBC中的Connection。
Session实例通过SessionFactory实例构建:
Configuration config = new Configuration().configure();
SessionFactory sessionFactory = config.buildSessionFactory();
Session session = sessionFactory.openSession();
之后我们就可以调用Session所提供的save、find、flush等方法完成持久层操作:
Find:
String hql= " from TUser where name='Erica'";
List userList = session.find(hql);
Save:
TUser user = new TUser();
user.setName("Emma");
session.save(user);
session.flush();
最后调用Session.flush方法强制数据库同步,这里即强制Hibernate将user实例立即同步到数据库中。如果在事务中则不需要flush方法,在事务提交的时候,hibernate自动会执行flush方法,另外当Session关闭时,也会自动执行flush方法。
Hibernate数据关联:
一对一关联:
Hibernate中的一对一关联由“one-to-one”节点定义,每个用户对应一个组,这在我们的系统中反映为TUser 到TGroup 的one-to-one 关系。其中TUser 是主控方,TGroup是被动方。
one-to-one关系定义比较简单,只需在主控方加以定义。这里,我们的目标是由TUser 对象获取其对应的TGroup 对象。因此TUser 对象是主控方,为了实现一对一关系,我们在TUser 对象的映射文件TUser.hbm.xml 中加入one-to-one节点,对TGroup对象进行一对一关联:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration
PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-2.0.
dtd">
<hibernate-mapping>
<class
name="org.hibernate.sample.TUser"
table="t_user"
dynamic-update="true"
dynamic-insert="true"
>
……
<one-to-one
name="group"
class="org.hibernate.sample.TGroup"
cascade="none"
outer-join="auto"
constrained="false"
/>
……
</class>
</hibernate-mapping>
一对多关联:
一对多关系在系统实现中也很常见。典型的例子就是父亲与孩子的关系。这个示例中,每个用户(TUser)都关联到多个地址(TAddress),如一个用户可能拥有办公室地址、家庭地址等多个地址属性。这样,在系统中,就反应为一个“一对多”关联。
一对多关系分为单向一对多关系和双向一对多关系。
单向一对多关系只需在“一”方进行配置,双向一对多关系需要在关联双方均加以配置。
单向一对多关系:
对于主控方(TUser),TUser.hbm.xml配置文件:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration
PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-2.0.
dtd">
<hibernate-mapping>
<class
name="org.hibernate.sample.TUser"
table="t_user"
dynamic-update="true"
dynamic-insert="true"
>
……
<set
name="addresses"
table="t_address"
lazy="false"
<!--inverse=false的一方(主控方)负责维护关联关系。-->
inverse="false"
cascade="all"
sort="unsorted"
order-by="zipcode asc"
>
<key
column="user_id"
>
</key>
<one-to-many
class="org.hibernate.sample.TAddress"
/>
</set>
……
</class>
</hibernate-mapping>
通过单向一对多关系进行关联相对简单,但是存在一个问题。由于是单向关联,为了保持关联关系,我们只能通过主控方对被动方进行级联更新。且如果被关联方的关联字段为“NOT NULL”,当Hibernate创建或者更新关联关系时,还可能出现约束违例。
例如我们想为一个已有的用户“Erica”添加一个地址对象:
......
Transaction tx = session.beginTransaction();
TAddress addr = new TAddress();
addr.setTel("1123");
addr.setZipcode("233123");
addr.setAddress("Hongkong");
user.getAddresses().add(addr);
session.save(user);//通过主控对象级联更新
tx.commit();
......
为了完成这个操作,Hibernate会分两步(两条SQL)来完成新增t_address记录的操作:
1. save(user)时:
insert into t_address (user_id, address, zipcode, tel)
values (null, "Hongkong", "233123", "1123")
2. tx.commit()时
update t_address set user_id=”1”, address="Hongkong",
zipcode="233123", tel="1123" where id=2
第一条SQL用于插入新的地址记录。
第二条SQL用于更新t_address,将user_id设置为其关联的user对象的id值。
问题就出在这里,数据库中,我们的t_address.user_id字段为“NOT NULL”型,当Hibernate执行第一条语句创建t_address记录时,试图将user_id字段的值设为null,于是引发了一个约束违例异常。
因为关联方向是单向,关联关系由TUser对象维持,而被关联的addr对象本身并不知道自己与哪个TUser对象相关联,也就是说,addr对象本身并不知道user_id应该设为什么数值。因此,在保存addr时,只能先在关联字段插入一个空值。之后,再由TUser对象将自身的id值赋予关联字段addr.user_id,这个赋值操作导致addr对象属性发生变动,在事务提交时,hibernate会发现这一改变,并通过update sql将变动后的数据保存到数据库。第一个步骤中,企图向数据库的非空字段插入空值,因此导致了约束违例。
由于Hibernate实现机制中,采用了两条SQL进行一次数据插入操作,相对单条insert,几乎是两倍的性能开销,效率较低,因此,对于性能敏感的系统而言,这样的解决方案所带来的开销可能难以承受。
双向一对多关系的出现则解决了这个问题。它除了避免约束违例和提高性能的好处之外,还带来另外一个优点,由于建立了双向关联,我们可以在关联双方中任意一方访问关联的另一方。
双向一对多关系:
双向一对多关系,实际上是“单向一对多关系”与“多对一关系”的组合。也就是说我们必须在主控方配置单向一对多关系的基础上,在被控方配置多对一关系与其对应。
对于被控方(TUser),TUser.hbm.xml配置文件:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration
PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-2.0.
dtd">
<hibernate-mapping>
<class
name="org.hibernate.sample.TUser"
table="t_user"
dynamic-update="true"
dynamic-insert="true"
>
……
<set
name="addresses"
table="t_address"
lazy="false"
inverse="true"
cascade="all"
sort="unsorted"
order-by="zipcode asc"
>
<key
column="user_id"
>
</key>
<one-to-many
class="org.hibernate.sample.TAddress"
/>
</set>
</class>
</hibernate-mapping>
注意:,inverse被设为“true”,这意味着TUser不再作为主控方,而是将关联关系的维护工作交给关联对象org.hibernate.sample.TAddress 来完成。这样TAddress对象在持久化过程中,就可以主动获取其关联的TUser对象的id,并将其作为自己的user_id,之后执行一次insert操作即可完成全部工作。
在 one-to-many 关系中,将many 一方设为主动方(inverse=false)将有助性能的改善。
对于主控方(TAddress),TAddress.hbm.xml配置文件:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration
PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-2.0.
dtd">
<hibernate-mapping>
<class
name="org.hibernate.sample.TAddress"
table="t_address"
dynamic-update="false"
dynamic-insert="false"
>
……
<many-to-one
name="user"
class="org.hibernate.sample.TUser"
cascade="none"
outer-join="auto"
update="true"
insert="true"
access="property"
column="user_id"
not-null="true"
/>
</class>
</hibernate-mapping>
注意:在 TAddress 对象中新增一个TUser field “user”,并为其添加对应的getter/setter 方法。同时删除原有的user_id 属性及其映射配置,否则运行期会报字段重复映射错误。
......
Transaction tx = session.beginTransaction();
TAddress addr = new TAddress();
addr.setTel("1123");
addr.setZipcode("233123");
addr.setAddress("Hongkong");
addr.setUser(user);//设置关联的TUser对象
user.getAddresses().add(addr);
session.save(user);//级联更新
tx.commit();
......
观察Hibernate执行过程中调用的SQL语句:
insert into t_address (user_id, address, zipcode, tel) values
(1, 'Hongkong', '233123', '1123')
正如我们所期望的,保存工作通过单条Insert语句的执行来完成。
多对多关联:
Hibernate关联关系中相对比较特殊的就是多对多关联,多对多关联与一对一关联和一对多关联不同,多对多关联需要另外一张映射表用于保存多对多映射信息。
由于多对多关联的性能不佳(由于引入了中间表,一次读取操作需要反复数次查询),因此在设计中应该避免大量使用。同时,在对多对关系中,应根据情况,采取延迟加载(Lazy Loading 参见后续章节)机制来避免无谓的性能开销。
这里我们以Group和Role之间的映射为例:
TGroup.hbm.xml中关于多对多关联的配置片断:
<hibernate-mapping>
<class
name="org.hibernate.sample.TGroup"
table="t_group"
dynamic-update="false"
dynamic-insert="false"
>
……
<set
name="roles"
table="t_group_role" ①
lazy="false"
inverse="false"
cascade="save-update" ②
>
<key
column="group_id" ③
>
</key>
<many-to-many
class="org.hibernate.sample.TRole"
column="role_id" ④
/>
</set>
</class>
</hibernate-mapping>
注意:
① 这里为t_group 和t_role之间的映射表。
② 一般情况下,cascade应该设置为“save-update”,对于多对多逻辑而言,很少出现删除一方需要级联删除所有关联数据的情况,如删除一个Group,一般不会删除其中包含的Role(这些Role 可能还被其他的Group所引用)。反之删除Role一般也不会删除其所关联的所有Group。
③ 映射表中对于t_group表记录的标识字段。
④ 映射表中对于t_role表记录的标识字段。
TRole.hbm.xml中关于多对多关联的配置片断:
<hibernate-mapping>
<class
name="org.hibernate.sample.TRole"
table="t_role"
dynamic-update="false"
dynamic-insert="false"
>
……
<set
name="groups"
table="t_group_role"
lazy="false"
inverse="true"
cascade="save-update"
sort="unsorted"
>
<key
column="role_id"
>
</key>
<many-to-many
class="org.hibernate.sample.TGroup"
column="group_id"
outer-join="auto"
/>
</set>
</class>
</hibernate-mapping>
多对多关系中,由于关联关系是两张表相互引用,因此在保存关联状态时必须对双方同时保存。
public void testPersist(){
TRole role1 = new TRole();
role1.setName("Role1");
TRole role2 = new TRole();
role2.setName("Role2");
TRole role3 = new TRole();
role3.setName("Role3");
TGroup group1 = new TGroup();
group1.setName("group1");
TGroup group2 = new TGroup();
group2.setName("group2");
TGroup group3 = new TGroup();
group3.setName("group3");
group1.getRoles().add(role1);
group1.getRoles().add(role2);
group2.getRoles().add(role2);
group2.getRoles().add(role3);
group3.getRoles().add(role1);
group3.getRoles().add(role3);
role1.getGroups().add(group1);
role1.getGroups().add(group3);
role2.getGroups().add(group1);
role2.getGroups().add(group2);
role3.getGroups().add(group2);
role3.getGroups().add(group3);
try {
Transaction tx = session.beginTransaction();
//多对多关系必须同时对关联双方进行保存
session.save(role1);
session.save(role2);
session.save(role3);
session.save(group1);
session.save(group2);
session.save(group3);
tx.commit();
} catch (Exception e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
}
上面的代码创建3个TGroup对象和3个TRole对象,并形成了多对多关系。
相关推荐
* 使用Hibernate框架对数据库进行操作和映射 * 使用Spring框架提供基本的应用程序开发功能和事务管理 JavaWeb开发三大框架整理归纳是指Struts、Hibernate和Spring三个框架的整合和比较。三个框架的整合可以提供一个...
测试过程中,若遇到问题,可参照作者归纳的总结和不能运行的原因来排查和修复。 总之,"Spring+Hibernate+MySql的应用实例"是一个典型的Java Web开发示例,涵盖了从数据持久化到业务逻辑处理再到Web展示的完整流程...
在Hibernate中,这种状态的对象不会受到框架的管理,任何对它的修改都不会自动保存到数据库。 2. **Persistent(持久状态)**:当对象被Hibernate管理并与其数据库记录相关联时,它就进入了Persistent状态。这通常...
Struts2框架具有良好的第三方整合性,能与其他Spring和Hibernate框架无缝集成,形成一个较为完整的Java Web应用解决方案。 Spring框架是一个全面的轻量级解决方案,主要目标是解决企业应用开发的复杂性。Spring的...
SSH框架,即Struts、Spring和Hibernate三大开源框架的组合,是Java Web开发中常用的一种架构模式。它提供了强大的MVC(Model-View-Controller)功能,以及对业务逻辑和持久层的良好管理。下面将详细阐述SSH框架搭建...
SSH框架,全称为Struts2、Spring和Hibernate的组合,是Java Web开发中常见的三大开源框架。本章节将详细介绍如何在MyEclipse环境中搭建一个基于SSH的项目工程,确保开发人员能够在统一的环境中进行协作。 首先,...
Spring框架是Java开发中广泛使用的轻量级框架,它提供了许多功能,如依赖注入(DI)、面向切面编程(AOP)、事务管理等。下面将详细阐述这些知识点。 1. **AOP(面向切面编程)** - AOP是一种编程范式,用于将关注...
根据提供的文档信息,我们可以归纳出该文档主要围绕Hibernate框架展开,并详细介绍了持久层的概念及其相关技术。接下来,我将从标题、描述、标签以及部分内容中提取关键知识点,并对其进行详细阐述。 ### Hibernate...
SSH框架整合搭建是Java开发中常见的一种技术组合,它包括Struts、Spring和Hibernate三个主要组件。这个过程涉及多个步骤,下面将详细解释每个部分。 1. **Struts部分** - **添加Struts功能支持**:这一步通常在IDE...
这四大框架在 Java EE 开发中扮演着重要角色,本文将对这四大框架进行系统的归纳和介绍。 Struts 框架 Struts 是一个基于 Sun Java EE 平台的 MVC 框架,主要是采用 Servlet 和 JSP 技术来实现的。Struts 框架可...
随着Java开发领域对Hibernate框架的广泛应用,Eclipse作为流行的集成开发环境(IDE)也开始支持各种与Hibernate相关的插件,以帮助开发者更高效地进行工作。本文将聚焦于其中一个插件——Hibernate Synchronizer,...
根据提供的文档信息,我们可以归纳总结出以下几个核心知识点: ### 1. 创建数据库 - **目的**:为Spring+Struts+Hibernate(SSH)框架的应用提供数据存储基础。 - **步骤**: - 设计数据库表结构,确保符合业务...
根据所提供的文件信息,我们可以归纳出一系列与Java相关的框架技术及其访问网址,这对于初学者或是有经验的开发者来说都是宝贵的学习资源。下面,我们将深入探讨这些框架的技术特点和应用场景。 ### 1. JavaMail ...
根据提供的文件信息,我们可以归纳出以下几个关键的知识点: ### 1....这些知识点对于理解和掌握 Hibernate 框架的基本使用至关重要,同时也是开发基于 Hibernate 的应用程序时不可或缺的基础技能。
这些知识点不仅是Struts 2和Hibernate框架的基本要素,也是Java Web开发者必备的专业技能。理解并熟练运用这些技术,能够帮助开发者更高效地构建和维护企业级应用。对于开发者来说,熟悉这些概念并能灵活运用,可以...
#### 四、Hibernate框架 1. **定义**:Hibernate是一个开放源码的对象关系映射(ORM)框架,用于将Java对象映射到数据库表及其字段上。 2. **特点**: - **持久化**:提供对象的持久化服务。 - **查询语言**:...
#### Hibernate框架知识点 - 在SSH整合中,Hibernate主要负责对象与数据库之间的映射关系处理,通过配置文件`hibernate.cfg.xml`来实现。 - **配置文件**:`hibernate.cfg.xml`是Hibernate的核心配置文件,用于设置...
- **内部资料**:此次分享的是针对Hibernate框架的培训资料,主要面向希望通过深化Hibernate技能来提升自己的开发者。 #### 1.2 Hibernate简介 - **Hibernate**是一个开放源代码的**对象关系映射(ORM)**解决方案...