Hibernate属于ORM中间件,使用JDBC进行数据的CRUD操作会有重复的ResultSet的get和set操作,这是由于阻抗不匹配所造成的。而是用Hibernate则不会出现此问题,Hibernate对JDBC进行了轻量的封装,所以可以通过它直接进行JDBC操作。它是一种为了解决面向对象与面向关系数据库互不匹配而出现的技术!
Hibernate是以“对象”为操作的基本单元,而非关系数据库的
数据表,
数据库表可以通过Hibernate进行生成,也可以通过数据表进行反向生成对象。
Hibernate的的基本映射数据类型:
映射关系如下:
JAVA类型 Hibernate的映射数据类型 标准SQL类型
Hibernate的
主键映射:
主键分为自然主键和代理主键之分,自然主键就是充当主键的字段本身具有一定的含义,是构成记录的组成部分,比如学生的学号;而代理主键的字段本身不具有业务含义,只起主键的作用,比如自动增长的ID。Hibernate推荐使用代理主键。
Hibernate是使用对象标识符(OID)来区分不同的持久对象,而OID则是通过Hibernate内置的标识对象生成器来生成的。
Assigned:OID友业务逻辑程序产生,Hibernate只负责持久化,用于自然主键
Increment:OID由Hibernate依递增的方式产生,该
算法依赖保存于当前应用实例中的一个最大值的变量,当有多个应用实例需要访问数据库时,难免出现重复的主键,应当
谨慎使用。
Identity:OID由底层数据库的自增主键生成机制产生。
Sequence:OID由底层数据库的sequence自增主键生成方式产生
Native:根据底层数据库对自增主键生成OID能力的支持,具体选择相应的生成器,常用于
跨平台!(提高可移植性)
关联关系:
一对一映射:一对一关联关系实现有两种方式,一种是通过共享主键的方式,一种是通过唯一
外键方式。
共享主键的方式一对一:
限制两个数据表的主键使用相同的值,通过主键形成一对一的映射关系
Company类:
class="java">private Integer id;
private String companyname;
private String linkman;
private String telephone;
private String email;
private Login login;
映射文件:
<class name="cn.changluo.Login" table="login" catalog="test">
<id name="id" type="integer">
<column name="id" length="4"/>
<generator class="foreign">
<param name="property">company</param>
</generator>
</id>
<property name="loginname" type="string" column="loginname"/>
<property name="loginpwd" type="string" column="loginpwd"/>
<one-to-one name="company" class="cn.changluo.Company" constrained="true"></one-to-one>
</class>
Login类:
private Integer id;
private String loginname;
private String loginpwd;
private Company company;
映射文件:
<class name="cn.changluo.Company" table="company" catalog="test">
<id name="id" type="integer">
<column name="id" length="4"/>
<generator class="identity"></generator>
</id>
<property name="companyname" type="string" column="companyname"/>
<property name="email" type="string" column="email"/>
<property name="linkman" type="string" column="linkman"/>
<property name="telephone" type="string" column="telephone"/>
<one-to-one name="login" class="cn.changluo.Login" cascade="all" lazy="false"></one-to-one>
</class>
测试:
public static void main(String[] args) {
save();
query();
}
public static void save(){
Configuration cfg = new Configuration();
cfg.configure("hibernate.cfg.xml");
SessionFactory sf = cfg.buildSessionFactory();
Session session = sf.openSession();
Transaction tx = session.beginTransaction();
Company c = new Company();
c.setCompanyname("gouyundong");
c.setEmail("33333333@qq.com");
c.setLinkman("zhangsan");
c.setTelephone("13329902303");
Login l = new Login();
l.setLoginname("LL");
l.setLoginpwd("ll");
c.setLogin(l);
l.setCompany(c);
session.save(c);
session.save(l);
tx.commit();
}
public static void query(){
Configuration cfg = new Configuration();
cfg.configure("hibernate.cfg.xml");
SessionFactory sf = cfg.buildSessionFactory();
Session session = sf.openSession();
Company c = (Company) session.get(Company.class, 1);
System.out.println(c.getCompanyname());
}
显示的SQL语句:
Hibernate: insert into test.company (companyname, email, linkman, telephone) values (?, ?, ?, ?)
Hibernate: insert into test.login (loginname, loginpwd, id) values (?, ?, ?)
Hibernate: select company0_.id as id10_1_, company0_.companyname as companyn2_10_1_, company0_.email as email10_1_, company0_.linkman as linkman10_1_, company0_.telephone as telephone10_1_, login1_.id as id11_0_, login1_.loginname as loginname11_0_, login1_.loginpwd as loginpwd11_0_ from test.company company0_[b] left outer join [/b]test.login login1_ on company0_.id=login1_.id where company0_.id=?
一对一关联在默认的情况下采用的是迫切做外连接的检索策略。
唯一外键的方式一对一:一个表的外键和另一个表的唯一主键对应,形成一对一的映射关系,这种一对一的关系其实就是多对一关系的一种特殊情况
Address类:
private Integer id;
private String province;
private String city;
private String street;
private String zipcode;
private Client client;
映射文件:
<class name="cn.changluo.Address" table="address" catalog="test">
<id name="id" type="integer">
<column name="id" length="4"/>
<generator class="identity"></generator>
</id>
<property name="province" type="string" column="province"/>
<property name="city" type="string" column="city"/>
<property name="street" type="string" column="street"/>
<property name="zipcode" type="string" column="zipcode"/>
<one-to-one name="client" class="cn.changluo.Client" property-ref="address"></one-to-one>
</class>
Client类:
private Integer id;
private String clientname;
private String phone;
private String email;
private Address address;
映射文件:
<class name="cn.changluo.Client" table="client" catalog="test">
<id name="id" type="integer">
<column name="id" length="4" />
<generator class="identity"></generator>
</id>
<property name="clientname" type="string" column="clientname" />
<property name="email" type="string" column="email" />
<property name="phone" type="string" column="phone" />
<many-to-one name="address" class="cn.changluo.Address"
column="address" lazy="false" cascade="all" unique="true">
</many-to-one>
</class>
测试:
public static void main(String args[]){
save();
}
public static void save(){
Configuration cfg = new Configuration();
cfg.configure("hibernate.cfg.xml");
SessionFactory sf = cfg.buildSessionFactory();
Session session = sf.openSession();
Transaction tx = session.beginTransaction();
Address a = new Address();
a.setProvince("FUJIAN");
a.setCity("XM");
a.setStreet("GAOXIONG road");
a.setZipcode("361000");
Client c = new Client();
c.setClientname("changluo");
c.setEmail("changluo@qq.com");
c.setPhone("8344793");
c.setAddress(a);
a.setClient(c);
session.save(c);
session.save(a);
tx.commit();
}
多对一单项关联关系:
Customer类:
private Integer id;
private String cname;
private String bank;
private String phone
;
映射文件:
<class name="cn.changluo.Customer" table="customer"
catalog="test">
<id name="id" type="integer">
<column name="id" length="4" />
<generator class="identity"></generator>
</id>
<property name="cname" type="string" column="cname" />
<property name="bank" type="string" column="bank" />
<property name="phone" type="string" column="phone" />
</class>
Order类:
private Integer id;
private String orderno;
private double money;
private Customer customer;
映射文件:
<class name="cn.changluo.Order" table="order" catalog="test">
<id name="id" type="integer">
<column name="id" length="4" />
<generator class="identity"></generator>
</id>
<property name="orderno" type="string" column="orderno" />
<property name="money" type="double" column="money" />
<many-to-one name="customer" column="customer_id"
class="cn.changluo.Customer" lazy="false" not-null="true">
</many-to-one>
</class>
测试:
Configuration cfg = new Configuration();
cfg.configure("hibernate.cfg.xml");
SessionFactory sf = cfg.buildSessionFactory();
Session session = sf.openSession();
Transaction tx = session.beginTransaction();
Customer c = new Customer();
c.setBank("GSYH");
c.setCname("LSL");
c.setPhone("0234403");
Order o = new Order();
o.setMoney(669);
o.setOrderno("208203");
o.setCustomer(c);
session.save(c);
session.save(o);
tx.commit();
多对一双向关联:
Customer类:
private Integer id;
private String cname;
private String bank;
private String phone;
private Set orders = new HashSet();
映射文件:
<class name="cn.changluo.Customer" table="customer"
catalog="test">
<id name="id" type="integer">
<column name="id" length="4" />
<generator class="identity"></generator>
</id>
<property name="cname" type="string" column="cname" />
<property name="bank" type="string" column="bank" />
<property name="phone" type="string" column="phone" />
<set name="orders" cascade="all" inverse="true" lazy="false">
<key column="customer_id"></key>
<one-to-many class="cn.changluo.Order"/>
</set>
</class>
Order类和Order的映射文件同单项映射一致
测试:
Configuration cfg = new Configuration();
cfg.configure("hibernate.cfg.xml");
SessionFactory sf = cfg.buildSessionFactory();
Session session = sf.openSession();
Transaction tx = session.beginTransaction();
Customer c = new Customer();
c.setBank("GSYH");
c.setCname("LSL");
c.setPhone("0234403");
Order o = new Order();
o.setMoney(669);
o.setOrderno("208203");
o.setCustomer(c);
Order o1 = new Order();
o1.setMoney(390);
o1.setOrderno("oisd");
o1.setCustomer(c);
Set orders = new HashSet();
orders.add(o);
orders.add(o1);
c.setOrders(orders);
session.save(c);
session.save(o);
tx.commit();
映射一对多双向自身关联关系:
参与关联的双方都是同一个持久化类
Goodscate类:
private Integer id;
private String cateno;
private String catename;
private Goodscate parent;
private Set childs = new HashSet();
映射文件:
<class name="cn.changluo.Goodscate" table="goodscate"
catalog="test">
<id name="id" type="integer">
<column name="id" length="4" />
<generator class="identity"></generator>
</id>
<property name="cateno" type="string" column="cateno" />
<property name="catename" type="string" column="catename" />
<many-to-one name="parent" column="parentid"
class="cn.changluo.Goodscate" lazy="false">
</many-to-one>
<set name="childs" cascade="all" lazy="false" inverse="true">
<key><column name="parentid"></column></key>
<one-to-many class="cn.changluo.Goodscate"/>
</set>
</class>
测试:
Configuration cfg = new Configuration();
cfg.configure("hibernate.cfg.xml");
SessionFactory sf = cfg.buildSessionFactory();
Session session = sf.openSession();
Transaction tx = session.beginTransaction();
Goodscate pgs = new Goodscate();
pgs.setCateno("001");
pgs.setCatename("no1");
Goodscate cgs = new Goodscate();
cgs.setCateno(pgs.getCateno()+"_"+001);
cgs.setCatename("no101");
pgs.getChilds().add(cgs);
cgs.setParent(pgs);
session.save(pgs);
session.save(cgs);
tx.commit();
多对多单项关联
多对多关联在关系数据库中不能直接实现,还必须依赖一张连接表,用于保存这种关联关系
Items类:
private Integer id;
private String itemno;
private String itemname;
映射文件:
<class name="cn.changluo.Items" table="items" catalog="test">
<id name="id" type="integer">
<column name="id" length="4"/>
<generator class="identity"></generator>
</id>
<property name="itemno" type="string" column="itemno"/>
<property name="itemname" type="string" column="itemname"/>
</class>
Orders类:
private Integer id;
private String orderno;
private double money;
private Set items = new HashSet();
映射文件:
<class name="cn.changluo.Orders" table="orders" catalog="test">
<id name="id" type="integer">
<column name="id" length="4"/>
<generator class="identity"></generator>
</id>
<property name="orderno" type="string" column="orderno"/>
<property name="money" type="double" column="money"/>
<set name="items" table="selectitems" lazy="true" cascade="save-update">
<key column="orderid"></key>
<many-to-many class="cn.changluo.Items" column="itemid"></many-to-many>
</set>
</class>
测试类:
Configuration cfg = new Configuration();
cfg.configure("hibernate.cfg.xml");
SessionFactory sf = cfg.buildSessionFactory();
Session session = sf.openSession();
Transaction tx = session.beginTransaction();
Items i1 = new Items();
i1.setItemname("车");
i1.setItemno("car");
Items i2 = new Items();
i2.setItemname("食物");
i2.setItemno("food");
Set items = new HashSet();
items.add(i1);
items.add(i2);
Orders o = new Orders();
o.setMoney(200);
o.setOrderno("2220");
o.setItems(items);
session.save(i1);
session.save(i2);
session.save(o);
Hibernate的检索策略:
立即检索:就是立即装载和初始化检索方法指定的对象,即使session关闭了,依然可以被正常访问。
在Hibernate3.0中Lazy的默认值为“true”,即为延迟加载
在类级别中推荐使用立即检索,这样可以立即访问检索出来的对象,而在关联级别推荐使用延迟检索策略,因为在加载了持久化对象后,大多不会立即访问其关联对象,况且当关联对象巨大时,将其装载进
内存开销也很大。
<class name="cn.changluo.Customer" table="customer"
catalog="test" lazy="false">
<id name="id" type="integer">
<column name="id" length="4" />
<generator class="identity"></generator>
</id>
<property name="cname" type="string" column="cname" />
<property name="bank" type="string" column="bank" />
<property name="phone" type="string" column="phone" />
<set name="orders" cascade="all" inverse="false" lazy="false">
<key column="customer_id"></key>
<one-to-many class="cn.changluo.Order"/>
</set>
</class>
当关联关系中的lazy属性值,设置为false时,当装载Customer后,会立即装载与当前Customer对象一对多管理啊你的所有Order对象;
当加载Order对象时,可以看到执行了多个select语句,这样会影响效率,为了减少查询次数,使用一条SQL语句执行查询,可以使用Hibernate的批量检索功能!
<set name="orders" cascade="all" inverse="true" lazy="false" batch-size="5">
<key column="customer_id"></key>
<one-to-many class="cn.changluo.Order"/>
</set>
设置batch-size来设置批量检索的大小,这个值依据系统的实际情况进行设置,当设置了批量检索的值之后,可以
发现仅执行了一条SQL语句:
select orders0_.customer_id as customer4_1_, orders0_.id as id1_, orders0_.id as id7_0_, orders0_.orderno as orderno7_0_, orders0_.money as money7_0_, orders0_.customer_id as customer4_7_0_ from test.order orders0_ [b]where orders0_.customer_id in (?, ?)[/b]
不管<class>标签中的lazy属性为何值,当使用Session实例的get()方法装载持久化对象时,均使用立即检索
延迟检索:是Hibernate3.0默认的检索策略,此策略就是等到访问的时候才装载和初始化指定对象。如果Session实例关闭之前没有初始化延迟检索出来的对象,Session实例关闭后再访问时,就会抛出
异常。
当关联对象设置为lazy=true时,当session关闭时,需要访问关联对象,就需要在session关闭之前进行初始化
<set name="orders" cascade="all" inverse="true" lazy="true" batch-size="5">
<key column="customer_id"></key>
<one-to-many class="cn.changluo.Order"/>
</set>
Customer cust = (Customer) session.get(Customer.class, 1);
if(!Hibernate.isInitialized(cust.getOrders())){
Hibernate.initialize(cust.getOrders());
}
session.close();
要在Session范围内进行初始化。
迫切左外连接检索:迫切左外连接检索就是充分利用SQL的外连接查询功能,减少Select语句的数据,提高检索效率
<set name="orders" cascade="all" inverse="false" outer-join="true">
<key column="customer_id"></key>
<one-to-many class="cn.changluo.Order"/>
</set>
当使用session.createQuery(“from Customer as a”).list()查询所有的Customer对象时,迫切左外连接检索策略将被忽略而失效,检索策略有lazy属性决定。
Hibernate的事务控制:
在
实际应用过程中,隔离级别越高,越能保证数据的完整性和一致性,但是对冰法性能的影响也越大,对于大多数应用程序,可以优先考虑把数据库系统的隔离级别设置为ReadCommitted,它能够避免脏读,而且具有较好的并发性能。尽管可能导致不可重复读,虚读和第二类丢失梗系这些问题,但可以再出现这类问题的个别场合使用悲观锁和乐观锁来解决。
<property name="hibernate.connection.isolation">2</property>
设置隔离级别:1、读未提交数据;2、读已提交数据;4、可重复度、8、
序列化
悲观锁:每次在操作数据时,总是悲观的认为会有别的事务也会来操作同一数据,从此锁住该数据,知道自己操作完成在解除锁。此种锁定模式会造成每次读取数据都进行锁定,而其后的存取就补习等待,这会造成性能上的问题。
Hibernate中可以利用Query或者Criteria的setLockMode()方法来设定
Configuration cfg = new Configuration();
cfg.configure("hibernate.cfg.xml");
SessionFactory sf = cfg.buildSessionFactory();
Session session = sf.openSession();
Query query = session.createQuery("from Customer as c");
[b]query.setLockMode("c", LockMode.UPGRADE);[/b] Iterator it = query.list().iterator();
Customer cust = null;
while(it.hasNext()){
cust = (Customer)it.next();
System.out.println(cust.getCname());
System.out.println(cust.getPhone());
}
LockMode.UPGRADE:利用数据库的for update字句实现锁定
LockMode.UPGRADE_NOWAIT:使用for update nowait字句进行锁定,供
Oracle数据库使用
SQL语句:
select customer0_.id as id6_, customer0_.cname as cname6_, customer0_.bank as bank6_, customer0_.phone as phone6_ from test.customer customer0_ [b]for update[/b]
乐观锁:它通常认为多个事务同是操纵一个数据的情况是很少的,因而根本不做数据库层次上的锁定,只是基于数据的
版本表示实现应用程序级别上的锁定机制。基于数据的版本标识就是为数据添加一个版本标识,一般通过数据库表增加一个“version”字段来实现。
此功能的实现方式为:在读取数据时,连同版本号一起读出,之后更新此数据时,版本号加1。在提交数据时将现有版本号与数据库表对应记录的版本号进行对比,如果提交的数据版本号大于数据库表中版本号,则允许更新数据,否则禁止更新数据。
private Integer id;
private String cname;
private String bank;
private String phone;
private Integer version;
<version name="version" column="version"></version>
Hibernate的
缓存机制(目前的工作项目中,有使用二级缓存的配置,待续…….):
Hibernate的缓存机制分类:
1.Session缓存,是用于临时保存Session实例中的持久化对象。是Hibernate不可分割的基本组成部分,也可以称为一级缓存
2.SessionFactory缓存,SessionFactory缓存分为内置缓存和外置缓存;其中内置缓存中存放的是SessionFactory对象的一些集合属性包含的数据,像元数据和预定义SQL语句等,内置缓存是只读的;外置缓存中存放的是数据库数据的副本,其作用与一级缓存类似,用户弥补一级缓存的不足,默认情况下,SessionFactory不会启用二级缓存,使用时可以通过插件的形式进行配置使用。SessionFactory缓存也可以称为二级缓存,二级缓存除了以内存作为存储介质,也可以使用
硬盘等外部存储介质
Hibernate的缓存范围
1.事务范围
只能被当前事务访问,缓存会随着事务的结束而结束生命周期,事务范围内的缓存使用的是内存作为存储介质(一级缓存属于事务范围)
2.应用范围
应用范围的缓存可以被应用范围的书屋所共享,缓存的生命周期依赖于应用的周期。此缓存范围可以使用内存或者硬盘作为存储介质,而且应用范围的缓存必须对缓存采取必要的事务隔离机制,因为它可能出现多个事务并发访问缓存。
3.集群缓存
缓存被一个机器或者多个机器的进程共享,数据被复制到集群中的每个节点。缓存中的数据通常采用对象的松散数据形式。