配置文件的详解
映射文件
名称:自定义 格式为xml 建议: 类名.hbm.xml
路径:自定义 建议放在domain下
导入约束
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
内容:
类和表的映射关系
class标签
name属性:类的全限定名
table属性:表名(若表名和类名同样的话,table属性能够省略不写)
属性和主键字段的映射关系
id标签
name属性:属性名称
column属性:主键字段名称(若列名和属性名一致的话,column属性能够省略不写)
主键生成策略:目前使用native
属性和普通字段的映射关系
property标签
name属性:
column属性:
/////////////////////////////////////////////////
核心配置文件
名称: 建议使用 hibernate.cfg.xml 也可使用hibernate.properties(不建议)
路径: 建议放在 src目录下
导入约束:
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
配置内容:
数据库链接4个基本信息 : 参考\project\etc\hibernate.properties property标签
hibernate的属性配置 方言,是否显示sql,是否格式化sql... property标签
<!--
是否由hibernate来生成表的定义语句及如何生成
常见值:
update:使用表的使用,先判断表是否存在,若不存在自动建立表.
若存在,
再判断配置文件和表是否对应上,
若对应不上,更新表结构
若对应上,直接使用表
了解值:
create:每次使用表的时候,都从新建立一张表,若以前表存在则删除
create-drop:每次使用表的时候,都从新建立一张表,若以前表存在则删除,当此次执行完全完成的时候,删除此表.
validate:每次使用表的时候,须要校验表和映射文件中的配置是否同样,若一致则使用,若不同呢则抛异常
-->
<property name="hibernate.hbm2ddl.auto">validate</property>
映射文件的路径 mapping标签
<mapping resource="cn/itcast/domain/Customer.hbm.xml"/>
==================================
api的详解
Configuration:类 配置对象
做用:
★1.加载核心配置文件
(了解)new Configuration():默认加载src目录下的名称为 hibernate.properties的配置文件(不能配置映射文件的路径)
★new Configuration().configure():默认加载src目录下的名称为 hibernate.cfg.xml的配置文件
(了解)new Configuration().configure(String 配置文件的路径):加载指定路径下的配置文件
new Configuration().configure("config/a.xml"):加载src目录下config目录下的a.xml
★2.建立sessionFactory
buildSessionFactory()
(了解)3.加载映射文件
addResource(string );
-----------------------------------
SessionFactory:接口 session工厂
SessionFactory并非轻量级的,由于通常状况下,一个项目一般只须要一个SessionFactory就够
做用:
1.初始化Hibernate。
2.它充当数据存储源的代理(底层维护了一个链接池)
★3.并负责建立Session对象
openSession();
---------------------
抽取一个工具类
HibernateUtils
提供一个获取session的方法
---------------------
整合c3p0
步骤:
1.导入jar包 hibernate解压目录下/lib/optional/c3p0/*.jar
2.在核心配置文件中配置c3p0提供商
<property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
Session:接口,至关于链接
Session对象是有生命周期的,它以Transaction对象的事务开始和结束边界
线程不安全,轻量级,session不要做为成员变量.
★做用:
1.开启事务
beginTransaction()
2.和数据库进行交互
Serializable save(Object obj); 返回值为此记录的id
update(Object ob);
delete(Object ob);
OID查询
T get(Class<T> clazz,Serializable id);经过id获取一个记录,转成相应的对象
T load(Class<T> clazz,Serializable id);经过id获取一个记录,转成相应的对象
区别:
(★)get:当即发送sql语句,返回值为对象自己
load:不会当即发送sql语句,当使用该对象的非oid属性的时候才发送sql.返回的是代理对象
延迟加载:使用的时候才发送sql.
Transaction:接口 管理事务
commit();
rollback();
*-*-*-*-*-*-*-*-*-*-*-**-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-java
持久化类
javabean+映射文件
规范:
这个类必须是一个公共的具体的类(public class)
字段私有,必须提供公共的访问方法
必须提供一个无参构造器
必须提供一个属性(OID)和主键对应
尽可能使用包装类修饰属性
尽可能不要使用final修饰持久化类(若使用final修饰了load和get方法同样了)
///////////////////////////////////////
主键的生成策略
主键的分类:
代理主键:给对象额外添加一个字段,没有具体的业务,只是做为主键存在.推荐使用
天然主键:使用对象自己一个具备业务意义的字段做为主键(例如:身份证号)
hibernate中的主键生成策略
常见的值:
increment:使用的是hibernate的自增,有问题,数据类型必须为 int short long,不推荐使用
★identity:使用的数据库底层的自增策略,数据类型必须为 int short long,表明:mysql
★sequence:使用的数据库底层的序列策略,数据类型必须为 int short long,表明:oracle
★★native:使用的数据库自己的策略,能够简单认为判断数据库支持自增仍是序列.
★uuid:使用的随机的字符串
assigned:放弃hibernate维护主键,咱们本身维护.
持久化对象三种状态(持久态★)
瞬时态:对象没有oid值,也没有和session关联
持久态★:对象有oid值,也和session关联
脱管态:对象有oid值,可是没有和session关联
注意:
持久态:能够自动更新数据库
为什么持久态对象能自动更新数据库?底层依赖的是hibernate的一级缓存.
一级缓存
缓存:
介于程序和硬件之间的一种介质.
做用:
读取数据的时候,先从缓存中查询一下,若缓存中有,直接返回数据.若缓存中没有,再去硬件中查询;
hibernate是对jdbc进行的封装.效率其实并不高,因而hibernate提供了一系列的优化策略(例如:缓存,延迟加载,抓取策略...)来提供hibernate的运行效率.
hibernate中的缓存:
*一级缓存:是hibernate自带的,必须使用的.不能卸载.它的生命周期和session同样.因此有人也称之为session级别的缓存.
(了解)二级缓存:不是hibernate自带的,要想使用必须先导入jar包,在编写配置文件才可使用(ehcache).通常使用redis替代.
它的生命周期和sessionFactory同样,因此有人也称之为sessionFactory级别的缓存
hibernate中的一级缓存工做原理:
底层就是一系列的java集合.
读取数据的时候,优先从缓存中查询,若缓存中有直接用;若缓存中没有,在去数据库中查询,且会将查询的结果放入缓存中一份
保存或者更新数据,一样也会讲保存或者更新过的数据放入缓存中一份.
其实将一级缓存分红了两块区域(缓存区和快照区)
将数据放入了缓存区一份和快照区中一份.咱们修改数据的时候,只修改缓存区中的数据
当事务提交的时候,判断缓存区和快照区中的数据是否一致;
若一致:啥也不干
若不一致:发送sql语句,更新数据库
先来证实一下一级缓存的存在.
1.对一条记录查询两次
2.先保存一条记录在查询这条记录.
事务
事务的概念
事务的特性:
ACID
原子性
一致性
隔离性
持久性
若不考虑隔离性会产生那些读问题:
脏读
不可重复读
虚读
经过设置数据库的隔离级别就能够避免上面的一些问题
1 read uncommitted
2 read committed (oracle)
4 repeatable read (mysql)
8 serializable
java中应该在service层进行事务控制
如何保证service和dao使用的是同一个事务?
只须要保证他们使用的是同一个链接
如何保证service和dao使用的是同一个链接?
方式1:向下传递链接
*方式2:ThreadLocal
hibernate中的设置隔离级别:
只须要在核心配置文件中配置 hibernate.connection.isolation 属性
<!-- 设置事务的隔离级别 -->
<property name="hibernate.connection.isolation">4</property>
*hibernate其实对ThreadLocal进行封装了
步骤:
1.先在核心配置文件中配置 hibernate.current_session_context_class 属性 值为:thread
<!-- 开启与线程绑定的session -->
<property name="hibernate.current_session_context_class">thread</property>
2.获取session的时候,经过factory.getCurrentSession();
注意:
若使用getCurrentSession()获取的session,不须要咱们手动关闭了
////////////////////////////////////
查询的api(*)
hibernate中提供了5中常见的查询方式
oid查询(get和load)
对象导航查询(明天讲)
sql查询(直接写sql语句)
hql查询
qbc查询(条件查询的)
/////////////////////
hql查询
先获取Query对象
session.createQuery(String hql语句)
hql语句,几乎和sql语句同样.是一个面向对象的查询语句.
hql语句须要讲sql中的表名和字段名使用类名和属性名替代.
查询全部
hql:
from Customer
方法:
list()
uniqueResult() :返回惟一值
排序查询
hql:
from Customer order by ....
统计查询
hql:
select count(*) from Customer ....
分页查询
不要在hql中体现分页信息
方法:
setFirstResult(int 开始的索引)
setMaxResults(int 每页显示的大小)
条件查询
hql:
from Customer where 属性1 = ? and 属性2 = ?
方法:
setParameter(int 问号索引,Object 值);
投影查询(查询部分属性)
返回的是 Object[]
//投影查询(部分属性) 将查询的结果封装到对象中
/**
* 1.须要在持久化类中提供相应的构造器
* 2.改写hql语句
* select new 类名(属性1,属性2) from 类
*/
//////////////////////////
qbc查询
query by criteria:更加面向对象的查询方式.全是api
先获取查询对象 Criteria
session.createCrieria(Class clazz)
查询全部
list()
uniqueResult()
排序查询
addOrder(Order.asc|desc(属性名称))
分页查询
setFirstResult(int 开始的索引)
setMaxResults(int 每页显示的大小)
统计查询
setProjection(Projections.sum|min|max|avg|count("属性名"))
setProjection(Projections.rowCount())
条件查询
add(Restrictions.like|eq|lt|gt(属性名,值...))
离线查询(多条件+分页)
脱离session使用的查询方式.
获取离线对象的方式
DetachedCriteria dc = DetachedCriteria.forClass(Class clazz);
他的api几乎和criteria的api一致
咱们在web层使用离线查询对象,
用它来判断参数和封装参数
咱们最后须要在dao获取一个能够执行的查询对象便可
Criertia cc = dc.getExecutableCriteria(session);
sql查询
SqlQuery ss = session.createSqlQuery(String sql语句);
方法:
list
uniqueResult
setFirstResult
setMaxResults
setParameter
返回值都是:
object[]
若想要将结果封装成某个对象
addEnity(Class clazz)mysql
*-*-*--*-*--*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*web
多表操做:
常见的关系:
一对多
多对多
实体之间的关系:
一对多:用户和订单,分类和商品,订单和订单项,客户和联系人
多对多:学生和选课,订单和商品,角色和用户
一对一:公司和注册地,合法夫妻,居民和身份证号
数据库中表的设计:
一对多:根据须要在多表中添加一个外键,指向一表的键(通常是主键)
多对多:引入一张中间表,里面须要存放两位两张表的主键.
一对一:
解决的方案:
1.合二为一
2.惟一外键对应(就是把他们当作是一个特殊的一对多,在其中的一张表中添加外键,且给这个外键提供惟一约束)
3.主键对应
java中的实体设计:
一对多:
客户和联系人
根据须要
在一的一方添加多的一方的集合
在多的一方添加一个一方的对象
多对多:
放入一个对方的集合便可
一对一:
放入一个对方的对象便可
案例1:一对多
客户和联系人
1.建立数据库
2.建立对应的类
根据须要
在一的一方添加多的一方的集合
在多的一方添加一个一方的对象
3.建立映射文件
在多的一方配置many-to-one标签
name属性:一的一方在多的一方中的对象属性名称
class属性:一的一方的全限定名
column属性:外键的字段名称
在一的一方配置set标签
name属性:多的一方在一的一方中集合的属性名称
key标签:
column属性:外键的字段名称
one-to-many标签
class属性:多的一方的全限定名
别忘记去核心配置文件中加载两个映射文件
4.测试代码
设计两个客户:大雄 老金
设计三个联系人:
大雄:熊大和熊二
老金:金三胖
有一个小小的问题:
保存的时候还发送几条update语句?(一会讲)
没有添加任何配置,能不能只保存客户,将他关联的联系人也进行保存呢?瞬时对象异常
--------------------------
级联操做:
级联:操做主体的时候,将其关联的对象也进行相同的操做.
级联具备方向性
级联保存更新操做:
★保存客户,将他关联的联系人也进行保存
客户是主体,就须要在主体的一方配置级联保存更新,在映射文件中set标签上配置 cascade属性 值为:save-update
保存联系人,将他关联的客户也进行保存
联系人是主体,就须要在主体的一方配置级联保存更新,在映射文件中many-to-one标签上配置 cascade属性 值为:save-update
级联删除操做:
★删除客户的时候,将他关联的联系人也进行删除
客户是主体,就须要在主体的一方配置级联删除,在映射文件中set标签上配置 cascade属性 值为:delete 若还有其余的级联操做,用","隔开
删除联系人的时候,将他关联的客户也进行删除
联系人是主体,就须要在主体的一方配置级联删除,在映射文件中many-to-one标签上配置 cascade属性 值为:delete 若还有其余的级联操做,用","隔开
-------------------------
★默认删除
删除一的一方的时候:(必须先查询再删除)
若咱们使用web阶段的只是直接删一的一方的会报错;
hibernate中会先将从表中的对应数据的外键置为null,而后再删除主表中的数据.
冗余sql(多余sql)
咱们保存数据的时候 先保存了客户,而后保存了联系人,发现除了insert语句以外,还发送几条update语句
这个操做不影响结果
缘由:
双方都维护外键 且 双方进行了关联操做
解决方案:
方案1:让其中的一方放弃外键维护
方案2:单向关联
咱们在开发中使用让其中的一方放弃外键维护,谁放弃呢?
必须是一的一方放弃.
只须要在一的一方的映射文件中set标签上配置 inverse="true"
cascade决定的是 操做主体的时候 是否 也操做关联方
inverse决定的是 保存一的一方的的时候 多表中是否有外键
开发中:
通常都会在一的一方添加inverse 且设置cascade=save-update
案例2:多对多
用户和角色
1.建立实体类
放入一个对方的集合
2.建立映射文件
set标签
name属性:对方在本身中的集合属性名称
table属性:中间表表名
key标签
column属性:本身在中间表的外键信息
many-to-many标签
class属性:对方的全限定名
column属性:对方在中间表中的外键信息
在核心配置文件中加载映射文件
3.测试代码
让其中的一方放弃外键维护
开发中 通常让被动方放弃
级联保存或更新(了解中理解)
保存用户的时候,将其关联的角色也进行相同的操做
用户是主体,须要在用户的映射文件中配置级联保存更新 set标签上配置
级联删除:(了解中的了解)
删除用户的时候,将其关联的角色也进行相同的操做
用户是主体,须要在用户的映射文件中配置级联删除 set标签上配置
★默认删除:(★)
先删除中间表中的相应的记录,而后再删除对应表中的记录.
其余的操做:
例如:
给一个用户添加角色
查询用户
查询某个角色
给用户的roles中添加角色便可
给一个用户删除角色
给一个用户切换角色
注意:
hibernate是一个orm框架.查询到的数据都是持久态数据.
扩展:
一对一:(使用惟一外键对应)
放入对方的对象
映射文件
使用one-to-one标签 且在外键上添加一个 constrained
延迟加载和对象导航查询
需求:
想知道客户1的联系人个数
Customer c = session.get(Customer.class,1L);
c.getLinkmans().size();
想知道联系人1的客户名称
Linkman l = session.get(Linkman.class,1L);
l.getCustomer().getCust_name();
延迟加载:
使用的时候采起发送sql查询
客户1的联系人个数
由一的一方查询多的一方默认使用的延迟加载.(开发中咱们也是这样作的)
能不能查询客户的时候立马把联系人也查询出来.(了解)
在客户的映射文件中的set标签上设置一个属性(lazy属性) 值为false便可
联系人1的客户名称
有多的一方查询一的一方的时候默认使用的也是延迟加载
开发中咱们经过多的一方查询一的一方通常使用的是当即加载. (★)
在联系人的映射文件中 many-to-one标签,上设置一个属性(lazy属性) 值为false便可
在set标签上lazy属性的常见值有
true:使用延迟加载 (默认值) ★
false:使用当即加载
在many-to-one标签上lazy属性的经常使用值为 false
*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-redis
注解方式(JPA)
jpa是规范(接口),hibernate是其中的一套实现类而已
JPA:
单表操做:
环境搭建:
1.导入jar包
如果纯jpa操做.须要在以前包的基础之上额外导入一个 entitymanager.jar jar报目录:解压目录/lib/jpa/hibernate-entitymanager-5.0.7.Final.jar
2.编写核心配置文件(和以前hibernate.cfg.xml中内容大体相同)
名称:persistence.xml
路径:src/META-INF
导入约束:
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
</persistence>
内容:
数据库的单元
规范的实现提供商.(省略)
注解类的路径(省略)
数据库的链接信息.
hibernate的配置
导入c3p0配置
不用导入(隔离级别,事务session绑定线程配置)
注意:
最后修改一下 transaction-type="RESOURCE_LOCAL" 使用数据库本地事务
3.抽取工具类(相似于以前的hibernateUtils) JPAUtils
以前提供了一个私有的静态sessionfactory对象,今天提供一个 entityManagerFactory
以前还提供了一个获取session的方法,今天提供一个获取 EntityManager的方法
4.建立持久化类
类上的注解
@Entity //声明本身是一个持久化类
@Table(name="cst_customer") //声明对应的表
oid属性上的注解
@Id //声明它是oid
@Column(name="cust_id")//声明对应的字段名称 若字段名称属性名称一致的话name能够省略不写 或者Column注解直接不写
@GeneratedValue(strategy=GenerationType.IDENTITY)//声明主键生成策略
普通属性上的注解
@Column(name="cust_name")//声明对应的字段名称 若字段名称属性名称一致的话name能够省略不写 或者Column注解直接不写
5.入门代码
//开启事务
EntityTransaction tx = em.getTransaction();
tx.begin();
//保存
persist(obj);
经常使用的api
保存: persist(Object obj)
查询:
find(Class clazz,Object id); 当即查询
getReference(Class clazz,Object id); 延迟加载
更新:
方式1:快照方式
方式2:强制更新,将脱管态数据强制更新到数据库中 merge(Object obj)
删除:
remove();
查询的api:
jpql查询:Java持久化查询语言
获取查询对象
em.createQuery(jpql语句)
查询全部
getResultList()
getSingleResult()
分页查询
同样
排序查询
语句 + order by
统计查询
同样
条件查询
问号占位符
api
setParameter(int 第几个问号,Object obj)
投影查询
同样
多表的操做:
一对多
注意:属性名称尽可能使用驼峰式 尽可能不要出现"_"
1.建立持久化类
多的一方
/**
* 配置多对一 @ManyToOne
* targetEntity属性:一的一方的字节码对象,若在对方中已经配置过,能够省略不写.
*
* 配置外键字段信息@JoinColumn
* name属性:外键的字段名称
* referencedColumnName属性:指向的主表的键名称(通常是主键,如果主键的话能够省略不写的)
*
* 注意:
* @JoinColumn 和 mappedBy 互斥
*/
@ManyToOne(targetEntity=Customer.class)
@JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id")
private Customer customer;
一的一方
/**
* @OneToMany 配置一对多
* targetEntity属性:多的一方的字节码对象
* mappedBy属性:本身在多的一方中的属性名称(至关于放弃外键维护权)
* 如果双方都配置映射关系的话必须在放弃的一方使用.
*/
@OneToMany(targetEntity=Linkman.class,mappedBy="customer")
private Set<Linkman> linkmans = new HashSet<Linkman>();
2.测试保存
---------------
级联操做
在@OneToMany或者@ManyToOne标签上配置 cascade={CasCadeType.Persist | merge | all | remove}
默认删除:
就会报错.
咱们能够模拟一下模拟删除,具体状况看代码.
///////////////////////
多对多
建立持久化类
被动方
@ManyToMany
targetEntity属性:对方的字节码对象
mappedBy属性:本身在对方中的属性名称,放弃外键维护权
例如:
@ManyToMany(targetEntity=User.class,mappedBy="roles")
private Set<User> users = new HashSet<User>();
主动方
@ManyToMany
targetEntity属性:对方的字节码对象
@JoinTable :添加一张中间表
name属性:中间表表名
JoinColumns属性:本身在中间表中的外键信息 使用@JoinColumn
InverseJoinColumns属性:对方在中间表中的外键信息
例如:
@ManyToMany
@JoinTable(name="sys_user_role",
joinColumns={
@JoinColumn(name="user_num",referencedColumnName="user_id")
},
inverseJoinColumns={
@JoinColumn(name="role_num",referencedColumnName="role_id")
}
)
private Set<Role> roles=new HashSet<Role>();
sql