黑马程序员技术交流社区
标题: 【石家庄校区】hibernate知识库 [打印本页]
作者: zhangxuchang 时间: 2018-6-27 15:16
标题: 【石家庄校区】hibernate知识库
本帖最后由 小石姐姐 于 2018-6-29 11:09 编辑
环境搭建
1.导入jar包
1.导入相关jar包
拷贝hibernate-release-5.0.7.Final目录下lib/required目录下所有jar包
2.导入数据库驱动jar包
3.导入日志相关jar包
log4j-1.2.16.jar
slf4j-api-1.6.1.jar
slf4j-log4j12-1.7.2.jar
4.拷贝hibernate-release-5.0.7.Final\lib\optional\c3p0下所有jar包
c3p0-0.9.2.1.jar
hibernate-c3p0-5.0.7.Final.jar
mchange-commons-java-0.2.3.4.jar
5.将hibernate-release-5.0.7.Final目录下project/etc/log4j.properties文件拷贝到工程src目录下
2.提供核心配置文件 hibernate.cfg.xml (src目录下)
3.映射配置文件 类名.hbm.xml (与实体类在同一包下)
核心配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
//上边的需要复制粘贴
<hibernate-configuration>
<session-factory>
<!-- 配置关于数据库连接的四个项 driverClass url username password -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql:///hibernateTest</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">abc</property>
<!-- 设置连接提供者 -->
<property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
<!-- c3p0连接池的配置 -->
<property name="hibernate.c3p0.max_size">20</property> <!-- 最大连接池 -->
<property name="hibernate.c3p0.min_size">5</property> <!-- 最小连接数 -->
<property name="hibernate.c3p0.timeout">120</property> <!-- 超时 -->
<property name="hibernate.c3p0.idle_test_period">3000</property> <!-- 空闲连接 -->
<!-- 可以将向数据库发送的sql显示出来 -->
<property name="hibernate.show_sql">true</property>
<!-- 格式化sql -->
<property name="hibernate.format_sql">true</property>
<!-- hibernate的方言 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 自动创建表 -->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- 用于设置事务提交方式 -->
<property name="hibernate.connection.autocommit">false</property>
<!-- 配置hibernate的映射文件所在位置 -->
<mapping resource="cn/itheima/domain/Customer.hbm.xml" />
</session-factory>
</hibernate-configuration>
配置映射文件
<?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">
<!-- package属性代表javabean所在的包 -->
<hibernate-mapping>
<!--
name 实体类的全名
table 表的名称
catalog 数据库名称
-->
<class name="com.b3a4a.domain.Customer" table="t_customer" catalog="db_hibernate_study">
<!--id它是用于描述主键
name 实体类中那个属性对应的是主键
column 数据库表中哪一列是主键-->
<id name="id" column="id">
<!-- 主键生成策略 -->
<generator class="native"></generator>
</id>
<!--使用property来描述属性与字段的对应关系
name 实体类中的属性
column 实体类中属性对应数据库表中的列
length 该列的长度限制-->
<property name="name" column="name" length="20" type="string"></property>
<property name="address" column="address" length="50"></property>
<property name="sex" column="sex" length="20"></property>
字符串一定给length type可以不写
</class>
</hibernate-mapping>
执行原理
1.通过Configuration().configure();读取并解析hibernate.cfg.xml配置文件
2.由bibernaate.cfg.xml中的
<mapping resource="cn/itheima/domain/Customer.hbm.xml"/>读取解析映射文件
3.通过config.buildSessionFactory();得到sessionFactory
4.sessionFactory.openSession();得到session
5.session.beginTransaction();开启事务
6.具体业务操作
7.session.getTransaction().commit;提交事务(update insert delete)
8.关闭session session.close();
9.关闭session sessionFactory.close();一般不关闭
hibrenate api
1.Configuration 加载hibernate.cfg.xml核心配置
2.SessionFactory 接口, 创建Session对象, 不是轻量级的不关闭
3.Session 接口, 非线程安全,与数据库CRUD,save/update/delete
4.Transaction 事务接口, 管理事务
5.Query 接口, 查询操作 HQL/SQL
6.Criteria 接口, 面向对象标准化查询, 轻量级
详解
获得session:
1.sessionFactory.openSession();
这种方式获取的session不能被手动关闭, 不必再写session.close()
2.sessionFactory.getCurrentSession();
这种方式获得session每次都是新的,前提是本地线程绑定session
即在cfg.xml配置了<property name="hibernate.current_session_context_class">thread</property>
Session常用方法:
save/update/delete
get/load 根据id进行查询
saveOrUpdate 执行save或update操作
createQuery() 获取Query对象
createSQLQuery() 获取一个可以操作sql的SQLQuery对象
createCriteria() 获取一个Criteria 查询操作
Transaction:
commit 事务提交
rollback 回滚
session.beginTransaction(); 开启事务, 获得Transaction对象
如果程序没有开启事务, 是否存在事务?
有事务, session的每一个操作就会开启一个事务.
Query:
session.createQuery(hql);
session.createSQLQuery(sql);
Query查询操作Hibernate注解开发在hibernate中我们使用注解,可以帮助我们简化伙伴们文件配置
PO类注解配置
@Entity 声明一个实体
@Table来米哦啊书类与表对应
@Table(name="t_book",catalog="hibernateTest")
public calss Book{@Id 来声明一个主键
@GenerateValue 用它来声明一个主键生成策略
//@GeneratedValue //native
@GeneratedValue(strategy = GenerationType.IDENTITY) //identiy默认情况下相当于native
可以选择的主键生成策略 AUTO IDENTITY SEQUENCE@Column 来定义列
@Column(name="c_name",length=30,nullable=true)注意:对于PO类中所有属性,如果你不写注释,默认情况下也会在表中生成对应的列,列的名称就是属性的名称。
@Temporal来声明日期类型
@Etmporal(TemporalType.TIMESTAMP) //是用来定义日期类型
private Date publicationDate; //出版日期可以选择
TemporalType.DATAY 只有年月日
TemporalType.TIME 只有小时分钟秒
TemporalType.TIMESTAMP 有年月日小时分钟秒
@Type:可允许你去指定Hibernate里面的一些类型
@Type(type="double") //允许你去指定Hibernate里面的一些类型
private Double price;//价格,如果没有添加注解,也会自动的生成在表中编写完上述Book实体类之后,千万不要忘记下面这一步,我们最终需要在hibernate.cfg.xml 文件中将我们类中的注解配置引用生效
<mapping class="cn.itheima.domain.Book"/>
问题:1.如果我们主键生成策略想使用UUID类型?
@Id
@GenericGenerator(name=“myuuid”,strategy="uuid")
@GeneratedVAlue(generator="myuuid")
private String id;问题2:如果设定类的属性不在表中映射?
@Transient
private String msg; // 现在这个属性不想生成在表中对于我们以上讲解的关于属性配置id注解,我们也可以在其对一个的getxxx方法去使用
一对多(多对一)@OneToMany
一对多:
rargetEntity: 多的一方类型
mappedBy:放弃外键维护
orphanRemoval:删除孤儿
@ManyToOne
多对一:
rargetEntity:一的一方的类型
以Customer与Order为例
客户(Customer) 类
@OneToMany(targetEntity=Order.class,mappedBy="c")
private Set<Order> orders = new HashSet<Order>();订单(Order) 类
@ManyToOne(targetEntity=Customer.class)
@JoinColumn(name="c_customer_id")//指定外键列
private Customer c; //描述订单属于一个客户示例:保存客户时,保存订单
对于这个示例我们需要在Customer中配置cascade操作,save-update
第一种方式,可以使用 JPA 提供的注解
@OneToMany(targetEntity=Order.class,mappedBy="c",cascade=CascadeType.ALL)
private Set<Order> orders = new HashSet<Order>();第二种方式:可以使用hibernate提供的注解
@OneMany(targetEntity=Order.class,mappedBy="c")
@Cascade(CascadeType.SAVE_UPDATE)
private Set<Order> orders=new HashSet<Order>();下面是示例代码
//3.建立关系
c.getOrders().add(o1);
c.getOrders().add(02);
//4.保存客户,并级联保存订单
session.save(c);订单中没有关联客户的id,为什么?
原因:我们在Customer中配置了mappedBy=“c”它代表的是外键的维护由Order方来维护,而Customer不维护,这时你在保存客户时,级联保存订单,是可以的,但是不能维护外键,所以,我们必须在代码中添加订单与客户关系。
多对多@Cascade配置级联:
CascadeType.xxx
一对一@OneToOne 声明一对一外键映射(主键映射了解)
检索方式导航对象图检索方式,根据已加载的对象导航到其他对象
OID检索方式,按照对象的OID来检索对象(get/load)
HQL检索方式,使用面向对象的HQL插叙语句
QBC检索方式,使用QBC(Query by Criteria)API来检索对象,这种API封装了基于字符串形式的查询语句,提供了更加米面向对象的查询接口:
基本检索:
Criteria criteria=session.createCriteria(Customer.class);
List<Customer>list=criteria.list();
排序检索:
cruteria.addOrder(org.hibernate.criteriion.Order.asc("money"));//升序
条件检索:
Restrictions.xxx()eq and or like get it....
分页检索:
criteria.setFirstResult(2-1)*6);
criteria.setMaxResults(6);
统计检索:
criteria.setProjection( projections.projectionList().add(Projections.sum("money")).add(Projections.groupProperty("c")));
离线条件检索: DetachedCriteria dc=DetachedCriteria.forClass(Customer.class);不借助于session web
Criteria criteria=dc.getExecutableCriteria(session);dao
本地SQL检索方式,使用本地数据库的SQL查询语句
Hibernate-day04先来说明一件问题演示sql多表操作交叉连接 CROSS JOIN 会产生迪卡尔积,一般不用
select * from t_customer cross join t_order;内链接 inner join on
SELECT * FROM t_customer AS c INNER JOIN t_order AS o ON c.id=o.c_customer_id;
隐式内连接 使用 "逗号"将表分开,使用WHERE来消除迪卡尔积
SELECT * FROM t_customer AS c ,t_order o WHERE c.id=o.c_customer_id;外连接,分为左外连接 left outer join ,右外连接 right outer join
OUTER可以省略
SELECT * FROM t_customer c LEFT OUTER JOIN t_order o ON c.id=o.c_customer_id;
再就是hql的多表操作,hql的迫切连接很重要,但是在sql中是没有的注意说明
fetch不可以与单独条件的with一起使用
fetch这个关键字,可以说是把结果封装到对象中去,底层也是执行inner join/left outer join
distinct这个关键字,只要是用来去重的,sql中也有,我们使用迫切连接结果可能会出现重复,所以要用他
注意测试时候,要在类中重写toString方法哦
也有交叉连接.这个就不多说了
内连接
显示内连接,使用的是inner join with
from Customer c inner join c.orders with c.id=1
返回结果是List<Object[]> 需要两层遍历隐式内连接,与sql中操作不一样,它是通过”.”运算符来关联
from Order o where o.c.id=1迫切内连接,它得到的结果是直接封装到PO类中,而内连接得到的是Object[]数组,数组中封装的是PO类对象.
select distinct c from Customer c inner join fetch c.orders
外连接
左外连接
select * from 表1 a left join 表2 b on 表的关联条件 where 条件右外连接
select * from 表1 a right join 表2 b on 表的关联条件 where 条件迫切左外连接
select distinct c from Customer c left outer join fetch c.orders where c.id=1
Hibernate事务管理(非常重要,面试必问)事务介绍问题:什么是事务?
问题:事务四个特性?
问题:不考虑事务的隔离性,会产生什么问题?
脏读:一个事务读取到另一个事务的未提交数据
不可重复读:一个事务读取到另一个事务提交的数据(主要是指update),会导致两次读取的结果不一致。
虚读(幻读): 一个事务读取到另一个事务提交的数据(主要是指insert),会导致两次读取结果不一致.
问题:对于上述问题如何解决?
我们可以通过设置隔离级别来解决.
READ_UNCOMMITED 读取未提交,它引发所有的隔离问题
READ_COMMITTED 读已提交,阻止脏读,可能发生不可重复读与虚读.
REPEATABLE_READ 重复读 阻止脏读,不可重复读 可能发生虚读
SERIALIZABLE 串行化 解决所有问题 不允许两个事务,同时操作一个目标数据。(效率低下)
事务提交方式
<!-- 用于设置事务提交方式 -->
<property name="hibernate.connection.autocommit">false</property>在Hibernate中设置事务管理级别它可取的值有 1 2 4 8
1 代表的事务隔离级别为READ UNCOMMITTED
2 代表的事务隔离级别为READ COMMITTED
4 代表的事务隔离级别为 REPEATABLE READ
8 代表的事务隔离级别为 SERIALIZABLE
在hibernate.cfg.xml文件中配置
<!-- 设置事务隔离级别 -->
<property name="hibernate.connection.isolation ">4</property>
Hibernate中session的管理,Hibernate提供了三种管理session的方式:Session对象的生命周期与本地线程绑定(ThreadLocal)
sessionFactory.getCurrentSession();<!-- 绑使用当前线程 -->
<property name="current_session_context_class">thread</property>Session对象的生命周期与JTA事务绑定(分布式事务管理)
这里只稍微概述下,后期会学到哦,有个小概念
JTA提供了跨Session的事务管理能力,这是与JDBCTransaction最大的差异。
JDBC 事务由Connection管理,也就是说,事务管理实际上是在JDBC Connection中实现,事务周期限于
Connection的生命周期之内,对于基于JDBC Transaction的Hibernate事务管理机制,事务管理在
Session所依托的JDBC Connection中实现,事务周期限于Session的生命周期。
JTA事务管理由JTA容器实现,JTA容器对当前加入事务的众多Connection进行调度,实现其事务性要求,
JTA的事务周期可横跨多个JDBC Connection生命周期,同样,对基于JTA事务的Hibernate,JTA事务横跨
多个Session。需要注意的是,参与JTA事务的 Connection需避免对事务管理进行干涉,如果采用JTA
Transaction,就不应该再调用Hibernate的Transaction功能。
Hibernate委托程序来管理Session的生命周期
sessionFactory.getOpenSession();
Hibernate优化方案HQL优化1.使用参数绑定2.尽量少使用NOT3.尽量使用where来替换having4.减少对表的查询5.使用表的别名6.实体的更新与删除一级缓存优化注解配置抓取策略set上的fetch与lazy1.查询所有
使用HQL
2.分页查询
query.setFirstResult(10);
query.setMaxResult(10);
3.查询指定列信息****(查询属性)
和HQL投影检索一样
先要在Customer实体类生成name,address的有参构造,并加无参构造.
select new Customer(name,address) from Customer;
将属性封装到对象中
4.条件查询
from Customer where name=?
query.setParameter(0,"张三") 对参数进行赋值, hql是从0开始的
如果查询结果是唯一的, query.uniqueResult();
5.执行本地SQL
sqlQuery=session.createSQLQuery("select*from t_customer");
把查询到的sql语句结果封装到Customer对象中
sqlQuery.addEntity(Customer.class);
如果sql中有参数, 使用setParameter()方法完成参数传递
6.Criteria:
适合多条件查询
criteria.add(Restrictions.eq("name","xxx"));
查询姓名为张三或者address为上海
criteria.add(Restrictions.or(Restrictions.eq("name","张三"),Restrictions.eq("address","上海")));
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) |
黑马程序员IT技术论坛 X3.2 |