hibernate
hibernate常用配置以及简单使用
整理了网上的一些资料和自己对于hibernate的理解,供自己或者刚接触这个框架的朋友参考使用
hibernate有两个配置文件
1.核心配置文件hibernate.cfg.xml
2.映射文件(ORM元数据)XXX.hbm.xml
下面来分别介绍两个配置文件中的元素和配置文件的作用
1.核心配置文件
核心配置文件放在src目录下
核心配置文件中配置了数据库驱动,数据库地址,数据库方言等信息
下面介绍几个常用元素
数据库连接的相关配置
<!-- 数据库驱动 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- 数据库url -->
<property name="hibernate.connection.url">jdbc:mysql:///test</property>
<!-- 数据库连接用户名 -->
<property name="hibernate.connection.username">admin</property>
<!-- 数据库连接密码 -->
<property name="hibernate.connection.password">1111</property>
数据库方言
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
打印和格式化sql
<!-- 将hibernate生成的sql语句打印到控制台 -->
<property name="hibernate.show_sql">true</property>
<!-- 将hibernate生成的sql语句格式化(语法缩进) -->
<property name="hibernate.format_sql">true</property>
自动建表
<!--
#hibernate.hbm2ddl.auto create 自动建表.每次框架运行都会创建新的表.以前表将会被覆盖,表数据会丢失.(开发环境中测试使用)
#hibernate.hbm2ddl.auto create-drop 自动建表.每次框架运行结束都会将所有表删除.(开发环境中测试使用)
#hibernate.hbm2ddl.auto update (推荐使用) 自动生成表.如果已经存在不会再生成.如果表有变动.自动更新表(不会删除任何数据).
#hibernate.hbm2ddl.auto validate 校验.不自动生成表.每次启动会校验数据库中表是否正确.校验失败.
-->
<property name="hibernate.hbm2ddl.auto">update</property>
引入映射文件
<!-- 引入orm元数据
路径书写: 填写src下的路径
-->
<mapping resource="cn/test/pojo/Customer.hbm.xml" />
2.映射文件
映射文件可以和实体类放在同一个包下,配置表与实体对象的关系映射
<!-- package属性:填写一个包名.在元素内部凡是需要书写完整类名的属性,可以直接写简答类名了. -->
<hibernate-mapping package="cn.test.pojo" >
<!--
class元素: 配置实体与表的对应关系的
name: 完整类名
table:数据库表名
-->
<class name="Person" table="test_person" >
<!-- id元素:配置主键映射的属性
name: 填写主键对应属性名
column(可选): 填写表中的主键列名.默认值:列名会默认使用属性名
type(可选):填写列(属性)的类型.hibernate会自动检测实体的属性类型.
每个类型有三种填法: java类型|hibernate类型|数据库类型
not-null(可选):配置该属性(列)是否不能为空. 默认值:false
length(可选):配置数据库中列的长度. 默认值:使用数据库类型的最大长度
-->
<id name="person_name" >
<!-- generator:主键生成策略
identity : 主键自增.由数据库来维护主键值.录入时不需要指定主键.
increment:hibernate维护主键,这个不使用,有线程安全问题.
sequence: Oracle中的主键生成策略.
hilo: 高低位算法.主键自增.由hibernate来维护.开发时不使用.
native:hilo+sequence+identity 自动三选一策略.
uuid: 产生随机字符串作为主键. 主键类型必须为string 类型.
assigned:自然主键生成策略. hibernate不会管理主键值.由开发人员自己录入.
-->
<generator class="native"></generator>
</id>
<!-- property元素:除id之外的普通属性映射
name: 填写属性名
column(可选): 填写列名
type(可选):填写列(属性)的类型.hibernate会自动检测实体的属性类型.
每个类型有三种填法: java类型|hibernate类型|数据库类型
not-null(可选):配置该属性(列)是否不能为空. 默认值:false
length(可选):配置数据库中列的长度. 默认值:使用数据库类型的最大长度
-->
<property name="person_sex" column="person_sex" >
<!-- <column name="person_sex" sql-type="varchar" ></column> -->
</property>
<property name="person_age" column="person_age" ></property>
<property name="person_tel" column="person_tel" ></property>
</class>
</hibernate-mapping>
hibernate核心API
configuration 配置加载类.用于加载主配置,orm元数据加载
SessionFactory功能: 用于创建操作数据库核心对象session对象的工厂. 简单说功能就一个—创建session对象
sessionfactory 负责保存和使用所有配置信息.消耗内存资源非常大,所以在web项目中只创建一个
//1 读取指定主配置文件 => 空参加载方法,加载src下的hibernate.cfg.xml文件
Configuration conf = new Configuration().configure();
//2 根据配置信息,创建 SessionFactory对象
SessionFactory sf = conf.buildSessionFactory();
//3 获得session
//打开一个新的session对象
sf.openSession();
//4 session获得操作事务的Transaction对象
//开启事务并获得操作事务的tx对象
Transaction tx2 = session.beginTransaction();
也可以用sf.getCurrentSession() 获得一个与当前线程绑定的session
tx.commit();//提交事务
tx.rollback();//回滚事务
session.close();//释放资源
sf.close();//释放资源
如果用的是getCurrentSession 那么提交事务之后,session会自动关闭,不需要再手动关闭了.
获取与当前线程绑定的session需要在配置文件里面配置一下
<!-- 指定session与当前线程绑定 -->
<property name="hibernate.current_session_context_class">thread</property>
简单的增删该查
1.增
Person person = new Person();
person.setPerson_name("aa");
session.save(person);
2.删(删除的话首先要获得删除的对象)
Person person = session.get(Person.class,3);
session.delete(person);
3.改(也需要首先获取要修改的对象)
Person person = session.get(Person.class,3);
person.setPerson_name("bb");
session.update(person);
4.查
Person person = session.get(Person.class,3);
3.创建hibernate实体类的注意事项
1.持久化类提供无参数构造,(因为hibernate是用反射来创建对象的,反射默认的是使用类中的空参构造)
2.成员变量私有,提供共有get/set方法访问.需提供属性
3.持久化类中的属性,应尽量使用包装类型(因为使用包装类型可以接受数据库中的null)
4.持久化类需要提供oid.与数据库中的主键列对应(没有主键的话无法映射到hibernate)
5.不要用final修饰class
4.hibernate一级缓存cache
hibernate对象有三种状态(瞬时,持久,托管)
实际上save,update,delete是对三种状态的转换
持久化状态的对象会自动同步到数据库中
操作的核心是将对象转为持久化状态
hibernate缓存和快照
java中是通过对象的地址来比较对象是否相同,而hibernate是通过oid来比较对象是否相同
程序调用get获取对象,hibernate会去缓存中通过oid查找对象,如果没有则会发送sql语句,然后将查询结果分装成两个resultSet
一个放在缓存中,一个放在快照中,然后将缓存中的对象返回给程序,提交事务时去和快照中的对比,如果有变化,
则会更新到数据库中.
1:提高查询效率
2:减少不必要的修改语句发送
提高效率手段
hibernate中的事务
事务的特性
acid(原子性,一致性,隔离性,持久性)
事务并发存在的问题
1.脏读
2.幻读
3.不可重复读
事务隔离级别
1.读未提交(存在以上三个问题)
2.读已提交(可解决脏读问题)
4.可重复度(可解决脏读和不可重复读问题)
8.串行化(效率低,不推荐)
配置事务隔离级别
<property name="hibernate.connection.isolation">4</property>
HQL语句(适合用于简单多表查询)
hibernate query language
1.书写hql
String hql = " from Person"; // 查询所有Person对象
2.//2> 根据HQL语句创建查询对象
Query query = session.createQuery(hql);
//3> 根据查询对象获得查询结果
List<Customer> list = query.list(); // 返回list结果
//如果接收唯一的查询结果用query.uniqueResult()
条件查询
//1> 书写HQL语句
String hql = " from Person where person_name = 1 ";
//2> 根据HQL语句创建查询对象
Query query = session.createQuery(hql);
//3> 根据查询对象获得查询结果
Person p = (Person) query.uniqueResult();
占位符
//1> 书写HQL语句
String hql = " from Person where person_name = ? ";
//2> 根据HQL语句创建查询对象
Query query = session.createQuery(hql);
//设置参数
//query.setLong(0, 1l);
query.setParameter(0, 1l);
//3> 根据查询对象获得查询结果
Person p = (Person) query.uniqueResult();
命名占位符
//1> 书写HQL语句
String hql = " from Person where person_name = :person_name";
//2> 根据HQL语句创建查询对象
Query query = session.createQuery(hql);
//设置参数
query.setParameter("person_name", 1);
//3> 根据查询对象获得查询结果
Person p = (Person) query.uniqueResult();
分页查询
//1> 书写HQL语句
String hql = " from Person "; // 查询所有Customer对象
//2> 根据HQL语句创建查询对象
Query query = session.createQuery(hql);
//设置分页信息 limit ?,?
query.setFirstResult(1);
query.setMaxResults(1);
//3> 根据查询对象获得查询结果
List<Person> list = query.list();
排序
String hql = " from test.demo.Person order by person_id desc ";//完整写法
Query query = session.createQuery(hql);
List list = query.list();
聚合函数
String hql1 = " select count(*) from test.demo.Person ";//完整写法
String hql2 = " select sum(person_age) from test.demo.Person ";//完整写法
String hql3 = " select avg(person_age) from test.demo.Person ";//完整写法
String hql4 = " select max(person_id) from test.demo.Person ";//完整写法
String hql5 = " select min(person_age) from test.demo.Person ";//完整写法
Query query = session.createQuery(hql5);
Number number = (Number) query.uniqueResult();
投影查询
String hql1 = " select person_name from cn.itcast.domain.Customer ";
String hql2 = " select person_name,person_id from test.demo.Person ";
String hql3 = " select new Person(person_name,person_id) from test.demo.Person ";
Query query = session.createQuery(hql3);
List list = query.list();
多表查询
以客户和联系人关系为例
客户下对应多个联系人
内连接
//HQL 内连接 => 将连接的两端对象分别返回.放到数组中.
String hql = " from Customer c inner join c.linkMens ";
Query query = session.createQuery(hql);
List<Object[]> list = query.list();
//HQL 迫切内连接 => 帮我们进行封装.返回值就是一个对象
String hql = " from Customer c inner join fetch c.linkMens ";
Query query = session.createQuery(hql);
List<Customer> list = query.list();
左连接
//HQL 左外连接 => 将连接的两端对象分别返回.放到数组中.
//一个对象放在一个数组中
String hql = " from Customer c left join c.linkMens ";
Query query = session.createQuery(hql);
List<Object[]> list = query.list();
Criteria(QBC)
hibernate的无语句面向对象查询
基本查询
//查询所有的Person对象
Criteria criteria = session.createCriteria(Person.class);
List<Person> list = criteria.list();
System.out.println(list);
条件查询
//创建criteria查询对象
Criteria criteria = session.createCriteria(Person.class);
//添加查询参数 => 查询person_id为1的Person对象
criteria.add(Restrictions.eq("person_id", 1));
//执行查询获得结果
Person p = (Person) criteria.uniqueResult();
分页查询
//创建criteria查询对象
Criteria criteria = session.createCriteria(Person.class);
//设置分页信息 limit ?,?
criteria.setFirstResult(1);
criteria.setMaxResults(2);
//执行查询
List<Person> list = criteria.list();
查询总条数
//创建criteria查询对象
Criteria criteria = session.createCriteria(Person.class);
//设置查询的聚合函数 => 总行数
criteria.setProjection(Projections.rowCount());
//执行查询
Long count = (Long) criteria.uniqueResult();
排序
Criteria c = session.createCriteria(Person.class);
c.addOrder(Order.asc("person_id"));
//c.addOrder(Order.desc("person_id"));
List<Person> list = c.list();
Criteria离线查询,可以在web层创建不依赖session的Criteria,绑定条件,然后传到dao层再关联session
//Service/web层
DetachedCriteria dc = DetachedCriteria.forClass(Person.class);
dc.add(Restrictions.idEq(6));//拼装条件(全部与普通Criteria一致)
//dao
Configuration conf = new Configuration().configure();
SessionFactory sessionFactory = conf.buildSessionFactory();
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
//关联session
Criteria c = dc.getExecutableCriteria(session);
List list = c.list();
tx.commit();
session.close();
SQL查询
基本查询
返回的是一个object数组集合,要是想将结果放到对象中,需要指定将结果集封装到哪个对象中
query.addEntity(Customer.class);
//1 书写sql语句
String sql = "select * from cst_customer";
//2 创建sql查询对象
SQLQuery query = session.createSQLQuery(sql);
//3 调用方法查询结果
List<Object[]> list = query.list();
//query.uniqueResult();
条件查询
//1 书写sql语句
String sql = "select * from cst_customer where cust_id = ? ";
//2 创建sql查询对象
SQLQuery query = session.createSQLQuery(sql);
query.setParameter(0, 1l);
//指定将结果集封装到哪个对象中
query.addEntity(Customer.class);
//3 调用方法查询结果
List<Customer> list = query.list();
分页查询
//1 书写sql语句
String sql = "select * from cst_customer limit ?,? ";
//2 创建sql查询对象
SQLQuery query = session.createSQLQuery(sql);
query.setParameter(0, 0);
query.setParameter(1, 1);
//指定将结果集封装到哪个对象中
query.addEntity(Customer.class);
//3 调用方法查询结果
List<Customer> list = query.list();
查询优化
延迟加载
使用session.load()方法,可以应用类级别的加载策略
在orm元数据中配置
lazy属性默认是true,当设置为false时,load和get没有区别
<class name="Person" table="test_person" lazy="true">
关联级别查询
lazy设置为true,fetch使用select
延迟加载存在no-session问题
解决办法:扩大session作用范围
可使用过滤器filter
前处理打开session,开启事务
放行
后处理关闭session,提交事务
hibernate关系表达(一对多|多对一)
在表中,多的一方使用外键引用一的一方的主键
在对象中,一的一方使用集合表达持有多个多的一方,多的一方使用对象引用一的一方,表达多的一方属于哪个一的一方
ORM元数据中表达
一的一方
Custonmer.hbm.xml
<!-- 集合,一对多关系,在配置文件中配置 -->
<!--
name属性:集合属性名
column属性: 外键列名
class属性: 与我关联的对象完整类名
-->
<!--
级联操作: cascade
save-update: 级联保存更新
delete:级联删除
all:save-update+delete
级联操作: 简化操作.目的就是为了少些两行代码.
-->
<!-- inverse属性: 配置关系是否维护.
true: customer不维护关系
false(默认值): customer维护关系
inverse属性: 性能优化.提高关系维护的性能.
原则: 无论怎么放弃,总有一方必须要维护关系.
一对多关系中: 一的一方放弃.也只能一的一方放弃.多的一方不能放弃.
-->
<set name="linkMens" inverse="true" cascade="save-update" >
<key column="lkm_cust_id" ></key>
<one-to-many class="LinkMan" />
</set>
一的一方
<!-- 多对一 -->
<!--
name属性:引用属性名
column属性: 外键列名
class属性: 与我关联的对象完整类名
-->
<!--
级联操作: cascade
save-update: 级联保存更新
delete:级联删除
all:save-update+delete
级联操作: 简化操作.目的就是为了少些两行代码.
-->
<!-- 多的一方: 不能放弃维护关系的.外键字段就在多的一方. -->
<many-to-one name="customer" column="lkm_cust_id" class="Customer" >
</many-to-one>
多对多关系
在表中使用一张中间表,引用两方主键作为两个外键.
在对象中,两方都用集合来表达拥有多个对方
<!-- 多对多关系表达 -->
<!--
name: 集合属性名
table: 配置中间表名
key
|-column:外键,别人引用"我"的外键列名
class: 我与哪个类是多对多关系
column:外键.我引用比人的外键列名
-->
<!-- cascade级联操作:
save-update: 级联保存更新
delete:级联删除
all:级联保存更新+级联删除
结论: cascade简化代码书写.该属性使不使用无所谓. 建议要用只用save-update.
如果使用delete操作太过危险.尤其在多对多中.不建议使用.
-->
<set name="roles" table="sys_user_role" cascade="save-update" >
<key column="user_id" ></key>
<many-to-many class="Role" column="role_id" ></many-to-many>
</set>
多对多关系中,一定要有一方放弃维护关系,否则会因为主键冲突报错