1.1 ORM概述
Object Relation Mapping 对象关系映射。
对象-关系映射(OBJECT/RELATIONALMAPPING,简称ORM),是随着面向对象的软件开发方法发展而产生的。用来把对象模型表示的对象映射到基于S Q L 的关系模型数据库结构中去。这样,我们在具体的操作实体对象的时候,就不需要再去和复杂的 SQ L 语句打交道,只需简单的操作实体对象的属性和方法[2] 。O R M 技术是在对象和关系之间提供了一条桥梁,前台的对象型数据和数据库中的关系型的数据通过这个桥梁来相互转化[1] 。
简单的说就是把我们程序中的实体类和数据库表建立起来对应关系。
与其它操作数据库的技术相比,Hibernate具有以下几点优势:
l Hibernate对JDBC访问数据库的代码做了轻量级封装,大大简化了数据访问层繁琐的重复性代码,并且减少了内存消耗,加快了运行效率。
l Hibernate是一个基于JDBC的主流持久化框架,是一个优秀的ORM实现,它很大程度的简化了DAO(Data Access Object,数据访问对象)层编码工作。
l Hibernate的性能非常好,映射的灵活性很出色。它支持很多关系型数据库,从一对一到多对多的各种复杂关系。
l 可扩展性强,由于源代码的开源以及API的开放,当本身功能不够用时,可以自行编码进行扩展。
明确:
操作实体类就相当于操作数据库表
hibernate的执行流程
SessionFactory
该对象维护了很多信息:
l 连接数据库的信息
l hibernate的基本配置
l 映射文件的位置,以及映射文件中的配置
l 一些预定义的SQL语句(这些语句都是通用的) 比如:全字段保存,根据id的全字段更新,根据id的全字段查询,根据id的删除等等。
l hibernate的二级缓存(了解)
1.1.1 使用原则:
由于SessionFactory维护了很多信息同时又是线程安全的,一般情况下,一个项目中只需要一个SessionFactory,只有当应用中存在多个数据源时,才为每个数据源建立一个SessionFactory实例。因此,不应该反复的创建和销毁。
原则:一个应用应该只有一个SessionFactory。在应用加载时创建,应用卸载时销毁。
以上两种获取session实例方式的主要区别是,采用openSession方法获取Session实例时,SessionFactory直接创建一个新的Session实例,并且在使用完成后需要调用close方法进行手动关闭。而getCurrentSession方法创建的Session实例会被绑定到当前线程中,它在提交或回滚操作时会自动关闭。
1.1.1 get和load的区别
区别:
1、查询的时机不一样
get方法任何时候都是立即加载,即只要一调用get马上发起数据库查询
load方法默认情况下是延迟加载,即真正用到对象的非OID字段数据才发起查询
load方法可以通过配置的方式改为立即加载。
配置的方式:
由于load方法是hibernate的方法所以只有XML的方式:
<class name="Customer" table="cst_customer" lazy="false">
2、返回的结果不一样
get方法永远返回查询的实体类对象。
load方法当是延迟加载时,返回的是实体类的代理对象。
涉及的概念:
立即加载:
是不管用不用马上查询。
延迟加载:
等到用的时候才真正发起查询。
## Hibernate第二天 ##
### 回顾与反馈 ###
Hibernate第一天
1)一种思想 : ORM OM(数据库表与实体类之间的映射) RM
2)一个项目 : CRM 客户关系管理系统 客户公司表
3)一个框架 : Hibernate 简化JDBC 面向对象的数据库操作
注意问题 : Hibernate简化了操作 但是牺牲了SQL的执行性能
线程问题 数据库连接放到线程
程序世界 程序运行期间 进程(一个软件运行期间驻留在内存中的一个程序) 与
进程接收到执行任务的时候 它就会创建一个线程去帮助我完成
这个任务
程序运行期间 内存里有一个进程 进程要做某些事情交给具体的线程
多线程
### 课程安排 ###
1. Hibernate的持久化类和对象标示符
2. Hibernate的一级缓存和对象状态
3. Hibernate的事务控制
4. Hiberante查询对象的API
### 重点内容 ###
1. Hibernate的持久化类和对象标示符
a. 描述Hibernate持久化类及编写规则
- 持久化类 : 持久化 某个java的普通类与表建立映射关系之后
- 规范
- 1) 持久化类需要无参的构造
- 2) 属性都是私有的 提供get set方法
- 3) 持久化类的属性都是包装类型 null int(0) Integer(null)
- 4) 持久化类中必须有一个属性与主键字段对应
- 5) implements Serializable
- JavaBean : Bean(豆角)-->封装思想 Java 玩积木
- Serializable 序列化 java对象-->保存到硬盘里的技术
- OID 表的主键ID --> 程序里被标记成<id>标签里面的属性
- 1) Hibernate用于区分两个对象的唯一途径
- 2) 对象标示符可以帮助session完成查询性能的优化
b. Hibernate中常用的主键生成策略
自然主键:把具有业务含义的字段作为主键,称之为自然主键
代理主键:把不具备业务含义的字段作为主键,称之为代理主键
- 数据库主键
- Grade g_id g_name g_desc
- 一个汉字 2 字节 一个数字 1字节
- 主键要求 唯一
- 自然主键 : g_name(业务中的某个字段拿来当主键) 省了一个字段
- 代理主键(推荐) : 不参与真实业务的字段 只为数据库方便管理的主键
- Hibernate主键自增策略:
- native : 让数据库自己决定主键如何生存适合于能主键自增的数据(mysql sqlserver) 适用于夸数据库
- sequence : 使用序列来生成主键,有的数据库不支持主键自增(Oracle)
- identity : 采用数据库的主键自增来生成主键(mysql,sqlserver,DB2)
- uuid : 根据128位数据算法生成32为的一个字母+数字的字符串用来标识主键
- uuid : 当你一个程序使用两个数据库的时候必须使用UUID作为同名表的主键ID; 数据1 数据2 userinfo (1 2 3 4) (1 2 3 4)
- 整理Hibernate的时候需要注意 : jar包 配置文件
2. Hibernate的一级缓存和对象状态
a. 能够描述Hibernate的一级缓存
- 缓存 : 临时存储硬盘数据到内存中的一项技术;
- 程序运行在内存里 所以当读取数据的时候 内存读内存 内存读硬盘快
- redis(2000)
- Hibernate为了提升查询性能也进行了缓存操作-->Session
- Session的一级缓存 是Hibernate用于进行数据库与程序之间沟通的一个桥;
- 当程序完成持久化操作的时候Hibernate会通过Session把该对象与相对应的数据库中的数据关联到一起;
- 快照机制 : 说明了session不光可以缓存数据库中的数据,还可以管理数据库中的数据,但是要注意快照机制只能简化更新操作;
- 缓存与快照机制保证了数据库的简化及性能提升操作;
b. Hibernate持久化对象的三种状态及特征
- Hibernate管理数据库表数据库的时候有个问题
- Java 类的对象
- Hibernate Session 管理的对象
- Hibernate管理Java对象的时候因为根据数据库数据对应于session管理不同分为三种状态
- 持久化 : 数据库有数据对应 被session管理
- 瞬时对象 : 数据库没有数据对应 session不管理
- 游离状态 : 数据库有数据对应 但是session不管理
- 操作 :
- 1) 添加的一定是瞬时的!
- 2) 更新和修改的一定是持久的!
它是hibernate用于区分两个对象是否是同一个对象的标识。
我们都知道,虚拟机内存区分两个对象看的是内存的地址是否一致。数据库区分两个对象,靠的是表的主键。hibernate负责把内存中的对象持久化到数据库表中,靠的就是对象标识符来区分两个对象是否是同一个。实体类中映射主键的字段就是OID
Hibernate的一级缓存就是指Session缓存,Session缓存是一块内存空间,用来存放相互管理的java对象,在使用Hibernate查询对象的时候,首先会使用对象属性的OID值在Hibernate的一级缓存中进行查找,如果找到匹配OID值的对象,就直接将该对象从一级缓存中取出使用,不会再查询数据库;如果没有找到相同OID值的对象,则会去数据库中查找相应数据。当从数据库中查询到所需数据时,该数据信息也会放置到一级缓存中。Hibernate的一级缓存的作用就是减少对数据库的访问次数。
Hibernate的一级缓存有如下特点:
l 当应用程序调用Session接口的save()、update()、saveOrUpdate时,如果Session缓存中没有相应的对象,Hibernate就会自动的把从数据库中查询到的相应对象信息加入到一级缓存中去。
l 当调用Session接口的load()、get()方法,以及Query接口的list()、iterator()方法时,会判断缓存中是否存在该对象,有则返回,不会查询数据库,如果缓存中没有要查询对象,再去数据库中查询对应对象,并添加到一级缓存中。
l 当调用Session的close()方法时,Session缓存会被清空。
1.1.1 快照机制:
Hibernate 向一级缓存放入数据时,同时复制一份数据放入到Hibernate快照中,当使用commit()方法提交事务时,同时会清理Session的一级缓存,这时会使用OID判断一级缓存中的对象和快照中的对象是否一致,如果两个对象中的属性发生变化,则执行update语句,将缓存的内容同步到数据库,并更新快照;如果一致,则不执行update语句。Hibernate快照的作用就是确保一级缓存中的数据和数据库中的数据一致
1.1 对象的三种状态
1.1.1 对象的状态说明:
了解了主键的生成策略之后,我们可以进一步来了解持久化类了。Hibernate为了更好的来管理持久化类,特将持久化类分成了三种状态。在Hibernate中持久化的对象可以划分为三种状态,分别是瞬时态、持久态和脱管态,一个持久化类的实例可能处于三种不同状态中的某一种,三种状态的详细介绍如下。
1、 瞬时态(transient)
瞬时态也称为临时态或者*态,瞬时态的实例是由new命令创建、开辟内存空间的对象,不存在持久化标识OID(相当于主键值),尚未与Hibernate Session关联,在数据库中也没有记录,失去引用后将被JVM回收。瞬时状态的对象在内存中是孤立存在的,与数据库中的数据无任何关联,仅是一个信息携带的载体。
2、 持久态(persistent)
持久态的对象存在持久化标识OID ,加入到了Session缓存中,并且相关联的Session没有关闭,在数据库中有对应的记录,每条记录只对应唯一的持久化对象,需要注意的是,持久态对象是在事务还未提交前变成持久态的。
3、 脱管态(detached)
脱管态也称离线态或者游离态,当某个持久化状态的实例与Session的关联被关闭时就变成了脱管态。脱管态对象存在持久化标识OID,并且仍然与数据库中的数据存在关联,只是失去了与当前Session的关联,脱管状态对象发生改变时Hibernate不能检测到。
1.1.2 学习对象状态我们要明确的:
a、是为了更好的掌握hibernate中操作的方法。
b、区分状态只有两个标识
一是否有OID
二是否和Session建立的关系
临时状态:
没有OID,和Session没有关系。
持久化状态:
有OID,和Session有关系。
脱管状态:
有OID,和Session没有关系。
3. Hibernate的事务控制
a. Hibernate进行事务控制
- 事务四大特性 多线程环境下事务并发问题 脏读 .....
- 事务隔离级别的设置
- 读未提交 最危险 事务操作最快 1
- 读已提交 比较适合 2
- 可重复读 比较笨拙 4
- 串行读 最安全 事务操作最慢 8
如果一旦出现事务隔离级别的设置 肯定涉及到多线程并发操作
JavaSE开发 单线程
b. 线程与session绑定的时候
1) 自定线程类与session绑定
private static ThreadLocal<Session> sessionTL = new ThreadLocal<Session>();
public static Session getSession(){
// 创建方法返回值
Session session = null;
// 1. 放到线程里 2.C3P0
// 当前线程先获取一下
session = sessionTL.get();
if(null == session){
session = factory.openSession();
// 把session放入当前线程
sessionTL.set(session);
}
// 返回
return session;
}
特点 : 查询方法不用加事务 但是编写其来麻烦
2) Hibernate默认绑定
<property name="hibernate.current_session_context_class">thread</property>
public static Session getCurrentSession(){
// 使用hibernat默认的session与线程绑定
return factory.getCurrentSession();
}
特点: 编写简单,但是所有的方法都必须加上事务才可以运行
4. Hiberante查询对象的API
X. 问题:不能查询所有
a. Query对象的作用及常用的方法
- HQL查询 : Hibernate query la...
- HQL查询 : Hibernate还是属于一种SQL查询;保留SQL结构,查询表被替换成类,查询字段被替换成属性;
- query查询 query对象 -->离开session
b. Criteria对象的作用及常用的方法
Criteria 可以离开session独立生存
### 课后总结 ###
作业: Hibernate+servlet完成一张表的CURD 分页
封装: 三层开发
1.1.1 Query中的方法说明:
l list方法:该方法用于查询语句,返回的结果是一个list集合。
l uniqueResult方法:该方法用于查询,返回的结果是一个Object对象。
l setter方法:Query接口中提供了一系列的setter方法用于设置查询语句中的参数,针对不同的数据类型,需要用到不同的setter方法。
l uniqueResult()方法:该方法用于返回唯一的结果,在确保只有一条记录的查询时可以使用该方法。
l setFirstResult()方法:该方法可以设置获取第一个记录的位置,也就是它表示从第几条记录开始查询,默认从0开始计算。
setMaxResult()方法:该方法用于设置结果集的最大记录数,通常与setFirstResult()方法结合使用,用于限制结果集的范围,以实现分页功能。
Long total = (Long)query.uniqueResult();//返回的是一个唯一的结果集。 只有确定结果集唯一时,才能使用
1.1.1.1 统计查询
/**
* QBC使用聚合函数
* 统计查询
* 涉及的对象:
* Criteria
* 涉及的方法:
* setProjection(Projection p);
* 参数的含义
* Projection:要添加的查询投影
*/
@Test
public void test2(){
Session s = HibernateUtil.getCurrentSession();
Transaction tx = s.beginTransaction();
//1.获取对象
Criteria c = s.createCriteria(Customer.class);//from Customer | select * from cst_customer
//2.想办法把select * 变成 select count(*)
// c.setProjection(Projections.rowCount());//select count(*)
c.setProjection(Projections.count("custId"));//select count(cust_id)
//3.获取结果集
// List list = c.list();
// for(Object o : list){
// System.out.println(o);
// }
Long total = (Long)c.uniqueResult();
System.out.println(total);
tx.commit();
}
/**
* 离线条件查询
1.1.1.1 条件查询
/**
* 条件查询
*/
@Test
public void test2(){
Session s = HibernateUtil.getCurrentSession();
Transaction tx = s.beginTransaction();
//1.获取Criteria对象
Criteria c = s.createCriteria(Customer.class);//它就相当于HQL的from Customer
//2.设置查询条件
c.add(Restrictions.like("custName", "%集%"));//from Customer where custName like "%集%"
c.add(Restrictions.eq("custLevel", "普通客户"));
//3.执行对象的方法获取结果集
List list = c.list();
for(Object o : list){
System.out.println(o);
}
tx.commit();
}
* 离线:
* 它是和在线对应的。
* Criteria对象是一个在线对象,它是由一个可用的(活动的)Session对象获取的出来的。
* 当session失效时,就无法再获取该对象了。
* 有一个对象,它也可以用于设置条件,但是获取的时候并不需要Session对象。
* 该对象就叫做离线对象:
* DetachedCriteria对象
* 使用该对象进行的查询就叫做:离线查询
*
* 如何获取该对象
* DetachedCriteria dCriteria = DetachedCriteria.forClass(要查询的实体类字节码);
*
1.1.1.1 离线查询
/**
* 离线条件查询
* 离线:
* 它是和在线对应的。
* Criteria对象是一个在线对象,它是由一个可用的(活动的)Session对象获取的出来的。
* 当session失效时,就无法再获取该对象了。
* 有一个对象,它也可以用于设置条件,但是获取的时候并不需要Session对象。
* 该对象就叫做离线对象:
* DetachedCriteria对象
* 使用该对象进行的查询就叫做:离线查询
*
* 如何获取该对象
* DetachedCriteria dCriteria = DetachedCriteria.forClass(要查询的实体类字节码);
*
*/
@Test
public void test3(){
//模拟一次web操作: 浏览器发送请求——调用servlet——调用service——调用dao——拿到结果到jsp上展示
List list = servletFindAllCustomer();
for(Object o : list){
System.out.println(o);
}
}
//模拟servlet
public List<Customer> servletFindAllCustomer(){
//离线对象
DetachedCriteria dCriteria = DetachedCriteria.forClass(Customer.class);
//设置条件:和Criteria是一样的
dCriteria.add(Restrictions.like("custName","%集%"));
return serviceFindAllCustomer(dCriteria);
}
public List<Customer> serviceFindAllCustomer(DetachedCriteria dCriteria) {
return daoFindAllCustomer(dCriteria);
}
public List<Customer> daoFindAllCustomer(DetachedCriteria dCriteria) {
Session s = HibernateUtil.getCurrentSession();
Transaction tx = s.beginTransaction();
//把离线对象使用可用Session激活
Criteria c = dCriteria.getExecutableCriteria(s);
List<Customer> list = c.list();
tx.commit();
return list;
}
*/
@Test
public void test3(){
//模拟一次web操作: 浏览器发送请求——调用servlet——调用service——调用dao——拿到结果到jsp上展示
List list = servletFindAllCustomer();
for(Object o : list){
System.out.println(o);
}
}
//模拟servlet
public List<Customer> servletFindAllCustomer(){
//离线对象
DetachedCriteria dCriteria = DetachedCriteria.forClass(Customer.class);
//设置条件:和Criteria是一样的
dCriteria.add(Restrictions.like("custName","%集%"));
return serviceFindAllCustomer(dCriteria);
}
public List<Customer> serviceFindAllCustomer(DetachedCriteria dCriteria) {
return daoFindAllCustomer(dCriteria);
}
public List<Customer> daoFindAllCustomer(DetachedCriteria dCriteria) {
Session s = HibernateUtil.getCurrentSession();
Transaction tx = s.beginTransaction();
//把离线对象使用可用Session激活
Criteria c = dCriteria.getExecutableCriteria(s);
List<Customer> list = c.list();
tx.commit();
return list;
}
hibernate第三天
1.1 一对多XML关系映射
1.1.1 客户配置文件:
<?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.itheima.domain">
<class name="Customer" table="cst_customer">
<id name="custId" column="cust_id">
<generator class="native"></generator>
</id>
<property name="custName" column="cust_name"></property>
<property name="custLevel" column="cust_level"></property>
<property name="custSource" column="cust_source"></property>
<property name="custIndustry" column="cust_industry"></property>
<property name="custAddress" column="cust_address"></property>
<property name="custPhone" column="cust_phone"></property>
<!-- 一对多关系映射
涉及的标签
set:用于映射set集合属性
属性:
name:指定集合属性的名称
table:在一对多的时候写不写都可以。
它指定的是集合元素所对应的表
one-to-many:用于指定当前映射配置文件所对应的实体和集合元素所对应的实体是一对多关系。
属性:
class:指定集合元素所对应的实体类名称。
key:用于映射外键字段的。
属性:
column:指定从表中的外键字段名称
-->
<set name="linkmans" table="cst_linkman">
<key column="lkm_cust_id"></key>
<one-to-many class="LinkMan"/>
</set>
</class>
</hibernate-mapping>
1.1.2 联系人配置文件:
<?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.itheima.domain">
<class name="LinkMan" table="cst_linkman">
<id name="lkmId" column="lkm_id">
<generator class="native"></generator>
</id>
<property name="lkmName" column="lkm_name"></property>
<property name="lkmGender" column="lkm_gender"></property>
<property name="lkmPhone" column="lkm_phone"></property>
<property name="lkmMobile" column="lkm_mobile"></property>
<property name="lkmEmail" column="lkm_email"></property>
<property name="lkmPosition" column="lkm_position"></property>
<property name="lkmMemo" column="lkm_memo"></property>
<!-- 多对一关系映射
所以在一对多中,一的一方都会放弃外键的维护权(关系的维护)。这个时候如果想让一的一方放弃外键的维护权,只需要进行如下的配置即可。
涉及的标签:
many-to-one:用于建立多对一的关系映射配置
属性:
name:指定的实体类中属性的名称
class:该属性所对应的实体类名称。如果在hibernate-mapping上没有导包,则需要写全限定类名
column:指定从表中的外键字段名称
-->
<many-to-one name="customer" class="Customer" column="lkm_cust_id" /> </class>
</hibernate-mapping>
1.2 多对多关系映射
1.2.1 用户配置文件:
<?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.itheima.domain">
<class name="SysUser" table="sys_user">
<id name="userId" column="user_id">
<generator class="native"></generator>
</id>
<property name="userCode" column="user_code"></property>
<property name="userName" column="user_name"></property>
<property name="userPassword" column="user_password"></property>
<property name="userState" column="user_state"></property>
<!-- 多对多关系映射
涉及的标签:
set:用于映射集合属性
属性:
name:指定集合属性的名称
table:指定的是中间表的名称,在多对多的配置时,必须写。
key:指定外键字段
属性:
column:指定的是当前映射文件所对应的实体在中间表的外键字段名称
many-to-many:指定当前映射文件所对应的实体和集合元素所对应的实体是多对多的关系
属性:
class:指定集合元素所对应的实体类
column:指定的是集合元素所对应的实体在中间表的外键字段名称
-->
<set name="roles" table="user_role_rel">
<key column="user_id"></key>
<many-to-many class="SysRole" column="role_id"></many-to-many>
</set>
</class>
</hibernate-mapping>
1.2.2 角色配置文件:
<?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.itheima.domain">
<class name="SysRole" table="sys_role">
<id name="roleId" column="role_id">
一对多保存操作:
1.1.1 保存操作
保存原则:先保存主表,再保存从表。
/**
* 保存操作
* 需求:
* 保存一个客户和一个联系人
* 要求:
* 创建一个客户对象和一个联系人对象
* 建立客户和联系人之间关联关系(双向一对多的关联关系)
* 先保存客户,再保存联系人
* 问题:
* 在使用xml配置的情况下:
当我们建立了双向的关联关系之后,先保存主表,再保存从表时:
会产生2条insert和1条update.
* 而实际开发中我们只需要2条insert。
*
*/
@Test
public void test1(){
//创建客户和联系人对象
Customer c = new Customer();//瞬时态
c.setCustName("TBD云集中心");
c.setCustLevel("VIP客户");
c.setCustSource("网络");
c.setCustIndustry("商业办公");
c.setCustAddress("昌平区北七家镇");
c.setCustPhone("010-84389340");
LinkMan l = new LinkMan();//瞬时态
l.setLkmName("TBD联系人");
l.setLkmGender("male");
l.setLkmMobile("13811111111");
l.setLkmPhone("010-34785348");
l.setLkmEmail("98354834@qq.com");
l.setLkmPosition("老师");
l.setLkmMemo("还行吧");
//建立他们的双向一对多关联关系
//l.setCustomer(c);
c.getLinkmans().add(l);
Session s = HibernateUtil.getCurrentSession();
Transaction tx = s.beginTransaction();
//按照要求:先保存客户,再保存联系人(此时符合保存原则:先保存主表,再保存从表)
s.save(c);//如果在把客户对象转成持久态时,不考虑联系人的信息。就不会有联系人的快照产生
s.save(l);
tx.commit();//默认此时会执行快照机制,当发现一级缓存和快照不一致了,使用一级缓存更新数据库。
}
1.1.1.1 保存时遇到的问题
我们已经分析过了,因为双向维护了关系,而且持久态对象可以自动更新数据库,更新客户的时候会修改一次外键,更新联系人的时候同样也会修改一次外键。这样就会产生了多余的SQL,那么问题产生了,我们又该如何解决呢?
其实解决的办法很简单,只需要将一方放弃外键维护权即可。也就是说关系不是双方维护的,只需要交给某一方去维护就可以了。通常我们都是交给多的一方去维护的。为什么呢?因为多的一方才是维护关系的最好的地方,举个例子,一个老师对应多个学生,一个学生对应一个老师,这是典型的一对多。那么一个老师如果要记住所有学生的名字很难的,但如果让每个学生记住老师的名字应该不难。其实就是这个道理。
所以在一对多中,一的一方都会放弃外键的维护权(关系的维护)。这个时候如果想让一的一方放弃外键的维护权,只需要进行如下的配置即可。
inverse的默认值是false,代表不放弃外键维护权,配置值为true,代表放弃了外键的维护权。代码如下:
解决多一条update语句的问题:
<set name="linkmans" table="cst_linkman" inverse="true">
<key column="lkm_cust_id"></key>
<one-to-many class="LinkMan"/>
</set>
<generator class="native"></generator>
</id>
<property name="roleName" column="role_name"></property>
<property name="roleMemo" column="role_memo"></property>
<!-- 多对多关系映射 -->
<set name="users" table="user_role_rel">
<key column="role_id"></key>
<many-to-many class="SysUser" column="user_id"></many-to-many>
</set>
</class>
</hibernate-mapping>
1.1.1 修改操作
/**
* 更新操作
* 需求:
* 更新客户
* 要求:
* 创建一个新的联系人对象
* 查询id为1的客户
* 建立联系人和客户的双向一对多关联关系
* 更新客户
* 问题:
* 当更新一个持久态对象时,它关联了一个瞬时态的对象。执行更新
* 如果是注解配置:什么都不会做
* 如果是XML配置:会报错
* 解决办法:
* 配置级联保存更新
*/
@Test
public void test2(){
//创建联系人对象
LinkMan l = new LinkMan();//瞬时态
l.setLkmName("TBD联系人test");
l.setLkmGender("male");
l.setLkmMobile("13811111111");
l.setLkmPhone("010-34785348");
l.setLkmEmail("98354834@qq.com");
l.setLkmPosition("老师");
l.setLkmMemo("还行吧");
Session s = HibernateUtil.getCurrentSession();
Transaction tx = s.beginTransaction();
//查询id为1的客户
Customer c1 = s.get(Customer.class, 1L);
//建立双向关联关系
c1.getLinkmans().add(l);
//l.setCustomer(c1);
//更新客户
s.update(c1);
tx.commit();
}
1.1.1.1 修改中遇到的问题:
什么是级联操作:
级联操作是指当主控方执行保存、更新或者删除操作时,其关联对象(被控方)也执行相同的操作。在映射文件中通过对cascade属性的设置来控制是否对关联对象采用级联操作,级联操作对各种关联关系都是有效的。
级联操作的方向性:
级联是有方向性的,所谓的方向性指的是,在保存一的一方级联多的一方和在保存多的一方级联一的一方。
【保存客户级联联系人】
首先要确定我们要保存的主控方是那一方,我们要保存客户,所以客户是主控方,那么需要在客户的映射文件中进行如下的配置。
代码如下:
解决报错的问题,配置级联保存更新:
<set name="linkmans" table="cst_linkman" cascade="save-update" inverse="true">
<key column="lkm_cust_id"></key>
<one-to-many class="LinkMan"/>
</set>
hibernate第四天学习
1.1 JPA的优势
标准化
JPA 是 JCP 组织发布的 Java EE 标准之一,因此任何声称符合 JPA 标准的框架都遵循同样的架构,提供相同的访问API,这保证了基于JPA开发的企业应用能够经过少量的修改就能够在不同的JPA框架下运行。
容器级特性的支持
JPA框架中支持大数据集、事务、并发等容器级事务,这使得 JPA 超越了简单持久化框架的局限,在企业应用发挥更大的作用。
简单方便
JPA的主要目标之一就是提供更加简单的编程模型:在JPA框架下创建实体和创建Java 类一样简单,没有任何的约束和限制,只需要使用 javax.persistence.Entity进行注释,JPA的框架和接口也都非常简单,没有太多特别的规则和设计模式的要求,开发者可以很容易的掌握。JPA基于非侵入式原则设计,因此可以很容易的和其它框架或者容器集成。
查询能力
JPA的查询语言是面向对象而非面向数据库的,它以面向对象的自然语法构造查询语句,可以看成是Hibernate HQL的等价物。JPA定义了独特的JPQL(Java Persistence Query Language),JPQL是EJB QL的一种扩展,它是针对实体的一种查询语言,操作对象是实体,而不是关系数据库的表,而且能够支持批量更新和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL 才能够提供的高级查询特性,甚至还能够支持子查询。
高级特性
JPA 中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,这样的支持能够让开发者最大限度的使用面向对象的模型设计企业应用,而不需要自行处理这些特性在关系数据库的持久化。
JPA在保存的时候不存在多两条条insert 和1条update语句 在更新的时候需要配置级联更新