本节重点:
1、hibernate映射文件
2、hibernate核心配置文件
3、hibernate核心类
1、hibernate(orm)入门(实体类+映射文件+核心配置文件)
1)orm的理解
orm(object relation mapping )
对象的名字 表名
对象的属性名称 列名
对象属性类型 列的类型
2)hibernate工程搭建步骤:(一个实体+一个映射文件+一个核心配置文件)
准备工作:数据库表
第一步:导入/lib/required/目录下的jar+mysql连接驱动+日志相关jar
第二步:创建实体Customer(类不能加上final+有主键+提供get/set方法+无参构造)
第三步:创建实体映射文件Customer.hbm.xml(hibernate-mapping/class/id+property)
第四步:创建核心配置文件hibernate.cfg.xml(hibernate-configuration/session-factory/property)
注意:xml文件头信息需要手动复制一下,解决xml红色圈圈以及提示需要手动在本地引入约束文件
3)测试操作数据
Configuration-->SessionFactory-->Session(get/load,save,update,delete)
save(customer);
update(customer);
delete(customer);
get(xx.class,1l);
load(xx.class,1l);
2、详细讲解:映射文件+核心配置文件
映射文件Customer.hbm.xml:
<hibernate-mapping>
<class>
<id name="" column="">
<generator class="native/sequence/identity"/>
</id>
<property name="" column=""></property>
</class>
<hibernate-mapping>
核心配置文件hibernate.cfg.xml:
<hibernate-configuration>
<session-factory>
<property name="" column=""></property>
<mapping resource=""/>
</session-factory>
<hibernate-configuration>
注意:hibernate核心配置文件中hibernate.hbm2ddl.auto=create/update
create: 每次都会创建新的表,一般测试中使用
update: 数据库中没有表,就创建表;
表结构和我们定义的实体映射文件表结构是否一致,不一致就做alter table操作
3、hibernate核心类(Configuration+SessionFactory+Session+Trasaction,首先理解,然后再去操作怎么得到该对象)
1)Configuration(加载配置文件的配置)
//默认去加载src下面的hibernate.cfg.xml文件,在加载核心配置文件的时候要保证加载实体映射文件
Configuration configure=new Configuration().configure();
2)SessionFactory(生产Session的工厂)
sessionFactory=configuration.buildSessionFactory();
====================================================================================================================================
1、开发规律:4个方面
请求路径
form表单 action写路径 表单提交
超链接 <a href="xxx"> 点击超链接
点击按钮 调用一个js函数 js函数里面可以 $.get() $.post() $.ajax()
请求方式
post/get put/delete
请求参数
从页面传递,java类获取
form表单 name="xxx" post
url带参数 xxxx?username=aaaa&password=123456 get
js里面 $.get() $.post() $.ajax()都可以带参数
传统方式:request.getParameter("参数名");
Struts2框架属性驱动/模型驱动
响应数据
传统方式:response.getWriter().write("json字符串");
Struts2:把数据放到值栈, result type="json"
2、存值取值:
java类把数据放到某个地方,页面获取
传统方式:域对象 request 页面通过${}
Struts2:把数据放到值栈 页面通过Struts2标签+Ognl表达式
push(对象); <s:property value="[0].top" />
set("键",对象); <s:property value="键的名字" />
list集合 <s:iterator />
map集合 <s:iterator />
3、注解
一个一个对应xml配置
4、hibernate
xml文件有红色xx jar包导入/dtd约束文件/xml配置节点是否正确
准备工作:准备数据库 表
建工程,导jar包
映射文件 实体类与表
核心配置文件 关联映射文件
Configuration 加载配置核心配置 hibernate.cfg.xml
核心配置加载映射文件,框架才知道哪个类与哪个数据的哪张表对应
创建SessionFactory
获取Session
开启事务
增删改查
提交事务
关闭Session
本节重点:
1、hibernate核心类
2、持久态对象的状态
3、持久态对象方法
3)Session(操作数据库的Session会话)
Session是一个非线程安全的对象 与线程绑定
造成线程不安全问题=多线程+全局变量
怎么解决Session的线程不安全问题?
解决方法一:在方法内部创建Session
解决方法二:getCurrentSession()
其他办法:使用ThreadLocal,变量副本
Session session=sessionFactory.openSession();// 操作完数据库之后需要手动关闭session
Session session=sessionFactory.getCurrentSession(); // 从当前线程中获取session,session操作完数据库之后,不需要手动关闭session
save 保存对象
update 修改对象
delete 删除对象
get/load 根据id进行查询
saveOrUpdate 执行save或update操作
createQuery() 获取一个Query对象
createSQLQUery()获取一个可以操作sql的SQLQuery对象
createCriteria() 获取一个Criteria它可以完成条件查询
4)Transaction(操作数据库的事务)
(1)如果不手动开启事务,默认一个session操作就是一个事务,
hibernate默认是hibernate.connection.autocommit=false也即并非自动提交事务
怎么证明?在hibernate.cfg.xml中配置<property name="hibernate.connection.autocommit">true</property>
然后发现即便代码中有异常,异常之前的一个事务还是正常提交了
(2)如果手动开启事务,那就从开启事务到事务提交中间的整个操作都是一个事务
(3)基本操作
// 开启事务,获取的事务对象
Transaction transaction=session.beginTransaction();
//事务提交
transaction.commit();
//事务回滚
transaction.rollback();
5)Query(hql查询接口)
HQL:hibernate的query语言(面向对象的角度去理解这个类似于sql的查询化语言)
HQL语句中根本看不到跟表相关任何信息
Query query =session.createQuery(hql);
hql: select name,address from Customer;
2种方式可以封装数据:
第1种:通过List<Object[]>,这里Object[]封装了name和address值
第2种:通过投影的方式 new Customer(name,address)
// int pageNo = 1 int pageSize=10
setFirstResult((pageNo-1)*pageSize)
setMaxResults(pageSize)
与msyql的limit分页传参一致
setParameter()给?设置值,从0开始
6)SQLQuery(原生sql查询接口)
SQLQuery sqlQuery=session.createSQLQuery("select * from t_customer");
使用List<Object[]> 或者 sqlQuery.addEntity(Customer.class);去封装查询数据
7)Criteria(条件接口QBC)
Criteria criteria=session.createCriteria(Customer.class);
Restrictions 条件有关都封装进去了,语法参照sql写法
1、hibernate中的持久化实体类书写要求(使用hibernate框架注意书写实体的规范)
1)实体类不要使用final(因为hibernate会根据实体产生代理对象)
2)实体类中有唯一的OID来与表中主键对应(hibernate中缓存要使用)
3)尽量使用包装类型而不是普通类型(一些基本类型属性插入表中都会将默认值存入,而包装类型默认值是null)
4)实体类成员变量提供get和set方法(标准的jpa要求属性提供get和set)
5)提供public无参构造(hibernate和jpa需要)
注意:get和load的区别
(1)get:采用的是立即加载,执行到该行代码的时候,马上发送SQL语句进行查询。
load:采用的是延迟加载(lazy),执行到该行代码的时候,不会马上发送SQL语句,
只有真正使用这个对象的时候(使用这个对象的普通属性的时候)才会发送SQL语句。
(2)get:查询之后返回的是真实对象本身,对象没有查找到返回null。
load:查询之后返回的是一个代理对象,是我们持久化实体类的子类,
查询不到的对象会出现一个exception。
2、主键类型和主键生成策略
1)主键类型
自然主键和代理主键
居民表1:identityCardId(身份证id),name(姓名)
居民表2:id(主键),identityCardId(身份证id),name(姓名)
居民表1中:身份证id是自然主键
居名表2中:id是代理主键
注意:开发中采用代理主键!
2)主键生成的常用类型(主键生成策略)
主键赋值,是谁去负责给主键赋值?
hibernate 数据库自己去赋值 自己手动去给主键赋值
increment/uuid identity/sequence/native assigned
负责代理主键:identity(就是mysql auto_increment)/sequence(oracle中序列)/native/increment/uuid
负责自然主键:assgined
create sequence seq_mysequence;
seq_mysequence.currVal
seq_mysequence.nextVal
注意:在实际过程使用最多的是identity/sequence/native/uuid(给所在表主键赋值,该主键的类型必须是字符串)
3、持久化对象的三种状态(必须理解)
1)三种状态(从2个维度去衡量一个持久对象的状态:session管理+对象OID是否有值)
瞬时态(没有oid值,没有交给session管理)
持久态(有oid值,交给了session管理) 重点理解
游离态/脱管态(有oid值,没有交给session管理)
oid的值:创建实体中跟表主键映射的成员变量是否有值
session管理:就是session去操作的所在范围
2)三种状态之间的切换
瞬时态转换成持久态:save(),saveOrUpdate()
游离态/脱管态转换成持久态:update(),saveOrUpdate()
其他情况转换成持久态:get()/load()/find()/qbc 查询
4、hibernate中的一级缓存(本地要去测试) Tair pair
1)持久化对象为什么具有自动更新数据库的能力?
session对象有一级缓存和快照,通过watch查看session
一级缓存(缓存实体对象):
session -> persistenceContext -> entityEntryContext -> head -> entityInstance
快照(将缓存数据copy一个副本):
session -> persistenceContext -> entityEntryContext -> head -> entiryEntry -> loadedState
原理:底层就是将缓存数据进行修改,提交事务时对比一级缓存和快照区是否相同,
如果不同就会发update操作,以一级缓存的数据为准
2)一级缓存常用的操作
(1)只有持久化态对象才会放入到session一级缓存中
(2)clear/evict/refresh介绍
clear(): 清除session 一级缓存
evict(c):清除session 一级缓存中指定的对象
refresh(c):从数据库同步到缓存中去
close():session已经关闭,当然会清空session一级缓存
3)持久化对象去操作数据库
get()/load()/Query的list查询/
save()/update()/saveOrUpdate()
saveOrUpdate(){
if(oid!=null){
update();
}else if(oid==null){
save();
}
}
以上操作都会将数据放到一级缓存中,此时我们可以理解成这些对象交给了session管理
注意:(1)在实际开发过程中,对于update,delete操作,我们一般采取的步骤是先查询
再去做操作,而不是创建一个对象通过赋值id来操作
(2)通过实验证明OID要唯一,OID要与数据库记录对应
====================================================================================================================================
本节重点:
1、一对多
2、inverse 与 cascade
3、注解一对多 多对多
5、hibernate一对多关联配置及其操作(理解)
专业术语理解
级联:cascade <set cascade="save-update">
维护:inverse <set inverse="true">
1)一对多关联实体及其映射文件配置(必须掌握)
Order private Customer c;
Customer private Set<Order> orders;
Order.hbm.xml
<many-to-one class="对方实体类完整类路径" column="外键名字">
Customer.hbm.xml
<set name="orders">
<key column="外键名字" />
<one-to-many class="对方实体类完整类路径" />
</set>
2)双向关联操作(理解)
// 订单关联客户
o1.setC(c);
o2.setC(c);
// 客户关联订单
c.getOrders().add(o1);
c.getOrders().add(o2);
session.save(o1);
session.save(o2);
session.save(c);
(1)如果不配置inverse或者是配置inverse="false",此时两方都在维护外键,就是给外键设置值
(2)如果我们在一的一方配置inverse="true",外键由对方来维护
(3)外键在哪边,就由哪边维护,多的一方维护
3)单向关联操作
情况一:
// 订单关联客户
o1.setC(c);
o2.setC(c);
// 保存订单
session.save(o1);
session.save(o2);
报错: org.hibernate.TransientObjectException: object references an unsaved transient instance
- save the transient instance before flushing: cn.itheima.oneToMany.Customer
持久态对象引用了瞬时态对象
怎么理解?瞬时态对象在数据表里没有对应记录生成,当然也没有OID
最后提交事务,订单表中的外键值没有
怎么解决?
(1)手动保存
(2)级联保存 cascade 保存A的时候也保存B,级联配置在A
情况二:
// 客户关联订单
c.getOrders().add(o1);
c.getOrders().add(o2);
// 保存客户
session.save(c);
4)对象导航(cascade理解)
实验前提:两方都设置了cascade="save-update"
有几条insert语句,要看有多少关联
o1.setC(c);
// c要关联o2 o3
c.getOrders().add(o2);
c.getOrders().add(o3);
session.save(o1);
5)级联操作(理解)
在Order.hbm.xml <many-to-one cascade="save-update" > 保存Order会保存Customer
在Customer.hbm.xml <set cascade="save-update" >保存Customer会保存Order
结论:在A配置cascade="save-update",就是保存A的时候也保存B
在A配置cascade="delete",就是删除A的时候也删除B中跟A有关联的记录
在A配置cascade="delete-orphan",删除与当前对象解除关系的对象
hibernate第3天总结(注解)
1、注解实现一对多(重点)
1)常用注解
(1)表相关映射注解:@Entity @Table
(2)主键相关映射注解:@Id @GeneratedValue 主键生成策略
@GenericGenerator(name = "myuuid", strategy = "uuid")
@GeneratedValue(generator = "myuuid")
(3)列相关映射注解:
改变表中列名+改变列长度+表中列约束:@Column
改变列的类型:@Type @Temporal
(4)@Transient:该列不会生成在数据表中
2)注解实现一对多
(1)在hibernate.cfg.xml文件中去加载映射文件<mapping class="cn.itheima.domain.Book" />
(2)Customer在一的一方:@OneToMany mappedBy="c" 这里的c要与Order里面的private Customer c对应
(3)Order在多的一方:@ManyToOne @JoinColumn声明外键
2、注解实现多对多(重点)
Student @ManyToMany
Teacher @ManyToMany
声明中间表:@JoinTable(@JoinColumn,@JoinColumn)
====================================================================================================================================
回顾:
1、什么叫维护外键?
就是保证外键有值
一个字段在B表中是外键,一定在A表中是主键,B表的这个外键
是根据A表的主键来生成的,A表的主键一定要有值,否则B表这个外键生成不了
概念:(父)主表A和(子)从表B,外键在哪个表,哪个表就是从表
2、为什么要维护外键?
保证数据的完整性
3、谁来维护外键?怎么维护外键?
默认是两方都来维护,并且多的一方一直都会有维护的权利
多的一方维护,参考hibernate-demo3/test2
o1.setC(c);
o2.setC(c);
session.save(o1);
session.save(o2);
维护的时候为什么要有级联?
因为一个字段在B表中是外键,一定在A表中是主键,B表的这个外键
是根据A表的主键来生成的,主键都没有值,外键怎么产生呢?所以要加级联
一的一方维护,参考hibernate-demo3/test3
c.getOrders().add(o1);
c.getOrders().add(o2);
session.save(c);
维护的时候为什么要有级联?
因为一的一方在维护,t_order表的记录都没有生成,怎么维护?所以要加级联
4、inverse在所有实验中可以最后考虑,一般在一的一方加上inverse="true"
get/load
private class A{
private Set<B> b;
private String name;
private C c;
}
本节重点:
1、注解配置一对一
2、查询的3种方式(HQL、QBC、原生sql)
3、HQL连接查询(内连接/外链接、迫切内连接/迫切左外链接)
3、注解实现一对一(重点)
User:@OneToOne(targetEntity = IDCard.class, mappedBy = "user")
IdCard:@OneToOne @JoinColumn(name = "c_user_id")
1)借助外键来实现一对一(非常常用)
2)借助主键来实现一对一(了解)
所有注解有个规律:mappedBy和JoinColumn是互斥的
4、检索方式总结(查询数据的5种方式)(重点)
1)对象导航查询
Customer customer=session.get(Customer.class,2);
customer.getOrders().size();
2)根据主键查询
session.get(Customer.class,2)/load(Customer.class,2)
3)HQL查询(通过Query查询)
select new Customer(name,money) from where group by order by
Query query=session.createQuery(HQL);
基本查询:
排序:from Order order by money desc
条件:from Order where money >?/:mymoney
分页:setFirstResult()/setMaxResults()
统计分组:select sum(money) from Order group by c
投影:select new Customer(name,address) from Customer; List<Object[]>
命名(了解):@NamedQuery Query query=session.createNamedQuery("")
常用的方法:list(),setParameter(序号从0开始),setFirstResult()/setMaxResults(),uniqueResult()
4)QBC查询(通过Criteria查询) Order(排序)、Restrictions
Criteria criteria=session.createCriteria(实体对象.class);
Restrictions.gt()/eq()/like()/
criteria.and()
criteria.add()
criteria.setProjection(Projections)
基本查询:
排序:Order
条件:Restrictions
分页:setFirstResutlt() setMaxResults()
统计分组:Projections
离线查询:(了解)
Criteria常用的方法:list(),add(),addOrder(),setProjection()
5)SQL查询(通过SQLQuery查询,原生sql)
SQLQuery sqlQuery=session.createSQLQuery(sql);
SQLQuery对象常用的方法:addEntity(xx.class)
回顾内连接和外连接:
内连接:
交叉连接:select * from emp e cross join dept d;
显式内连接:select * from emp e inner join dept d on e.deptno=d.deptno;
隐式内连接:select * from emp e , dept d where e.deptno=d.deptno;
外连接:
A a left join B b on a.col1=b.col2 outer可以省略
A a right join B b on a.col1=b.col2
orcle:
A a , B b where a.col1(+)=b.col2
A a , B b where a.col1 =b.col2(+)
select d.deptno 部门编号,d.dname 部门名称,count(e.empno) 部门总人数 from emp e, dept d where e.deptno(+)=d.deptno
group by d.deptno,d.dname order by d.deptno;
1、HQL 连接(通过"."来关联另外一个表,使用fetch 查询数据封装到from 后面对象中)(掌握)
1)显示内连接
from Customer c inner join c.orders with xx /where xx
使用with 底层使用 t_customer c inner join t_order o on c.id=o.id and (xx)
使用where底层使用 t_customer c inner join t_order o on c.id=o.id where xx;
查询的结果是对象:Customer Order
注意下面这种写法语法报错:
String hql = "from Customer c inner join Order o with c.id=1"
2)隐式内连接(了解)
from Customer c where c.orders.id=xxx;
隐式内连接不能写成 from Customer c ,c.orders (错误写法)
注意下面这种写法语法报错:
String hql = "from Order o,o.c where o.c.id=1"
3)迫切内连接
from Customer c inner join fetch c.orders
fetch将查询的数据封装到Customer中,迫切想得到Customer
内连接与迫切内连接的区别:
(1)inner join/ inner join fetch
(2)内连接查询得到的结果是List<Object[]>
迫切内连接查询得到的结果是List<Order>/List<Customer>,集合里是from后面的对象
(3)实际应用的时候根据需要选择使用
4)外连接
from Customer c left outer join c.orders outer可以省略
查询的结果是:List<Object[]>
5)迫切左外连接
from Customer c left outer join fetch c.orders outer可以省略
fetch将查询的数据封装到Customer中,迫切想得到Customer
注意:fetch不能和with联合使用
2、hibernate事务和session配置(了解)
1)hibernate事务管理
hibernate支持的事务的隔离级别
1 Read UnCommitted
2 Read Committed 是oracle默认的隔离级别
4 Repeatable Read 是mysql默认的隔离级别
8 Serializable
<property name="hibernate.connection.isolation">4</property> //代表当前隔离级别是Repeatable Read
2)hibernate的session管理(在分布式环境下,session很容易造成不安全问题)
在hibernate.cfg.xml中配置
<property name="hibernate.current_session_context_class">thread</property>
掌握下面这种写法:
Session session=sessionFactory.getCurrentSession();//从线程中拿到session是安全的
3、查询分类(理解)
1)类级别查询
Customer customer=session.get(Customer.class,1L);
Customer customer$=session.load(Customer.class,1L);
所谓类级别查询,就是执行查询某一个类的数据
load是延迟加载查询的,其底层是采用代理对象来完成延迟加载,所以Customer不要加final
默认的类级别的延迟属性lazy=true,可以在类上配置注解@Proxy(lazy="true")
或者映射文件上配置<class lazy="true">
延迟加载对象初始化:Hibernate.initialize(c1);
2)关联级别查询
c.getOrders().size()
====================================================================================================================================
本节重点:
1、fetch+lazy
2、hibernate总结及规律
4、优化:查询策略(抓取策略) 理解:fetch+lazy
1)配置fetch和lazy
(1)fetch和lazy
fetch能够指定sql语句的格式:
select 多条简单的sql,默认值
join 采用迫切左外连接
subselect 将生成子查询的sql
lazy能够控制sql语句何时发出,是性能问题
(2)在映射文件中取值,有相应注解对应
<set fetch="select,join,subselect" lazy="true,false,extra"></set>
<many-to-one fetch="join,select" lazy="false,proxy,no-proxy"/>
<one-to-one fetch="join,select" lazy="false,proxy,no-proxy" />
(3)企业中常用
应用场景:
商品列表 延迟加载
商品详情页 立即加载
得到商品关联的促销活动的总数 加强的延迟加载
多加载一的时候,立即加载:
@Fetch(FetchMode.JOIN)
@LazyToOne(LazyToOneOption.FALSE)
private Customer c;
一加载多的时候,延迟加载:
@Fetch(FetchMode.SELECT)
@LazyCollection(LazyCollectionOption.TRUE)
private Set<Order> orders = new HashSet<Order>();
2)批量查询
配置BatchSize目的是解决根据一方查询另外一方的时候,减少sql语句查询将需要被查询对象的id放入到in语句
配置BatchSize原则:
在主表上配置
查询Customer的时候把Order查询出来
@BatchSize(size=3)
private Set<Order> orders = new HashSet<Order>();
查询Order的时候把Customer查询出来
@BatchSize(size=3)
public class Customer
hibernate核心总结:
1、hibernate项目独立完成
1)实现的步骤
第一步:导入/lib/required/目录下的jar+mysql连接驱动+日志相关jar+lib\optional\c3p0下包
第二步:创建实体Customer(类不能加上final+有主键+提供get/set方法+无参构造)
第三步:创建实体映射文件Customer.hbm.xml(hibernate-mapping/class/id+property)
第四步:创建核心配置文件hibernate.cfg.xml(hibernate-configuration/session-factory/property)
2)hibernate的核心api
Configuration config=new Configuration().configure();
SessionFactory sessionFactory=config.buildSessionFactory();
Session session=sessionFactory.openSession();/getCurrentSession();
session.beginTransaction();
session.get()/load()/save()/update()/delete()
session.getTransaction().commit();
session.close();
2、查询
1)HQL(select from where group by order by)
Query query=session.createQuery("from Customer");
query.list()/setFirstResult()/setMaxResults()/uniqueResult()/setParameter()/
List<Object[]>
投影:select new Customer(name,address) from Customer;
2)QBC(Criteria)
Criteria criteria=session.createCriteria(Customer.class);
criteria.add(criteria...)
Restrictions
Projections
3)sql(SQLQuery)
SQLQuery sqlQsuery=session.createSQLQuery(sql);
addEntity(Customer.class);
3、一对多的维护外键和级联
cascade:级联
inverse:反转
4、一对多、一对一、多对多注解配置
把握规律:
1)有外键的一方配置JoinColumn,另一方配置mappedBy=""
2)保存的时候,保存有外键的这一方,级联保存另一方