hibernate中有两种主要的配置文件,一种是主配置文件,还有一种就是映射文件,映射文件需要注意的是以下几个方面来学习:
参考文档:http://download.csdn.net/download/u011249702/9487540
1.映射基础
2.普通属性
3.主键
4.联合属性
5.关联关系
6.继承结构
在配置hibernate的时候要注意以下几点,这个有的必须有的是非必须的,在使用中要明确和注意
对持久化对象的要求:
①必须体提供一个无参的构造函数,当使用了有参数的构造器的时候,记得要写上一个默认的构造器,因为hibernate使用了无参的构造器使用反射得到实体对象
②一般都要提供一个标识属性,和主键对应,如果没有有的功能将失去作用,如save 或 update()
③一般都使用get/set方法对持久化的字段声明,其他的方法不太推荐
④使用非Final类,如果使用了Final修饰,将会影响hibernate使用CGLIB生成代理功能,这样就不能使用懒加载。
⑤可以重写equals和hashCode(),如果需要把关联的类实体放入到set中,需要关联映射时,应该重写两个方法。
⑥OID需要定义一个OID找到相应的一个对象(Object identifier- OID)对象标识,数据库中的主键
下面介绍hbm映射文件,映射文件是对象和数据库表的映射,那么这个必须要满足很多的约束和定义,在使用中务必要仔细和明确,首先介绍hbm文件的基本配置属性:
在hibernate-mapping中必须定义一个class,在class中首先定义的是name属性,代表的是实体类,需要注明的是类的全限定名,但是为了开发的简单,可以在hibernate-mapping上定一一个package来指定包,然后是table属性,指的是数据库中对应的表,不写表示使用类的简简单名称,记下来是主键和主键生成城策略,接下来就是普通属性
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.heying.domain">
<class name="User" table="t_user">
<id name="id" column="id" type="int">
<generator class="native"/>
</id>
<property name="name" column="name" type="string" length="20" />
</class>
</hibernate-mapping>
普通属性使用property 定义,一般定义的name指的是实体对应的属性,column指的是表中的列名,同样可以省略,在定义中如果属性使用到了数据库中的关键字的时候就需要特别注意,比如使用的属性为Desc,这个在数据库中是一个关键字,可以使用column定义修改。type属性是配置文件和数据库使用的数据类型的一个桥梁,在配置文件中使用string指的是java.lang.String,也可以这么写,但是不建议。也可以定义指定数据库中的数据类型,但是这样就不是很通用,需要考虑到使用的数据库。类型主要有string,text,date,time,timestamp,binary等。
一.主键生成策略
assigned:手工指定,使用时必须在程序中指定注定insert的主键值;
foreign:外键,只适用基于共享主键的一对一关联映射的时候使用。即一个对象的主键是参照的另一张表的主键生成的;
native:根据底层数据库的能力选择 identity、sequence 或者 hilo 中的一个;
identitiy:由底层数据库生成标识符,有底层数据库提供的一种自增长策略,不是多有数据库都有自增长(比如Oracle使用的是序列),支持DB2,MySQL,MS SQL Server,Sybase 和 HypersonicSQL;
sequence:序列,在 DB2,PostgreSQL,Oracle,SAP DB,McKoi 中使用;
hilo:高/低位算法高效的生成(默认分别是 hibernate_unique_key 和 next_hi)作为高位值的来源,高底位额外使用了一张表,下面是高低位的配置例子来源官方文档:
<id name="id" type="long" column="cat_id">
<generator class="hilo">
<param name="table">hi_value</param>
<param name="column">next_value</param>
<param name="max_lo">100</param>
</generator>
</id>
Oracle风格的seqhilo(需要支持sequence)
<id name="id" type="long" column="cat_id">
<generator class="seqhilo">
<param name="sequence">hi_value</param>
<param name="max_lo">100</param>
</generator>
</id>
uuid:128位UUID算法生成主键,内置使用UUID.randomUUID()生成
increment:只有在没有其他进程往同一张表中插入数据时才能使用。在集群下不要使用,因为存在线程安全问题,具体实现是先select max(id)+1 赋值给下面。
更多可参考:http://www.cnblogs.com/kakafra/archive/2012/09/16/2687569.html
二.集合属性
集合属性分为set,list,array,map四种种基本的映射方式:
①Set集:
比如需要网络购物中,一个帐号可能对应多个收件地址,所以必须对这个帐号分配一个收件方式的地址集合,在已知帐号的情况,怎么设计收件地址表,那么需要对帐号的主键映射过去的外键,还需要一个地址的元素,当然必不可少还需要指明表的名称
<!-- addressSet地址的集合, Set集合,可以在set中使用 order-by=""/sort 排序,前者是数据库排序,者是内存中排序 -->
<set name="addressSet" table="user_addressSet"> <!-- 表名 -->
<key column="userId"></key><!-- 外键 -->
<element type="string" column="address"></element><!-- 列名 -->
</set>
②List集:
<!-- addressList地址的集合, List集合 -->
<list name="addressList" table="user_addressList">
<key column="userId"></key>
<index column="idx"></index>
<element type="string" column="address"></element>
</list>
③Array数组:
<!-- addressArray地址的集合, Array集合 -->
<array name="addressArray" table="user_addressArray">
<key column="userId"></key>
<index column="idx"></index>
<element type="string" column="address"></element>
</array>
④Map集:
<!-- addressMap地址的集合, Map集合 -->
<map name="addressMap" table="user_addressMap">
<key column="userId"></key>
<map-key type="string" column="key_"></map-key>
<element type="string" column="address"></element>
</map>
三.关联关系
关联关系主要分为:一对一,一对多,多对一,多对多,前面介绍的是值类型的集合,这个对应的是实体和实体之间的对应集合,集合元素是一个实体。
(1)一对多,多对一
现实世界中很多的一对多和多对一的关系,比如系和学生,部门员工,领导阶层等,下面介绍员工和部门的例子来解释,看一看对象的设计和实体对应的hbm映射文件怎么配置。
第一. 建立两个实体:Department(部门),Employee(员工)
public class Department {
private Integer id;
private String name;
private Set<Employee> employees = new HashSet<Employee>(); //关联的员工
get/set();
}
public class Employee {
private Integer id;
private String name;
private Department department; // 关联的部门
get/set();
}
设计好了实体,应该分析实体和数据库中的对应关系,会帮助设计数据库,看到类图上进行配置数据库就比较明确!
第二. 配置映射文件
在写映射文件的时候,建议大家按照哦对应的实体去写,这样避免写错了关系,如下:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.heying.f_hbm_oneToMany">
<class name="Employee" table="employee">
<id name="id">
<generator class="native"></generator>
</id>
<property name="name" type="string" column="name"></property>
<!-- employee 与 department 的关系 many to one ,departmentId 为外键列-->
<many-to-one name="department" class="Department" column="departmentId"></many-to-one>
<!-- name type(Class) column 原则 -->
</class>
</hibernate-mapping>
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.heying.f_hbm_oneToMany">
<class name="Department" table="department">
<id name="id">
<generator class="native"></generator>
</id>
<property name="name" type="string" column="name"></property>
<!-- department 与 employee 的关系 one to many, departmentId为结合外键 -->
<set name="employees" inverse="true"><!-- 放弃 -->
<key column="departmentId"></key>
<one-to-many class="Employee"/>
</set>
<!--
<set name="">
<集合外键><key></key>
<集合元素><one-to-many/>
</set>
-->
</class>
</hibernate-mapping>
第二. 设置关联关系
设置了一个部门和两个员工,初始出来两个之间是没有联系的,可以双向设定,两边都维护,也可以一方关联去维护,这边是双向都维护,给员工设置部门,给部门绑定员工。一般是有外键的一方维护关联,没有外键的一方放弃维护,可以使用inverse=”true”
①建立关联关系:
@Test
public void testSave()throws Exception{
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
// create object
Department department = new Department();
department.setName("产品部");
Employee employee1 = new Employee();
employee1.setName("zhangshan_1");
Employee employee2 = new Employee();
employee2.setName("zhangshan_2");
// join object
employee1.setDepartment(department);
employee2.setDepartment(department);
department.getEmployees().add(employee1);
department.getEmployees().add(employee2);
session.save(department);
session.save(employee1);
session.save(employee2);
tx.commit();
session.close();
}
②解除关联关系:
@Test
public void remove()throws Exception{
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
//员工方解除,设置部门为Null
/*Employee employee = (Employee) session.get(Employee.class, 1);
employee.setDepartment(null);*/
//从部门方解除关系,inverse="false"可以解除
Department department = (Department) session.get(Department.class, 1);
department.getEmployees().clear();
tx.commit();
session.close();
}
③删除关联关系
也有的使用级联删除cascade=”delete”,如果这个对象放弃维护关联关系,可以在后面添加cascade=”delete”,表示在删除这个对象时候,也会级联删除对应的对象数据。
@Test
public void delete()throws Exception{
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
// 删除员工对部门没有影响,也是删除外键方不影响引用的主键方,哪怕部门下没有员工部门还是可以存在
// Employee employee = (Employee) session.get(Employee.class, 1);
// session.delete(employee);
// 删除部门方,先会去维护员工方,如果没有关联员工,直接删除,
// 如果有让引用删除的主键的外键update为null,
// 然后去delete部门方,inverse="false"可以删除
Department department = (Department) session.get(Department.class, 1);
session.delete(department);
tx.commit();
session.close();
}
(2)多对多
多对多的例子也是很多的,学生老师,课程和学生等,下面依照学生和教师为例子
第一. 建立实体:
public class Student {
private Long id;
private String name;
private Set<Teacher> teachers = new HashSet<Teacher>();
......
}
public class Teacher {
private Long id;
private String name;
private Set<Student> students = new HashSet<Student>();
......
}
第二. 设置关联关系
多对多的关联关系,需要建立一个中间的表去保存两个多对多的关系,储存的分别是对方主键生成的外键,通过外键对用关系找到对应的对象的映射关系项。
student.hbm.xml:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.heying.g_manyToMany">
<class name="Student" table="student">
<id name="id">
<generator class="native"></generator>
</id>
<property name="name" type="string" column="name"></property>
<set name="teachers" table="teacher_student" inverse="true">
<key column="studentId"></key>
<many-to-many class="Teacher" column="teacherId"></many-to-many>
</set>
</class>
</hibernate-mapping>
teacher.hbm.xml:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.heying.g_manyToMany">
<class name="Teacher" table="teacher">
<id name="id">
<generator class="native"></generator>
</id>
<property name="name" type="string" column="name"></property>
<set name="students" table="teacher_student">
<key column="teacherId"></key>
<many-to-many class="Student" column="studentId"></many-to-many>
</set>
</class>
</hibernate-mapping>
第三. 关联对象
建立关联关系可以选择一方去维护,这个视情况决定哪边维护设置:inverse=”true”放弃维护,由对方维护,双方都放弃,中间表将不会插入数据。
①建立关联关系
@Test
public void testSave()throws Exception{
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
Teacher teacher1 = new Teacher();
teacher1.setName("张老师");
Teacher teacher2 = new Teacher();
teacher2.setName("王老师");
Student student1 = new Student();
student1.setName("学生1");
Student student2 = new Student();
student2.setName("学生2");
//映射关系
student1.getTeachers().add(teacher1);
student1.getTeachers().add(teacher2);
student2.getTeachers().add(teacher2);
teacher1.getStudents().add(student1);
teacher1.getStudents().add(student2);
teacher2.getStudents().add(student2);
// save
session.save(teacher1);
session.save(teacher2);
session.save(student1);
session.save(student2);
tx.commit();
session.close();
}
②解除关联关系
同样受维护对象影响,应该查看配置文件决定谁维护。
@Test
public void remove()throws Exception{
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
Teacher teacher = (Teacher) session.get(Teacher.class, 2L);
teacher.getStudents().clear();
tx.commit();
session.close();
}
③删除关联关系
同样有维护方决定,也可以使用级联cascade=”delete”,save-update同理,在保存的时候只保存一方时候没有save-update会报错。值得注意的是:一般情况下是一对一和一对多使用级联,多对多一般不使用。
@Test
public void delete()throws Exception{
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
Teacher teacher = (Teacher) session.get(Teacher.class, 1L);
session.delete(teacher);
tx.commit();
session.close();
}
在Hibernate中对象通常有4种状态,临时,持久,游离和删除
临时状态:一般为新new出来的对象,这些对象没有即没有和数据库对应,也没有和session关联上,一般需要通过session.save()来保存为持久状态;
持久状态:和数据库关联有session管理,比如在session中获取一个对象,然后通过对象的setter()来修改对象,会看到一条update语句更新到数据库,这个就是持久化对象,session会检测这个对象的全过程,有改变就会同步到数据库;
游离状态:数据库有对应的记录,不在session中管理,这个状态的对象不会修改数据库;
删除状态:使用delete()后的对象。
在session中有一个缓存的存在需要注意,操作缓存的操作主要有:
①session.flush():立即执行SQL语句
②session.evict(obj):清除一个指定对象
③session.clear():清空缓存,一般保存很多的数据可能会引起session的过载而内存溢出
在session中要缓存有时候能提高效率有时候也能读到不是真实的数据,比如一个事务中查询两次,在第一个查询结束后,第二个查询开始前,另外一个事务中的事务对这条数据做出改变,在第二次查询中就算使用了清除缓存和刷新缓存(session.refresh(obj))都不会读到最新修改的数据,这是因为数据库的默认隔离级别有关系,一个事务加一个锁,只能读到本次事务开始状态时候数据库对应的数据,其他事务做出改变只能在这个改变结束后在下一个事务中读取到。
事务隔离级别参考:http://blog.csdn.net/u011249702/article/details/50999576 最后面
一般是四个隔离界别的:
1.读未提交:读没有commit的数据 级别为:0001(16) 1
2.读已提交:读别人commit后的数据 级别为:0010(16) 2
3.可重复读:读本次事务之内的数据(大部分数据库的默认级别)级别为: 0100(16) 4
4.不可并发(串行):一个一个排队读取 级别为:1000(16) 8
改变本次连接的隔离级别配置:
<property name="hibernate.connection.isolation">4</property>
session中对象的加载:
①get():立即加载,返回真实对象,没有返回null
②load():延时加载,返回代理对象,没有抛exception,不会马上执行SQL语句,会把查询的结果放到缓存,一般在调用到需要查询的时候才会开始执行,如果没有必要查就不会查询数据库,这就是懒加载,要求对象的类不是Final修饰的(需要子类建立代理对象),默认情况下的配置是使用懒加载的。
③createQuery():查询语句
④createCriteria():查询语句
(3)一对一
一对一的关系很多,通常是一个对象的特有属性,比如在特定情况下一个人一个帐号对应,一个人一张身份证等
一般有两种方式来实现:
基于外键+唯一约束
基于外键和之前的一样,只是在生成上添加一个唯一约束。
第一实体:
public class Person {
private Integer id;
private String name;
private IdCard idCard;
......
}
public class IdCard {
private Integer id;
private String number;
private Person person;
......
}
第二 配置关联关系
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.heying.oneToOneByFk">
<class name="Person" table="person">
<id name="id">
<generator class="native"></generator>
</id>
<property name="name" type="string" column="name"></property>
<!-- property-ref: 关联对方中外键对应的属性名 -->
<one-to-one name="idCard" class="IdCard" property-ref="person"></one-to-one>
</class>
</hibernate-mapping>
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.heying.oneToOneByFk">
<class name="IdCard" table="idcard">
<id name="id">
<generator class="native"></generator>
</id>
<property name="number" type="string" column="name"></property>
<many-to-one name="person" class="Person" column="personId" unique="true"></many-to-one>
</class>
</hibernate-mapping>
第三 关联对象
①建立关联:略
②解除关联:
值得注意的是:只能从有外键方解除,只能在有外键方维护关系,这边只是获取idCard的对象设置person为null
③删除:同解除相似,只能外键方删除,在无外键方删除就会跑出异常
基于主键方式
基于主键生成另外一张表的主键表明这个值不能为null 且unique 所以不能单独存在,必须和主表对应存在,而基于外键可以把外键设置为null而数据独立存在。
第一 实体
略
第二 配置关联关系
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.heying.oneToOneByPk">
<class name="Person" table="person">
<id name="id">
<generator class="native"></generator>
</id>
<property name="name" type="string" column="name"></property>
<!-- property-ref: 关联对方中外键的属性名-->
<one-to-one name="idCard" class="IdCard"></one-to-one>
</class>
</hibernate-mapping>
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.heying.oneToOneByPk">
<class name="IdCard" table="idcard">
<id name="id">
<!--
使用基于主键的一对一的,有外键的一方,主键生成策略为foreign
-->
<generator class="foreign">
<!-- property: 生成主键值时候所根据的对象 -->
<param name="property">person</param>
</generator>
</id>
<property name="number" type="string" column="name"></property>
<one-to-one name="person" class="Person" constrained="true"></one-to-one>
</class>
</hibernate-mapping>
第三 关联对象
①建立关联:略
②解除关联:
值得注意的是:基于主键的一对一双方都不可以解除关联关系,因为主键有非空约束
③删除:同上面相似,只能外键方删除,先删除自己,在删除对方,在无外键方删除就会抛出异常