day1
一. hibernate数据持久化组件
对象持久化(Object Persistence):把数据保存在永久存储介质中(数据库)
1.为什么要持久化:
a.内存是暂时存储设备,断电后数据易丢失
b.网络传输无法传输内存中的对象,需要将对象外化
c.内存中数据查询,组织不方便
d.内存只能存储少量数据
2.怎样持久化
a.对象序列化 --> 二进制流
合并存储,粒度大,无规律
不支持检索
只适合少数个别对象的序列化
b.用JDBC/EJB/ORM 将数据存入数据库
用JDBC:(Java DB Connection)
优点:底层开发,控制力强(细); 效率最高; 标准的(SQL)JDBC,有可移植性
缺点:过于复杂; 代码量大; 可维护性差(代码重用性低);
用EJB:(Entity Java Bean)
优点:直接自动生成JDBC代码; 持久对象(PO)的状态由服务器管理; 声明式的事务
缺点:功能不全(特殊的组件,不能做继承关系); EJB容器是侵入性容器,失去OO的优点; 调试更复杂
用ORM:(object relation mapping)对象关系映射
优点:自动生成JDBC(代码量下降); 使用(plain oldest java object---pojo),非侵入型; 提供状态管理; 难度下降,不需要容器
缺点:由于开源, 文档少; bug多; 技术支持差
结论: 用java开发-->必须将数据持久化-->用数据库持久化-->须用ORM-->需要用Hibernate
二. Hibernate
1.POJO类
plain oldest java object,就是标准的Java Bean。
2.Hibernate
Hibernate -->一个可以自动的根据xml完成对象关系映射,并持久化到数据库的开源组件。其底层也是由JDBC实现的。hibernate是通过xml文件的配置,对数据库的底层的方言,以及数据库连接所需的信息,以及连接数据库的驱动。
hibernate的系统配置文件
hibernate.cfg.xml -->与数据库建立连接(一般放在项目的根目录下)
XXXX.hbm.xml-->用来建立类与表之间的映射关系(一般将映射类的xml文件和实体类放在一起)
3.Hibernate API
Configuragion 读配置文件(默认名:hibernate.cfg.xml) (org.hibernate.cfg.Configuration)
生成SessionFactory:SessionFactory 重量级的对象, 线程安全的 (org.hibernate.SessionFactory),生成Session .
Session,相当于JDBC中的Connection (org.hibernate.Session),轻量级的对象,线程不安全(原则上一个线程一个Session,不要放在并发的环境中)
生成Transaction
Transaction 管理事务的对象 (org.hibernate.Transaction)
Query 查询对象,提供面向对象的查询语言(HQL)
4.使用hibernate编程步骤
1,配置环境,加载hibernate的jar文件,以及连接数据库连接使用的jar文件,并配置CLASSPATH环境变量。
2,写hibernate所需的配置文件,hibernate.cfg.xml ,XXX.hbm.xml
3,写POJO类
4,调用hibernate API。
1)使用Configuration对象的buildSessionFactory()方法创建SessionFactory对象
2)使用SessionFactory对象openSession()方法创建Session对象。
3)使用Session的相应方法来操作数据库,将对象信息持久化到数据库。
5. HQL语句
Query q = s.createQuery("from Account where actNo=:actNo");
//from 类名 where 属性名=:shuxingming
q.setLong("actNo",12345);//设置属性名,并赋值
q.uniqueResult()//获得匹配HQL的唯一属性
附: 1.环境配置:
a. 类库
*** 导入hibernate 库文件 ***
1、在eclipse中建立一个java工程,如:hbn
2、在工程hbn的属性--Build Path中选择Add Libraries
3、在Add Libraries窗口中选择User Library点next按钮
4、在User Library窗口中点User Libraries ...按钮
5、在User Libraries窗口中,点new按钮
6、在New user library窗口中,填写一个User libary name(如:hbnlib,注意不要选择System libary 复选框),点OK按钮回到User Liberies窗口
7、在User Liberaries窗口中选择刚刚创建的hbnlib,点Add jars按钮
8、在打开的文件选择窗口中找到解压好的hibernate的库文件,全部选中,点"打开"按钮,返回User Libraries窗口
9、在User Libraries窗口中点OK按钮,返回User Library 窗口;再点Finish按钮,结束操作
b. 驱动
c. 映射文件/配置文件 的模板
d. Eclips导入2个DTD文件
*** 导入hibernate 库文件 ***
1、在eclipse中建立一个java工程,如:hbn
2、在工程hbn的属性--Build Path中选择Add Libraries
3、在Add Libraries窗口中选择User Library点next按钮
4、在User Library窗口中点User Libraries ...按钮
5、在User Libraries窗口中,点new按钮
6、在New user library窗口中,填写一个User libary name(如:hbnlib,注意不要选择System libary 复选框),点OK按钮回到User Liberies窗口
7、在User Liberaries窗口中选择刚刚创建的hbnlib,点Add jars按钮
8、在打开的文件选择窗口中找到解压好的hibernate的库文件,全部选中,点"打开"按钮,返回User Libraries窗口
9、在User Libraries窗口中点OK按钮,返回User Library 窗口;再点Finish按钮,结束操作
2. hibernate.cfg.xml的写法
<?xml version="1.0"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<!-- 与数据库建立连接 -->
<hibernate-configuration>
<session-factory>
<property name="show_sql">true</property><!--显示sql语句-->
<property name="format_sql">true</property><!--使显示的sql语句格式化-->
<property name="hbm2ddl.auto">create</property><!--如果原表存在则先删原表再创建新表-->
<property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="connection.url">jdbc:oracle:thin:@10.10.3.237:1521:tarena</property>
<property name="connection.username">openlab</property>
<property name="connection.password">open123</property>
<property name="connection.isolation">2</property><!-- 设置事务隔离级别(2表示避免脏读) -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property><!-- 使用的数据库方言信息 -->
<mapping resource="com/tarena/biz/entity/Account.hbn.xml"/><!-- 映射文件的位置,和实体类放在同一目录 -->
</session-factory>
</hibernate-configuration>
3. hibernate的映射类的XXXX.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.tarena.biz.entity"><!--映射类所在的包-->
<!-- 类名和表名的映射 -->
<class name="Account" table="account_hz">
<!-- 属性和字段的映射 -->
<id name="aid" column="aid"><!--主键生成策略-->
<generator class="hilo"><!—主键产生方式:高低位算法->
<param name="table">pk_table</param>
<param name="column">id_value</param>
</generator>
</id>
<property name="actNo" column="actNo"></property>
<property name="balance" column="balance"></property>
<!--在hibernate中其他类型可以自动识别只有Data类型必须指名-->
</class>
</hibernate-mapping>
4. 创建数据库表结构:
create account_hz(aid number(20) primary key,
actNo varchar(50) not null unique,
balance number(12,2) not null);
create t_hilo(hi number(20));
insert into t_hilo values(1);//必须事先插入一条记录到表
5. 调用hibernate API
public class Test {
public static void main(String[] args) {
Account act = null;
//step1: 创建Configration对象
Configuration config = new Configuration().configure();
//step2: 获取SessionFactorye
SessionFactory sf = config.buildSessionFactory();
//step3:获取Session和Transaction,进行持久化操作
Session s = null;
Transaction tran = null;
try {
s = sf.openSession();
tran = s.beginTransaction();//启动事务
act = new Account(12345, 2000.00);
s.save(act);//将帐户对象持久化
tran.commit();//提交事务
} catch (Exception e) {
if(tran != null){
tran.rollback();
e.printStackTrace();
} finally{
if(s!=null) s.close();
}
}
}
6.Util工具类:
public class HbnUtilV1 {
private static SessionFactory sf;
static{
Configuration config = null;
try {
config = new Configuration().configure();
sf = config.buildSessionFactory();
} catch (Exception e) {
e.printStackTrace();
}
}
public static Session getSession(){
Session s = null;
if(sf!=null)
s = sf.openSession();
return s;
}
}
day3
一. one-to-many
1. one端set标签的设置
<set name="acts" cascade="all" inverse="true"><!-- 让one端(user)将关系的维护方交给另一端:many端(Account) -->
<key column="fid"></key>
<one-to-many class="Account"/><!-- 指定集合中存放的是Account类 -->
</set>
注:inverse="true"就是在设置如果在内存中的修改或添加了这个集合中的某一个或某几个对象他不会将全部集合的信息同步到数据库,而是只将集合中被修改的对象重新同步到数据库。
2. many端
<many-to-one name="user" column="fid" cascade="save"><!-- 设置级联操作 -->
</many-to-one>
二.状态
1. 持久对象的状态-->PO外在的状态 (OOAD中 对象的状态-->PO内在的状态)
Transient 暂时状态: po 和Session无关,数据库中也无该记录
Persistent 持久状态: po 和Session有关,数据库中有该记录
Detached 游离/脱管态:po和Session无关,数据库中有该记录
注:和Session关系,即Session中有该对象的副本和该对象的引用
持久化对象: 即就是在数据库中存有其相对应数据的对象,并且在内存中也有这个对象,内部状态和外部状态同步, 这个对象在Session的管理范围内,也就是调用Session.save()方法同步到数据库的对象。
临时对象: 即在内存中刚刚创建(new)的对象,还没有同步到数据库,或者是调用Session.delete(),数据库中信息被删除了的对象。
游离对象: 也就是在数据库中有和该对象向对应的纪录,并且在内存中的也存在该对象,但是不在Session的管理范围之内,如在Session关闭之后,就成了游离对象,就不会在将其改变同步到数据库中.
内在状态-->外在化? 是-->持久态 --否-->暂态
(类属性) (表记录) --是-->Session同步?否-->游离态
2. 状态转换: _|_-->close():关闭Session/clear()清空缓存内对象/evict()清除某个持久对象
save() / saveOrUpdate() close()/clear()/evict()
Transient--------------------->Persistent----------------------->Detached
(临时态)<---------------------(持久态)<----------------------- (游离态)
delete() update()/saveOrUpdate()/lock()
三. 级联操作cascade
cascade属性是设置级联操作的也就是在操作一端的数据如果影响到多端数据时会进行级联操作.
1. none: 就是只对本对象进行操作,不使用级联操作,默认级联是none。
2. save-update: 也就是只有对象保存操作(持久化操作)或者是持久化对象的更新操作,才会级联操作关联对象(子对象),one2many关系中多的那一方会使用;
3. delete: 对持久化对象的删除操作时会进行级联操作关联对象(子对象)。
4. all: 对持久化对象的所有操作都会级联操作关联对象(子对象)。
5. all-delete-orphan: 在多端进行删除操作时,会在多端表中留下null空纪录,设置了级联操作为delete之会将表中表示关联的外键id置成null,不会将这条纪录也删除掉,而把级联设置成delete-orphan就不会留有空纪录,而是级联的把相关纪录删除掉。(级别最深)
cascade="save-update,delete-orphan"
四. 批量更新(Batch update)
1. session.flush()和session.clear()联合使用
2. batch-size: 这个属性写在set标签中,代表批量加载,也就是在加载一端的集合属性时会一次加载指定的数量的对象,而不是默认的一个一个的加载,会提高效率,批量加载只能用于延迟加载和立即加载策略,也就是(lazy="true"或者lazy="false")。
注: lazy="true" 延迟加载,所谓的延迟加载,就是对一端的集合属性的加载策略,就是在不使用到集合中的对象的数据就不会真正的加载集合中的对象数据,而是家在一个代理对象就相当于的一个空的容器。这也就是会出现LazyInitializationException异常,也就是没有初始化这个代理的集合对象,在事先查询到了集合中的对象就会初始化这个对象,如果Session没有关闭就会在查询加载集合中的对象信息,如果提前关闭了Session,当使用集合中的对象信息时就会有这个异常。
五. Hibernate控制的事务(ACID,atomicity consistency isolation durability)
事务保证原子操作的不可分,也就是操作的同时成功或同时失败。
Transaction tran=session.beginTranaction();
//....事务操作
tran.commit();
tran.rollback();
以上是事务对象的方法,来实现对事务的支持。
六、hibernate的事务隔离级别
hibernate的事务隔离级别和JDBC中大致相同。
设置时要在hibernate.cfg.xml配置
<property name="hibernate.connection.isolation">4</property>
1,读未提交的数据(Read uncommitted isolation)
2,读已提交的数据(Read committed isolation)
4,可重复读级别(Repeatable read isolation)
8,可串行化级别(Serializable isolation)
七、hibernate的锁(悲观锁,乐观锁)
1. 悲观锁: 是由数据库本身所实现的,会对数据库中的数据进行锁定,也就是锁行,
在同一时间内只能有一个读写数据操作。
LockMode.UPGRADE,修改锁,在get()方法中加上这个设置作为第三个参数。
LockMode.NONE 无锁机制
LockMode.READ 读取锁(JDBC中的for update)
LockMode.WRITE 写入锁,不能在程序中直接使用
例如:tran = s.beginTransaction();
user = (User)s.get(User.class , userid , LockMode.UPGRADE);
user.setName("new name");
tran.commit();
还可以使用Session.lock() Query.setLockMode() Criteria.setLockMode()方法来设置锁
2. 乐观锁: 也就是通过对记录加上某些信息来解决并发访问的问题。
解决冲突的手段: 加上版本versionNo
if(read_versionNo=versionNo) write versionNo++ else do it again
<version name="version"/>必须紧跟在<id>之后
persist就只是将级联对象也持久化到数据库。
day4
一. many-tomany(以学生选课为例)
1.Student.hbm.xml文件的设置
<set name="students" table="ying_enrollment" cascade="none"><!--cascade 设置为none,为了减少update更新冗余--!>
<key column="fcid"></key>
<many-to-many class="Student" column="fsid"/>
<!-- table ying_enrollment 中间表,体现多2多关系 -->
</set>
2.Course.hbm.xml文件的设置
<set name="courses" table="ying_enrollment" cascade="save-update" inverse="true"
lazy="false"><!-- lazy表示是否延迟加载set对象,false使Session关闭后仍然可用 -->
<key column="fsid"></key>
<many-to-many class="Course" column="fcid"/>
<!-- table ying_enrollment 中间表,体现多2多关系 -->
</set>
在Student实体类中要设置Student与Course的关联:
public void registerCourse(Course c) {
courses.add(c);
c.getStudents().add(this);
}
3.注: hibernate的实现方式是通过中间表(ying_enrollment),间接实现了多对多关系,实际上也是将多对多拆分
成两个双向的一对多关系。
Hibernate知识点小记
1.cascade
cascade属性是设置级联操作的也就是在操作一端的数据如果影响到多端数据时会进行级联操作,一对一的时候直接
写在标签上,其他的要写在set标签上
cascade="none|save-update|all|all-delete-orphan"
none 就是不使用级联操作,默认级联是none。
save-update 也就是只有对象保存操作(持久化操作)或者是持久化对象的更新操作,才会级联操作关联对象(子对象)。
all 对持久化对象的所有操作都会级联操作关联对象(子对象)。
all-delete-orphan,在多端进行删除操作时,会再多端表中留下null空纪录,设置了级联操作为delete之会将表中表示关联的外键id置成null,
不会将这条纪录也删除掉,而把级联设置成delete-orphan就不会留有空纪录,而是级联的把相关纪录删除掉。
2.inverse
inverse="true"就是在设置如果在内存中的修改或添加了这个集合中的某一个或某几个对象他不会将全部集合的信息同步到数据库,
而是只将集合中被修改的对象重新同步到数据库
3.lazy
lazy=“true”
延迟加载,所谓的延迟加载,就是对一端的集合属性的加载策略,就是在不使用到集合中的对象的数据就不会真正的加载集合中的对象数据,
而是家在一个代理对象就相当于的一个空的容器。这也就是会出现LazyInitializationException异常,也就是没有初始化这个代理的集合对象,
在事先查询到了集合中的对象就会初始化这个对象,如果Session没有关闭就会在查询加载集合中的对象信息,如果提前关闭了Session,
当使用集合中的对象信息时就会有这个异常。
4.fetch
fetch=“join”,这就是使用了预先抓取策略,也就是针对关联的对象的加载策略,在使用到关联对象的信息时会再发送sql语句,
如果不使用fetch=“join”,就会不使用表连接而是先查出一端的关联id再一条一条的发送sql语句查询到关联对象信息,使用了fetch=“join”
就会使用表连接将关联对象信息直接查寻出来的。fetch=“lazy”这个是默认的设置。
二. inherit_mapping
建表策略(以Account CheckAccount DebitAccount 类为例)
(1)只为具体类建表
只为具体类建表,使用于不使用多态的情况下,具体类之间没有继承关系时适用
需要针对每个类写映射配置文件,就和普通的单表映射的xml文件相同。
也可以使用一个xml文件来进行映射,可以通过写union-subclass标签来表现其关系
这里不能使用id生成策略中的native,而是要指定特定的生成策略。
例:
<union-subclass name="CheckAccount"
table="t_check_account"><!-- 只为具体类建表 -->
<property name="overdraft"></property>
</union-subclass>
<union-subclass name="DebitAccount"
table="t_debit_account"><!-- 只为具体类建表 -->
<property name="currency"></property>
</union-subclass>
(2)每个类建一个表
每个类建一个表,可以有效减少数据的冗余,减少字段,查询效率不很高。
正对每个类建一个表,只要写一个配置文件来进行类的映射即可
映射文件中的子类可以使用join-subclass标签来表示,并且引用父类的主键作为共享主键,就是不需要指定id生成策略
例:
<joined-subclass name="CheckAccount"
table="ying_check_account"><!-- 每一个类建一个表 -->
<key column="faid"></key>
<property name="overdraft"></property>
</joined-subclass>
<joined-subclass name="DebitAccount"
table="ying_debit_account"><!-- 每一个类建一个表 -->
<key column="faid"/>
<property name="currency"></property>
</joined-subclass>
(3)所有类建一个表
所有类只建一个表,查寻效率比较高,但是会产生很多空间浪费,当子类中有非空约束,
就不大适用了,这是对于子类可以使用subclass标签表示。
例:
<subclass name="CheckAccount"
discriminator-value="c">
<property name="overdraft"></property>
</subclass>
<subclass name="DebitAccount"
discriminator-value="d">
<property name="currency"></property>
</subclass>
注: 不考虑多态时,最好是用只针对具体类建表(2个表),而考虑多态时尽量使用所有类建一个表,
只有当子类中的属性过多是才考虑每个类建一个表的策略。
(4)三种策略的比较:(详见孙卫琴主编的精通Hibernate一书P424)
如果不需要支持多态查询和多态关联,可以采用每个具体类对应一个表
如果需要持多态查询和多态关联,并且子类包含的属性很多,可以采用所有类对应一个表
如果需要持多态查询和多态关联,并且子类包含的属性很多,可以采用每个类对应一个表
day5
一. 组件映射:
把持久类换成非持久类保存在数据库中;
一个类如果有一个独立的oid,则该类是一个持久类,否则是一个组件.
目的: 简化操作
适应情况:
两个类之间具有组合(或强度较小的聚合)关系
组合/聚合
PO类(整体)<------------->组件类(部分)
1:1/1:m
| 组件映射(1:1)
------->
集合映射(1:m)
组件是退化的PO;
oid消失
组件映射是退化的基数映射
只要有可能,就要用组件映射
例如:(以Account Address类为例,只需要写一个Account.hbm.xml)
<!-- 将Set中的所有Address类作为自己的一个组成部分-->
public class Account {
private Long aid;
private long actNo;
private double balance;
private Address address;
}
<set name="address" table="yinglh_address">
<key column="fid"></key>
<composite-element class="Address">
<property name="zipcode"></property>
<property name="city"></property>
<property name="street"/>
</composite-element>
</set>
二. 集合映射
1.集合比较
集合 顺序 重复
Set 无 否
List 有 能
Map 有 key(否)
bag 无 能
1、set映射
关联对象的属性除了外键之外,只有1、2个属性,那么就可以使用set映射
使用了set标签的element元素,不用创建关联对象就可以实现单向一对多的关联关系
public class Room implements Serializable{
private int id;
private String roomNumber;
private Set<String> equipments = new HashSet<String>();
private Set<Image> images = new HashSet<Image>();
}
<set name="equipments" table="equipment_set">
<key column="roomid" foreign-key="fk_equip_room_set"/>
<element column="description" type="string" length="128" not-null="true"/>
</set>
<set name="images" table="image_set">
<key column="roomid" foreign-key="fk_img_room_set"/>
<composite-element class="Image">
<property name="path" column="path" type="string" length="50" not-null="true"/>
<property name="width" column="width" type="integer" />
<property name="height" column="height" type="integer" />
</composite-element>
</set>
2、map映射
非常有用的一种集合映射
public class Room implements Serializable{
private int id;
private String roomNumber;
private Map<String, String> equipments = new HashMap<String, String>();
private Map<String, Image> images = new HashMap<String, Image>();
}
<map name="equipments" table="equipment_map">
<key column="roomid" foreign-key="fk_equip_room_map"/>
<map-key column="name" type="string" length="15" />
<element column="description" type="string" length="128" not-null="true"/>
</map>
<map name="images" table="image_map">
<key column="roomid" foreign-key="fk_img_room_map"/>
<map-key column="name" type="string" length="15" />
<composite-element class="Image">
<property name="path" column="path" type="string" length="50" not-null="true"/>
<property name="width" column="width" type="integer" />
<property name="height" column="height" type="integer" />
</composite-element>
</map>
3.list映射
public class Quiz {
private Long oid;
private String quizNo;
private List answers = new ArrayList();
}
<list name="answers" table="yinglinhai_answers">
<key column="fid"></key>
<list-index column="indexs"></list-index>
<element column="answer" type="string"></element>
</list>
4.bag映射
public class Lab {
private Long oid;
private int labNo;
private List records = new ArrayList();
}
<idbag name="records" table="yinglinhai_records">
<collection-id type="long" column="cid">
<generator class="increment"></generator>
</collection-id>
<key column="fid"/>
<element column="record" type="string"></element>
</idbag>
三、HQL
1、query
(1) session.createQuery("from User");
session.createCriteria(User.class);
(2)查询的分页显示
query.setFirstResult(0);
query.setMaxResult(10);
(a) MySql
select * from student where limite ?,?
(b) Oracle
select * from (select row_.* ,rownum rownum_
from (select student0_.id as id0_,
from student student0_) row)
where rownum_ <= ?
and rownum_ > ?
(3)在配置文件中的query
<query name="findByName">
<![CDATA[ --- 当作字符串看,不做特殊处理
from User u where u.namelike :name
]]>
</query>
session.getNamedQuery("findByName").setString("name",name);
from User; 返回对象数组,不可以强转
select u from User u; 返回对象集合,对于集合中的每个元素都可以强转成User对象
2、Dynamic query
public Collection findStudents(Student student){
StringBuffer sb = new StringBuffer("select student from Student student where student.id>-1 ");
--- student.id>-1 永远为真,再添加条件的时候都用and即可,
避免判断用where还是and
if(student.getName() != null)
sb.append("and student.name=:name");
if(student.getBirthday() != null)
sb.append("and student.birthday=:birthday");
if(student.getEmail() != null)
sb.append("and student.email=:email");
……
}
3、QBE
属性多时,效率高,语句少
忽略主键、version、关系(查询当前对象)
与QBC结合,弥补id查询
Example.excludeProperty("param"); --- 查询时忽略某个属性