标签:
Hibernate
课程内容:
1. 常见的O/R框架(了解)
2. Hibernate基础配置(重点)
3. ID生成策略(重点掌握auto)
4. Hibernate 核心开发接口介绍(重点)
5. 对象的三种状态(了解)
6. 关系映射(重点)
7. HQL(了解或重点)
8. 性能优化(重点)
9.
一.hibernate操作步骤
1. 可以将常用的jre包整合起来 下次使用直接调用
1.1右击项目àbuild pathàconfigurebuildpathàlibrariesà
1.2addlibraryàuser libraryàuser librariesànew à取个名字àadd jars—》加入需要的jar包
1.3 a. hibernate3.jar
b. lib\required 目录下的jar 包
c. slf4j-nop-1.5.8.jar
d. 数据库驱动jar包
1.4建立hibernate配置文件 默认名字为: hibernate.cfg.xml(src下面创建)
a.从参考文档中copy(文件头别忘了)
b.修改对应的数据库连接
c.注释暂时用不上的内容
1.5创建持久化类
a.对数据库dept表进行操作
b.创建包com.whhp.hibernate.mode
c. 创建实体类 dept
1.6创建dept类的映射文 Dept.hbm.xml(在该类的包下面创建)
a.参考文档copy(文件头别忘了)
b.id元素中填写主键
c. generator取值范围"native"由Hibernate根据使用的数据库自行判断采用identity、hilo、sequence其中一种作为主键生成方式。
d.字段名与列名相同,列名可以省略不写(表名一样)
1.7将映射文件加入到配置文件hibernate.cfg.xml中.
a.参考文档
b. <mapping resource="com/whhp/hibernate/mode/Dept.hbm.xml"/>
1.8编写测试类 Main 在Main中对Dept表进行操作
a.参考文档
二.方法2 通过myeclipse配置生成 连接数据库 和配置文件
连接数据库
1. windows==》工具栏—》Open perspective àmyeclipse database explorerànew
2.Oracle(Thin driver)
3. driver name:orcl(数据库名)
Connection URl: jdbc:oracle:thin:@127.0.0.1:1521:orcl
4. 保存密码 测试连接
5. 选择 display the selected schemas
6.添加用户
7. 右击打开连接 查看该用户下面的表
生成配置文件
1. 右击项目,选择myeclipse,选择 add hibernate capabilities
2. 选择hibernate 3.3core(如果使用自己的jar就勾上 User libraries)
3. Nextànext create sessionfactory class 钩去掉
编写sql
1. 右击项目创建文件夹sql
2. 创建文件dept.sql
3. 选择操作的数据库
4. 编写sql语句
编写映射文件
三.实现对数据库的增删改查
1. 不再创建Main 方法 使用junit来完成
a) 在项目下面创建文件夹(sourceFolder) 取名为test
b) 在文件夹中创建包,包名与需要测试的类所在的包名一致(避免导包)
c) 创建juit测试类 名字为 类名+test
d) Browse(添加要测试的类) à Next à添加要测试的方法
e) 在方法中创建对象调用要测试的方法,检查是否成功
四.Annotation版 helloworld
Hibernate 3.0开始支持Annotation
a) 添加jar包 hibernate-annotations.jar ejb3-persistence.jar hibernate-commons-annotations-3.3.0.ga.jar
b) 实体类添加注解 类名添加@entity 主键添加@id (注意: import javax.persistence)无需创建实体类映射了
c) Hinernate.cfg.xml 写法不一样 <mapping class="cn.whhp.mode.Teacher" />
d) Annotation的对象不一样了例如:
Configuration configuration=new AnnotationConfiguration();
Configuration configuration2=configuration.configure();
SessionFactory sFactory= configuration2.buildSessionFactory();
Session session= sFactory.getCurrentSession();
session.beginTransaction();
Teacher teacher=new Teacher();
//teacher.setId(3);
teacher.setName("习大大");
teacher.setTitle("主席");
session.save(teacher);
session.getTransaction().commit();
五. O/R Mapping Obejct/Relation Mapping 对象关系映射
1. jdbc操作数据很繁琐
2. sql语言编写并不是面向对象
3. 可以在对象和关系表之间建立关联来简化编程
4. O/R Mapping 简化编程
5. O/R Mapping跨越数据库平台
六 O/R Mapping Frameworks
1. Hibernate
2. Toplink(Java对象关系可持续性体系结构,优秀的对象关系映射持久层解决方案)
3. Jdo(Java对象持久化的新的规范,也是一个用于存取某种数据仓库中的对象的标准化API)
4. ibatis
5. JPA(JPA(Java Persistence API)是Sun官方提出的Java持久化规范。它为Java开发人员提供了一种对象/关系映射工具来管理Java应用中的关系数据。,而Hibernate是它的一种实现。除了Hibernate,还有EclipseLink(曾经的toplink),OpenJPA等可;供选择,所以使用Jpa的一个好处是,可以更换实现而不必改动太多代码) 意愿:一统天下
七hibernate 基础配置
1. Hibernate.cfg.xml:hbm2ddl.auto(用的比较多是create)
a) Create 自动在数据库创建表
b) Update 根据实体类更新表结构
c) create-drop关闭SessionFactory 会把数据库创建好的表给删掉
d) validate 对数据操作之前会检查表的结构是否与配置文件是否匹配
2.先建表还是先建类?
3.搭建日志环境并配置显示DDL语句
a) 问题? 控制台只显示插入的sql语句 未显示建表sql
b) 日志框架 slf
c) Hibernate 3.3.2所使用的slf 接口定义了一系列规范
d) 具体的实现有很多种 1.slf4j 2.log4j 3.jdk logging api 4.appche commons-logging
e) 用的较多log4j 可以用slf接口 log4j来实现
步骤:
i. 添加slf4j-log4j12-1.5.5.jar包实现 slf4j与log4j之间的对接
ii. 添加log4j jar包 log4j-1.2.15.jar
iii. 编写Log4j配件文件 (API中复制)路径hibernate-distribution-3.3.2.GA\project\etc
iv. log4j.logger.org.hibernate.tool.hbm2ddl=debug可以在控制台把建表以及其他相关信息打印出来
4.hibernate.cfg.xml : show_sql 输出所有SQL语句到控制台
5.hibernate.cfg.xml :format_sql在log和console中打印出更漂亮的SQL
6.表名和类名不同,对表名进行配置
a) Annotation:@Table(name=”表名”)
b) Xml:查询参考文档
7.字段名和属性相同
a) Annotation:默认为@Basic,写不写都可以
b) Xml中不用写column
8.字段名和属性不同
a) Annotation:@Column( name=”列名”)(写在字段get方法上)
b) Xml: 询参考文档
9.不需要persistence的字段
a) Annotation:@Transient
b) Xml不写
10.映射日期与时间类型,指定时间精度
a) Annotation:@Temporal @Temporal(TemporalType.TIMESTAMP)
b) Xml:指定Type
11.映射枚举类型
a) Annotation:
@Enumerated(EnumType.STRING) 字符串枚举类型
@Enumerated(EnumType.ORDINAL) 数序(数字)枚举类型(数据库存枚举的下标)
b) Xml:相当麻烦
12.@Lob
13.CLOB BLOB类型的数据存取
14.Hibernate自定义数据类型
15.hibernate类型
1、Java基本类型的Hibernate映射类型
Hibernate映射类型 |
Java类型 |
标准SQL类型 |
大小和取值范围 |
integer或者int |
int或者java.lang.Integer |
INTEGER |
4字节 |
long |
long Long |
BIGINT |
8字节 |
short |
short Short |
SMALLINT |
2字节 |
byte |
byte Byte |
TINYINT |
1字节 |
float |
float Float |
FLOAT |
4字节 |
double |
double Double |
DOUBLE |
8字节 |
big_decimal |
java.math.BigDecimal |
NUMERIC |
NUMERIC(8,2)8位 |
character |
char Character String |
CHAR(1) |
定长字符 |
string |
String |
VARCHAR |
变长字符串 |
boolean |
boolean Boolean |
BIT |
布尔类型 |
yes_no |
boolean Boolean |
CHAR(1) (Y-N) |
布尔类型 |
true_false |
boolean Boolean |
CHAR(1) (T-F) |
布尔类型 |
2、Java时间和日期类型的Hibernate映射
映射类型 |
Java类型 |
标准SQL类型 |
描述 |
date |
util.Date或者sql.Date |
DATE |
YYYY-MM-DD |
time |
Date Time |
TIME |
HH:MM:SS |
timestamp |
Date Timestamp |
TIMESTAMP |
YYYYMMDDHHMMSS |
calendar |
calendar |
TIMESTAMP |
YYYYMMDDHHMMSS |
calendar_date |
calendar |
DATE |
YYYY-MM-DD |
3、Java大对象类型的Hibernate映射类型
映射类型 |
Java类型 |
标准SQL类型 |
MySQL类型 |
Oracle类型 |
binary |
byte[] |
VARBINARY(或BLOB) |
BLOB |
BLOB |
text |
String |
CLOB |
TEXT |
CLOB |
serializable |
Serializable接口任意实现类 |
VARBINARY(或BLOB) |
BLOB |
BLOB |
clob |
java.sql.Clob |
CLOB |
TEXT |
CLOB |
blob |
java.sql.Blob |
BLOB |
BLOB |
BLOB |
考虑到 操作 blob 的字段太复杂 一个变换的技巧是 . 实体类用 byte[] 类型 , hibernate 类型用 binary ,数据库还是用 blob .这样 可以简化一些操作
八.ID生成策略
1. Hibernate生成表的结构并不是为了将来就用它生成,可能还有自己的扩展,比如index等,而是为了明白我们应该建立什么样的表和实体映射。
2. Xml生成id
generator
常用四个:native identity sequence uuid(数据库跨平台选native uuid)
3. @GeneratedValue(参考annotation API)
1. 手动指定;没说的之前写过,xml和属性都使用过;@Id
2. 使用 Generator 生成器;
Generator 有很多值:常用的有native , identity, sequence,uuid(id类型 String) ;
使用Generarot帮我们自动生成ID:
@GeneratedValue
注解在 @Id下:
@Id
@GeneratedValue
默认策略 auto/native ;如果你使用MySQL,那么自动使用 auto_increment
指定ID生成策略 @GeneratedValue
@GeneratedValue(strategy=GenerationType.AUTO) @GeneratedValue(strategy=GenerationType.IDENTITY) @GeneratedValue(strategy=GenerationType.SEQUENCE) @GeneratedValue(strategy=GenerationType.TABLE)
这里要注意要使用数据库支持的,比如这里MySQL我们使用IDENTITY
a) 自定义ID
b) AUTO(Teacher2)
默认:对Oracle 使用hibernate_sequence
对MySql 使用 auto_increment
c) Identity @GeneratedValue(strategy=GenerationType.IDENTITY)只能用在MySql或SqlServer中,Oracle不能用
d) SEQUENCE @GeneratedValue(strategy=GenerationType.SEQUENCE) 只能用在Oracle
i. 默认使用的序列名字为hibernate_sequence,如果需要指明序列可以如下操作
ii. 类名前加上@SequenceGenerator(name="teacherSEQ",sequenceName="seq_teacher")
iii. ID前加上@GeneratedValue(strategy=GenerationType.SEQUENCE,generator="teacherSEQ")
iv. 首先会查找在数据库中是否存在序列seq_teacher,不存在创建
e) @ TableGenerator 通过表来生成id(参考Annotation API)(跨数据库平台时可以用到,一般很少用)
在类前面加上@TableGenerator(
name="Teacher_GEN",(给表取个别名,方便在使用时查找)
table="GENERATOR_TABLE",(表本身的名字)
pkColumnName = "key",(第一列的列名)
valueColumnName = "hi",(第二列的列名)
pkColumnValue="EMP",(表中一条记录第一列的值为EMP)
allocationSize=1(每次取值后加1)
)
类的属性ID前面做如下修改@Id
@GeneratedValue(strategy=GenerationType.TABLE,generator="Teacher_GEN")
public int getId() {
return id;
}
f) 联合主键(能用1个就不要用2个)
a) Xml:
i. 要重写equals和hashCode保证主键的唯一性
ii. 要实现serializable 数据转移时需要
iii. 现将id和name作为主键
Student表中 private StudentPK pk;
创建主键表StudentPK,有id和name属性 实现Serializable,重写2个方法
@Override
public boolean equals(Object o){
if (o instanceof StudentPK) {
StudentPK sPk=(StudentPK) o;
if (this.id==sPk.id&&sPk.name.equals(this.name)) {
return true;
}
}
return false;
}
@Override
public int hashCode(){
return this.name.hashCode();
}
Student映射文件主键写法
<composite-id name="pk" class="cn.whhp.model.StudentPK">
<key-property name="id" />
<key-property name="name" />
</composite-id>
b) Annotation
同xml
在主表中添加注解
@EmbeddedId
public StudentPK getPk() {
return pk;
}
九.FAQ
用Junit测试时Hibernate SessionFactory初始化异常不提示下(junit的bug),解决如
i. 在Juint测试方法中添加try cath
ii. 用main方法测试
十.核心开发接口介绍
Hibernate 核心接口 三种对象状态 四种操作方法
a) Configuration
i. AnnotationConfiguration
ii. 进行配置信息管理
iii. 用来产生SessionFactory
iv. 可以再configure方法在指定hibernate配置文件
v. 只需要关注一个方法:buildSessionFactory
b) SessionFactory
i. 用来产生和管理session
ii. 通常情况下每个应用只需要一个SessionFactory
iii. 除非要访问多个数据库的情况
iv. 关注两个方法即可:openSesion getCurrentSession
Opensession每次都是新的
getCurrentSession从上下文找,如果有,用旧的,如果没有,建新的
c) Session
管理一个数据库的任务单元
方法
a) Save()
b) Delete()
c) Update()
d) SaveOrUpdate()
save()方法很显然是执行保存操作的,如果是对一个new出来的对象进行保存,自然要使用这个方法了,数据库中没有这个对象。
update()如果是对一个已经存在的游离对象进行更新那么肯定是要使用update()方法了,数据中有这个对象。
saveOrUpdate()这个方法是更新或者插入,有主键就执行更新,如果没有主键就执行插入。
区别:对于一个从托管状态到瞬态的对象(对于一个从数据库中取出来又被删除的对象),这个对象本身是有主键的,但是因为被删除了,所以这个时候因为数据库中已经没有了这条记录了。不过它还有主键存在,所以这个时候不可以使用update()或者是saveOrUpdate(),因为update()方法是认为数据库中肯定有这条记录的,而saveOrUpdate的执行过程就是先查看这个对象是不是有主键,有主键那么就执行update()方法,没有主键就执行save()方法,因此结果跟调用了update()方法的效果是一样的,结果就会出错,因为这个对象已经被删除了,数据库中已经没有这条记录了,只是它还有主键而已(仅仅是存在于内存中),因此这个时候要执行的是save()方法
e) Load
f) Get
g) Get与load的区别
get方法首先查询session缓存,没有的话直接发送sql查询数据库,一定要获取到真实的数据,否则返回null,并不适用二级缓存;反而load方法创建时首先查询session缓存,没有就创建代理,实际使用数据时才查询二级缓存和数据库,hibernate对于load方法认为该数据在数据库中一定存在,可以放心的使用代理来延迟加载,如果在使用过程中发现了问题,就抛异常(所谓延迟加载也称为懒加载就是当在真正需要数据的时候,才真正执行数据加载操作。可以简单理解为,只有在使用的时候,才会发出sql语句进行查询。)
(代理实际就是空的对象 并没有去数据库查询得到的 我们叫代理对象,如果 去数据库查询了 返回到了这个对象 我们叫实体对象 就是这个对象真实存在)
h) Clear方法
无论是load还是get 都会首先查找缓存(一级缓存),如果没有,才会从数据库查找,调用clear()方法可以强制清除session缓存
Session.Commit() 这个方法它自动调用了close()和flush()方法
i) 调用flush()可以强制从内存到数据库的同步
三种对象状态图
j) 三种状态的区别
区分方法:有没有id,数据库中有没有,缓存中有没有
Trasient(临时状态):没有id, 数据库中没有,
缓存没有
Persistent(持久化状态):有id, 数据库有, 缓存有
Detached(游离状态):有id, 数据库有, 缓存没有
Configuration configuration=new AnnotationConfiguration();
Configuration configuration2=configuration.configure();
SessionFactory sFactory=configuration2.buildSessionFactory();
Session session=sFactory.getCurrentSession();
session.beginTransaction();
//临时状态
People people=new People();
people.setName("女娲");
people.setSex(Gender.male);
//持久状态
session.save(people);
session.getTransaction().commit();
//游离状态
System.out.println(people.getId());缓存:
save方法完成之后,session将对象引用放在session缓存区中,
有一个hasMap引用,里面有一系列key和对应的vaule指向这个应用的对象。
比如上面的save(t)完成之后,会有一个id的key,然后value指向数据库中那个对象。
增删查改:
Configuration configuration=new AnnotationConfiguration();
Configuration configuration2=configuration.configure();
SessionFactory sFactory=configuration2.buildSessionFactory();
/**
* 新增
*/
public void savePeople(){
Session session=sFactory.getCurrentSession();
session.beginTransaction();
//临时状态
People people=new People();
people.setName("耶稣");
people.setSex(Gender.male);
//持久状态
session.save(people);
session.getTransaction().commit();
//游离状态
System.out.println(people.getId());
}
public void loadPeople(){
Session session=sFactory.getCurrentSession();
session.beginTransaction();
People people=(People) session.load(People.class,2);
System.out.println("load方法加载之前"+people.getName());
session.getTransaction().commit();
System.out.println("load方法加载之后"+people.getName());
System.out.println(people.getId());
}
public void getPeople(){
Session session=sFactory.getCurrentSession();
session.beginTransaction();
People people=(People) session.get(People.class, 2);
System.out.println("get方法加载之前"+people.getName());
session.getTransaction().commit();
System.out.println("get方法加载之后"+people.getName());
System.out.println(people.getId());
}
public void deletePeople(){
//必须有id好才能delete,也就是新new的对象不能delete,save之后才能提交
//id是对象的唯一标识,不管你姓名和别的参数怎么设置都会按照id进行删除,
//比如下面的name属性就不必设置了,但设置成别的参数也没有问题
Session session=sFactory.getCurrentSession();
session.beginTransaction();
People people=new People();
people.setId(1);
session.delete(people);
session.getTransaction().commit();
System.out.println(people.getId());
}
public void updatePeople(){
Session session=sFactory.getCurrentSession();
session.beginTransaction();
People people=(People) session.get(People.class,3);
session.getTransaction().commit();
Session session1=sFactory.getCurrentSession();
session1.beginTransaction();//people的状态为游离
people.setName("Update name");
session1.update(people);
session1.getTransaction().commit();
System.out.println("更新后名字为:"+people.getName());
}
public void clearPeople(){
Session session=sFactory.getCurrentSession();
session.beginTransaction();
People people=(People) session.load(People.class,3);
System.out.println(people.getName());
//如果不用这个clear方法查找同一个id只发出一条语句到数据库,
//因为在缓存中能找到,如果用clear清楚缓存之后,就会重新到数据库查询
session.clear();
People people1=(People) session.load(People.class,3);
System.out.println(people1.getName());
session.getTransaction().commit();
}
public void testflush() {
Session session=sFactory.getCurrentSession();
session.beginTransaction();
People people=(People) session.load(People.class,3);
System.out.println(people.getName());
people.setName("NameA");
//不加flush只会在commit的时候进行提交,两个set提交一次,
//最后设置的setter内容;当flush的时候会立即提交
session.flush();
people.setName("NAMEB");
session.getTransaction().commit();
}
十一.关系映射
对象之间的关系
1. 这里的关系映射指的是对象之间的关系,并不是指数据库的关系,而是当对象之间处于下列关系之一时,数据库表该如何映射,编程上该如何处理
a) 一对一:单向(主键,外键),双向(主键,外键)
b) 一对多:单向,双向(和多对一双向相同)
c) 多对一:单向,双向(一对多双向和多对一双向是一样的)
d) 多对多:单向,双向
e) (一对一单/双向主键关联映射,只作了解)
f) 集合映射:list ,map,set
g) 继承映射(了解):单表,多表,一张主表多张子表
h) 组件映射:@Embeddable,@Embedded
一对一(one to one) 单向关联映射
两个对象是一对一的的关系.
有两种策略可以实现一对一的关联映射
l 主键关联:即让两个对象具有相同的主键值,以表明他们之间的一对一的对应关系;数据库表不会有额外的字段来维护他们之间的关系,仅通过表的主键关系来维护.一对一主键关联映射默认了级联属性,其关联对象会同时存储.所以不会抛出TransientObjectException异常.
l 唯一外键关联:外键关联,本来是用于多对一的配置,但是如果加上唯一的限制之后,也可以表示一对一的关联关系. unique="true".
单向关联,如Person-person_id;加载person信息时能关联对应的person_id信息
双向关系,加载任何一方,都能关联出别一方的信息.
注意id的主键生成策略,foreign使用另外一个相关联的对象的标识符。通常和<one-to-one>联合起来使用。
类Person(id,name,idCard),
类IdCard(id,cardNo)
一对一(单向)基于主键关联映射(了解)
XML配置方法
一对一单向主键关联通常使用一个特定的id生成器。
<class name="Person" table=”t_person”>
<id name="id" >
<generator class="foreign">
<param name="property">idCard</param>
</generator>
</id>
<one-to-one name="idCard" constrained="true"/>
</class>
one-to-one不会加载字段,它告诉HIBERNATE怎样加载其引用对象.如何加载呢,默认根据主键加载其引用对象.如在t_person中查到id=2,自动加载t_idCard中id=2的对象信息. constrained="true",表明person主键是个外键,表示当前主键上存在着idCard约束,当前主键id作为外键,参照了idCard.
<param name="property">idCard</param>表明person中的id来源于idCard,也就是共享idCard的主键.
Annotation配置一对一(单向)主键关联映射.(BUG)
@OneToOne
@PrimaryKeyJoinColumn
有BUG,系统不会生成主键映射.推荐使用XML配置方法.
一对一(单向)基于外键关联映射
和单向多对一关联几乎是一样的。唯一不同的就是单向一对一关联中的外键字段具有唯一性约束。这种方法会在表中生成一个新的外键字段.如果不限制外字段的唯一性约束,就会导致产生多对一的关联. 指定多的一端unique="true",这样就限制了多的一端的多重性为一.
<class name="Person" table=”t_person”>
<id name="id" >
<generator class="native"/>
</id>
<property name=”name”/>
<many-to-one name="idCard" column="addressId" unique="true"
not-null="true"/>
</class>
这种状态注意TransientObjectException异常.在保存时就先保存外键idCard,再保存Person类.
一对一单向外键关联Annotation配置
@OneToOne
@JoinColumn(name="w_id")(不加也可以,会自动生成一个默认的名字)
一对一(双向)主键关联映射(了解)
hibernate一对一双向指的是2个对象都可以互相的调用,双向的配置可以方便我们调用对象。比如知道A对象就可以使用B对象,可以调用B对象就可以使用A对象
Person?àIdCard.在另一端也加上一个一对一的单向关联映射.
模型对象
Person(id,name,idCard)
IdCard(id,cardNo,person)中,双方都持有对方的属性引用.
一对一(双向)主键关联映射XML配置方式
在IdCard配置中建立映射,<one-to-one name="person"/>指示Hibernate如何加载,默认情况下根据主键加载.也就是在基于单向一对一的映射中, 在另一端也加上一个单向一对一的主键关联映射.
在Person一端配置
<class name="Person" table=”t_person”>
<id name="id" >
<generator class="foreign">
<param name="property">idCard</param>
</generator>
</id>
<one-to-one name="idCard" constrained="true"/>
</class>
在另一端IdCard配置
<class name=" IdCard " table=”t_idCard”>
<id name="id" >
<generator class="native"/>
</id>
<property name=”cardNo”/>
<one-to-one name="person" property-ref=”idCard” />
</class>
一对一(双向)主键关联映射Annotation(有BUG)
在两端各自的引用属性上加上
@OneToOne
@PrimaryKeyJoinColumn
一对一(双向)唯一外键关联映射
Person?----àIdCard.在另一端也加上一个一对一的单向关联映射.
在模型对象
Person(id,name,idCard)
IdCard(id,cardNo,person),
双方都持有对方的属性引用.
需要在另一端加上<one-to-one>,指示hibernate如何加载,默认情况下根据主键加载person;因为外键关联映射中,两个实体的关系是由person的外键idCard来维护的,所以不能指定person的主键来加载person,而应根据person的外键idCard来加载person对象.
一对一双向外键关联映射XML配置方式
Person一端:用多对一配置外键唯一形成一对一的配置方式.
<class name="Person" table=”t_person”>
<id name="id" >
<generator class="native"/>
</id>
<property name=”name”/>
<many-to-one name="idCard" column="addressId" unique="true"/>
</class>
IdCard一端:一对一,引用另一端外键
<class name=" IdCard " table=”t_idCard”>
<id name="id" >
<generator class="native"/>
</id>
<property name=”cardNo”/>
<one-to-one name="person" property-ref="idCard"/>
</class>
要想加载idCard,如果不加property-ref,默认根据person主键id来加载,property- ref="idCard"就指示hibernate从person里面的idCard属性来加载.
一对一双向外键关联映射Annotation配置方式
双方互持对方的属性的引用
关系模型
Husband(id,name,wife)
Wife(id,name,husband)
在Husband一端的wife属性上注解
@OneToOne
//@JoinColumn
在Wife一端的husband加上注解,mappedBy
@OneToOne(mappedBy="wife")引用属性
加上mappedBy属性后就可以在wife这一端告知这个关联是在wife属性上设置的.就不用管理wife这一端生成的husband的设置.生成的wife表格不会有husband字段.
规律:有双向关联时mappedBy通常必设.
联合主键一对一单向外键关联映射
对象模型
Wife(id,name,age) WifePk(id,name)(需要实现Serializable接口)
Husband(id,name,wife)
1 在Wife中建立联合主键生成策略
@IdClass(WifePk.Class)
@Id
2 在Husband中添加个外键即可
@OneToOne
3自定义Husband中外键的名字
@OneToOne
@JoinColumns(
{
@JoinColumn(name="wifeId", referencedColumnName="id"),
@JoinColumn(name="wifeName", referencedColumnName="name")
}
)
XML配置方式:略
组件映射
对象关系:一个对象是另一个对象的一部分
数据库表:是一张表
Annotation:@Embeddable,@Embedded
XML:<component>
对象模型
Husband(id,name,wife)
Wife(wifeName,wifeAge)
Annotation:
在Husband的wife属性上建立注解
@Embedded 表明该对象是从别的位置嵌入过来的,是不需要单独映射的表.
这种方式生成的表为husband(id,name,wifename,wifeage),不会生成wife表.
XML:
<class name="Husband" >
<id name="id">
<generator class="native"/>
</id>
<property name="name"></property>
<component name="wife">
<property name="wifeName"/>
<property name="wifeAge"/>
</component>
</class>
数据库设计的三大范式
第一范式:每列保证最小原子性
第二范式:是建立在第二范式的基础之上
一个表只描述一件事情
第三范式:是建立在第二范式的基础之上
所有列与主键的关系是直接的关系
多对一(many to one)单向关联映射
多对一的数据库设计原则:在多的那下端加外键
//注意在创建实体类属性时应尽量避免与SQL语句中的关键字重名.
多对一单向关联映射
实体模型(User多对一Group)
User(id,name,group)多
Group(id,groupname)一
Annotation配置
@Entity
@Table(name="t_group")//注意表名与SQL中关键字重名
只需要在多的一端User属性group进行注解配置
@ManyToOne
@JoinColumn(name=”groupId”)
XML配置
<many-to-one name="group" column="groupId" />
标签会在”多”的一端添加外键,相当于在数据库中添加外键
生成的表为user(id,name,groupid),t_group(id,groupname)
属性cascade
<many-to-one name="group" column="groupid" cascade="all"/>
取值all,none,save-update,delete,对象间的级联操作,只对增删改起作用.
在存储时User时,设置了cascade="all"会自动存储相应的t_group.而不用管user关联的对象(通常情况下会优先存储关联的对象,然后再存储user).
一对多(one to many)单向关联映射
模型(group一对多user)
Group(id,name,users)一
User(id,name)多
设计时在一的这一端存在着多的集合,生成的数据库表通常是在多的一端生成外键.
Set<User> users=new HashSet<User>()
一对多单向外键关联映射
在一的这一端Group端users属性上进行注解配置
@OneToMany
@JoinColumn(name="groupId")(不可少的)
如果不指定生成的外键列@JoinColumn(name="groupId"),默认会生成多对多的关系,产生一张中间表.
XML配置中配置一的那一端Group
<class name="com.hibernate.Group" table="t_group">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<set name="users">
<key column="groupId"/>指定生成外键字段的名字
<one-to-many class="com.hibernate.User"/>
</set>
</class>
规律:
l 单向时, 一方存在另一方的引用,数据库表中体现为外键.配置时一般只用配置一端.
l 双向时,双方互持对方的引用,要进行双向两端的配置.基于annotation的双向配置中,在一端配置好后,在另一端必须用指定mappedby属性.
l 关于一对多/多对一数据库设计时,总是在多的一端加外键.通常在多的一端指定生成的外键名称.
一对多/多对一双向关联
一对多与多对一的双向关联是同一种情况.
关系模型(group一对多user)
User(id,name,group)多
Set<User> users=new HashSet<User>()
配置规则:一般以多的一端为主,先配置多的一端
在多的一端User端配置group
@ManyToOne
在一的一端Group端配置时,在users只需要加个mappedBy="group"
@OneToMany(mappedBy="group")
XML配置
Group中
<set name="users">
<key column="groupId"/>
<one-to-many class="com.hibernate.User"/>
</set>
在User中
<many-to-one name="group" column="groupId"/>
务必确保在多的一端生成的生成的外键和一的一方生成的外键的名字相同,都为groupId.
如果名字不同则会在多的一端生成多余的外键.
create table t_group (
id integer not null auto_increment,
name varchar(255),
primary key (id)
)
create table t_user (
id integer not null auto_increment,
name varchar(255),
groupId integer,
primary key (id)
)
alter table t_user
add index FKCB63CCB6C3D18669 (groupId),
add constraint FKCB63CCB6C3D18669
foreign key (groupId) references t_group (id)
多对多单向关联
关系举例:老师à学生,老师需要知道自己教了哪些学生,但学生不知道自己被哪些老师来教.
数据库:中间表
Annotation:@ManyToMany
XML:<many-to-many>
关系模型(Teache多对多Student),从Teacher这一端能关联到students.
Teacher(id,name,students)多
Student(id,name)多
Set<Student> students=new HashSet<Student>()
在Teacher那一端配置
@ManyToMany
如果手动指定生成的中间表的表名和字段名
@JoinTable(
name="t_s", //表名
joinColumns={@JoinColumn(name="teacher_id")},//指向teacher表
inverseJoinColumns={@JoinColumn(name="student_id")}//指向另一张表
)
生成的表为
create table Student (
id integer not null auto_increment,
name varchar(255),
primary key (id)
)
create table Teacher (
id integer not null auto_increment,
name varchar(255),
primary key (id)
)
create table t_s (//生成的中间表
teacher_id integer not null,
student_id integer not null,
primary key (teacher_id, student_id)
)
t_s表的两个属性分别references其它表的主键.
XML中
<class name="com.xxx.Teacher">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<set name="students" table="t_s">table定义中间表的表名
<key column="teacher_id"></key>
<many-to-many class="com.xxx.Student" column="student_id"/>
</set>
</class>
多对多双向关联
关系举例:老师?à学生,老师需要知道自己教了哪些学生,学生也知道自己有哪些老师.
数据库:中间表
Annotation:@ManyToMany
XML:<many-to-many>
多对多单向配置只需要在一端进行配置就可以了.
关系模型(Teache多对多Student)
Teacher(id,name,students)多
Student(id,name,teachers)多
Set<Student> students=new HashSet<Student>()
Set<Teacher> teachers = new HashSet<Teacher>();
Annotation配置
在Teacher这一端的students上配置
@ManyToMany
@JoinTable(name="t_s",
joinColumns={@JoinColumn(name="teacher_id")},
inverseJoinColumns={@JoinColumn(name="student_id")}
)
在Student一端的teachers只需要配置
@ManyToMany(mappedBy="students")
XML配置方式:两端配置一样,注意表名和生成的中间表的字段属性名要一致
Teacher那一端配置
<set name="students" table="t_s">
<key column="teacher_id"/>
<many-to-many class="com.xxx.Student" column="student_id"/>
</set>
在Student那一端配置
<set name="teachers" table="t_s">
<key column="student_id"></key>
<many-to-many class="com.xxx.Teacher" column="teacher_id"/>
</set>
生成的数据库表和上面是一样的.
树状结构的设计(重要)
设计思想:数据库模型,面向对象模式,关系映射,CRUD
数据库模型:表(id,name,pid)
实体模型:父结点?一对多à子结点,一对多/多对一双向关联映射,一个子结点只有一个父结点,一个父结点有多个子结点.
Class Org
private int id;
private String name;
private Set<Org> children = new HashSet<Org>();
private Org parent;
关系映射:在同一个类中使用@ManyToOne和@OneToMany
在父结点parent上
@ManyToOne
@JoinColumn(name="parent_id")
public Org getParent() {
return parent;
}
在子结点children上
@OneToMany(cascade=CascadeType.ALL, mappedBy="parent")
public Set<Org> getChildren() {
return children;
}
基本关联关系对象的CRUD
1 想删除或者更新,先做load,除非精确知道ID.
2 Cascade属性(是一个数组)管理CUD,只是为了编程的方便,不要把它的功能看得太大.
cascade={CascadeType.ALL}
CascadeType取值
ALL Cascade all operations所有情况
MERGE Cascade merge operation合并(merge=save+update)
PERSIST Cascade persist operation存储 persist()(只有调用persist()方法才有效果,save()无效)
REFRESH Cascade refresh operation刷新
REMOVE Cascade remove operation删除
规律,双向关系要是程序中设定双向关联.还要设置mappedBy
演示案例:1.保存单方关联对象,另外一方自动保存
2.读取对象时,读取many时会自动读取出one的一方,读取one时不会自动读取many (Cascade不会影响读取)
3.删除对象时会自动删除关联对象(不设置会将多的一方对应一得一方id为null)
3 Fetch属性,读取,管理R(Retrieve查询)
fetch = FetchType.LAZY|EAGER
Hibernate Annotation的默认的FetchType在ManyToOne是EAGER的,在OneToMany上默认的是LAZY.
如果指定OneToOne的fetch=FetchType.LAZY,会延迟对关联对象的加载,不管是load还是get.
在XML中,在外键属性上设置inverse=”true|false”进行设置.
Hibernate查询(HQL)
Hql 语句对表还是对 对象操作?
QL(Query Language)
NativeSQL(数据库的本地语言)>HQL>QBC(Query by Criteria)
参数绑定形式
a) 按参数位置绑定
b) 按参数名称绑定
链式编程
Query q = session.createQuery("from Category c where c.id > :min and c.id < :max")
.setInteger("min", 2)
.setInteger("max", 8);
Query q = session.createQuery("from Category c where c.id > ? and c.id < ?");
q.setParameter(0, 2)
.setParameter(1, 8);
分页显示
Query q = session.createQuery("from Category c order by c.name desc");
q.setFirstResult(2);//设置起始记录位置.
q.setMaxResults(4);//设置每页显示的最大记录数
q.uniqueResult()//返回唯一的一条记录.
Is null
Is empty 测试集合是否为空
Criteria
Criteria criteria=session.createCriteria(Group.class);
List<Group> groups= criteria.list();
条件查询
a) 比较运算
查询部门名称为CLERK Restrictions.eq( )
Criteria criteria=session.createCriteria(Emp.class);
Criterion criterion=Restrictions.eq("job","CLERK");
criteria=criteria.add(criterion);
List<Emp> emps= criteria.list();
查询薪水大于2000 Restrictions.gt()
Criteria criteria=session.createCriteria(Emp.class);
Criterion criterion=Restrictions.gt("sal", 2000);
criteria=criteria.add(criterion);
List<Emp> emps= criteria.list();
查询没有奖金Restrictions.isNull()
Criteria criteria=session.createCriteria(Emp.class);
Criterion criterion=Restrictions.isNull("comm");
criteria=criteria.add(criterion);
b) 范围运算 Restrictions.in( )
c) 字符串模式匹配 Restrictions.ilike( )
d) 逻辑运算 Restrictions.and( )
e) 集合运算 Restrictions.isEmpty( )
性能优化 get 3.2s load 3s
1 注意session.clear()的运用,尤其是不断分页循环的时候
A 在一个大集合中进行遍历,取出其中含有敏感字的对象,把敏感字换成别的
B 另一种形式的内存泄露.
面试题:java有内存泄露吗?语法级别上没有,jvm自动回收,但是在写程序的过程中,用到了一些资源,一定记得回收。比如:打开了连接池,打开了连接 用完记得关闭,不然在内存中老开着,再比如,打开了文件记得关闭( java调用了 C C调用了window API,C语言是手动回收内存的 其实是java引起了内存泄露)
2 1+N问题 面试题
LAZY ,BatchSize,join fetch
3list和iterator的区别
list取所有
iterator先取ID,等到要用的时候再根据ID取出对象.
session中list第二次发出,仍会到数据库中查询数据.iterator第二次首先查找session级缓存.
4一级缓存和二级缓存和查询缓存
一级缓存就是Session级别的缓存,一个Session做了一个查询操作,它会把这个操作的结果放在一级缓存中,如果短时间内这个session(一定要同一个session)又做了同一个操作,那么hibernate直接从一级缓存中拿,而不会再去连数据库,取数据。
二级缓存就是SessionFactory级别的缓存,顾名思义,就是查询的时候会把查询结果缓存到二级缓存中,如果同一个sessionFactory创建的某个session执行了相同的操作,hibernate就会从二级缓存中拿结果,而不会再去连接数据库。
A 缓存
B 一级缓存,session级别的缓存
C 二级缓存,SessionFactory级别的缓存,可以跨越session级别存在.
适合放二级缓存:
经常被访问,改动不大不会经常访问,数量有限.如:用户权限,组织机构
D 打开二级缓存
Hibernate.cfg.xml
<property name="cache.use_second_level_cache">true</property>
使用ehcache
JAR文件:\lib\optional ehcache-1.2.3.jar
<property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
使用配置文件ehcache.xml,将其复制到src目录下.
Annotation注解:@Cache
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@Cache(
CacheConcurrencyStrategy usage(); (1)
String region() default ""; (2)
String include() default "all"; (3)
)
(1) usage: 给定缓存的并发策略(NONE, READ_ONLY, NONSTRICT_READ_WRITE, READ_WRITE, TRANSACTIONAL)
(2) region (可选的):缓存范围(默认为类的全限定类名或是集合的全限定角色名)
(3) include (可选的):值为all时包括了所有的属性(proterty), 为non-lazy时仅含非延迟属性(默认值为all)
E load默认使用二级缓存,iterator默认使用二级缓存
Flist默认向二级缓存添加数据,但是查询的时候不使用.
G 如果Query需要使用二级缓存,则打开查询缓存
<property name="cache.use_query_cache">true</property>
需要调用Query setCachable(true)方法指明使用二级缓存.
标签:
原文地址:http://blog.csdn.net/rongxiang000/article/details/51860092