索引
背景
公司的任务,二开,架构:Spring MVC Hibernate,前端用DHTMLX,MyEclipse开发,没用Maven构建,脑壳疼,因为,我对Hibernate不熟,而且想吐,因为DHTMLX开发貌似只有官方API靠谱,网上基本没资料。不过还是得做,所以特写此文,快速搞定这个Hibernate吧。看能不能在一两天之内搞明白。因为后续我要跟着二开,所以遵从公司环境:MyEclipse、SqlServer、手动jar包,如果连接MySQL啥的,导入相应驱动包,我这个SqlServer驱动包是sqljdbc4.jar
PS:本人会MyBatis,所以不会把这个Hibernate写的很多很深,能正常开发就ok
事实证明只用一天左右时间就差不多了
Hibernate介绍
全自动ORM框架,刚开始知道这个就够了,好了,下班。
环境搭建
1.准备jar,并导入
2.写实体类
Hibernate可以根据表生成数据库表,所以建好库之后就不用建表去了
3.配置实体类和数据库表的映射关系
在实体类的包里创建xml配置文件,引入约束,并配置
4.创建Hibernate核心配置文件
核心配置文件的名字和位置是固定的,位置在src下,名称是:hibernate.cfg.xml,引入约束,详细配置
主要有三个部分:a>配置数据库信息、b>配置hibernate信息、c>配置映射文件信息
第一个程序
先整理一下我们都做了什么:引入了所需jar包、创建了实体类及映射关系、创建了配置文件。那么,接下来的步骤也整理下:
1.加载hibernate核心配置文件
2.黄建SessionFactory对象
3.使用SessionFactory创建Session
4.开启事务
5.写具体逻辑crud操作
6.提交事务
7.关闭资源
package cn.xx.test;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import cn.xx.entity.Shop;
/**
*
* @author Administrator
* 测试类
*/
public class Test {
public static void main(String[] args) {
// 1.加载hibernate核心配置文件
//到src下找到名称是hibernate.cfg.xml
//在hibernate里封装对象
Configuration cfg = new Configuration();
cfg.configure();
// 2.创建SessionFactory对象
//读取hibernate核心配置文件,创建SessionFactory
//在过程中,根据映射关系,在配置数据库里把表创建
SessionFactory sessionFactory = cfg.buildSessionFactory();
// 3.使用SessionFactory创建Session
//类似于连接
Session session = sessionFactory.openSession();
// 4.开启事务
Transaction tx = session.beginTransaction();
// 5.写具体逻辑crud操作
//添加
Shop shop = new Shop();
shop.setId(1);
shop.setName("包子");
shop.setEmail("zo10010@163");
session.save(shop);
// 6.提交事务
tx.commit();
// 7.关闭资源
session.close();
sessionFactory.close();
}
}
然后,表也有了,数据也有了。
单例模式解决频繁创建、关闭SessionFactory
ps:这里可忽略,重新写。我后来在核心配置文件里配置了本地session绑定,并且把session也搞到这个单例里了。求生欲:此单例不完善,具体最优单例请移驾百度,核心思想就是为了解决频繁创建等问题。
package cn.xx.util;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
private static final Configuration config;
private static final SessionFactory factory;
static { // 静态初始化块,加载配置信息
config = new Configuration().configure(); // 读取文件
factory = config.buildSessionFactory();
}
public static SessionFactory getSessionFactory(){
return factory;
}
}
继续CRUD:根据id查询
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
//根据id查询使用session.get(),第一个参数是实体类.class,第二个参数是id
Shop shop = session.get(Shop.class, 1);
System.out.println(shop);
tx.commit();
session.close();
继续CRUD:修改
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
//修改id为2的name值,先查,再改
Shop shop = session.get(Shop.class, 2);
shop.setName("烧烤");
session.update(shop);
tx.commit();
session.close();
继续CRUD:删除
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
//删除
Shop shop = session.get(Shop.class, 2);
session.delete(shop);
tx.commit();
session.close();
继续CRUD:saveOrupdate
//不设id,就是insert,对象与session没有关联,这是瞬时态
Shop shop = new Shop();
shop.setName("包子");
shop.setEmail("zo10010");
session.saveOrUpdate(shop);
Hibernate:
insert
into
U_Shop
(name, email)
values
(?, ?)
//设了id就是修改,对象与session没有关联,这是托管态
Shop shop = new Shop();
shop.setId(3);
shop.setName("辣条");
shop.setEmail("zo10010");
session.saveOrUpdate(shop);
Hibernate:
update
U_Shop
set
name=?,
email=?
where
id=?
//这也是update,对象与session有关联,这是持久态
Shop shop = session.get(Shop.class, 3);
shop.setName("啤酒");
session.saveOrUpdate(shop);
Hibernate:
update
U_Shop
set
name=?,
email=?
where
id=?
Hibernate的缓存
Hibernate有一级缓存和二级缓存
一级缓存:使用范围在session,就是说从session打开到关闭,默认是打开的,存储数据必须是持久态。比如我去根据id查了一次数据库,在一个session里我又去根据这个id去查,那么第一次查的时候是查的数据库,并且在session里有缓存,第二次查的时候就不去数据库查了,而是在缓存里拿数据。
二级缓存:目前不使用了,redis替代了它,所以不研究了
Hibernate的事务
关于事务的特性,如果不考虑隔离性,会出现脏读、不可重复读、虚读问题,MySQL默认隔离级别是repeatable read,这里不做深究。这里重点搞一下关于事务的规范代码的写法
基本结构:
try {
开启事务
提交事务
} catch () {
回滚事务
} finally {
关闭
}
Session session = null;
Transaction tx = null;
try {
session = HibernateUtil.getSessionFactory().openSession();
tx = session.beginTransaction();
//操作
Shop shop = session.get(Shop.class, 3);
shop.setName("啤酒");
session.saveOrUpdate(shop);
} catch (Exception e) {
e.printStackTrace();
tx.rollback();
} finally {
session.close();
}
Hibernate的三个API
Query对象:
使用Query对象,不需要写SQL,但是要写HQL。
SQL和HQL的区别:SQL是操作数据库的表、字段,HQL是操作实体类和属性
//创建Query对象
//方法里边写hql语句
Query query = session.createQuery("from Shop");
//调用query里的方法得到结果
List<Shop> list = query.list();
for (Shop shop : list) {
System.out.println(shop);
}
Criteria对象:
啥也不用写,直接调用里边的方法,没错,我也很震精,感兴趣完事去看源码
//创建Criteria对象
//方法里边参数是实体类class
Criteria createCriteria = session.createCriteria(Shop.class);
List<Shop> list = createCriteria.list();
for (Shop shop : list) {
System.out.println(shop);
}
SQLQuery对象:
这个是调用底层sql实现,返回的是个数组,数组,数组,这个看看就行了。。一切从简
//创建SQLQuery对象
//参数是普通sql语句
SQLQuery query = session.createSQLQuery("select * from U_Shop");
List<Object[]> list = query.list();
for (Object[] objects : list) {
System.out.println(Arrays.toString(objects));
}
关于表的关系
都知道,表关系有:一对一、一对多、多对多。
一对一:就像你跟你女盆友的关系
一对多:一个公司有好多员工
多对多:一个订单可以有好多商品,一个商品属于多个订单
一对多操作
背景:一个公司有好多员工
1.搞两个实体类:公司类、员工类
package cn.xx.entity;
import java.util.Set;
/**
* 公司类
* @author Administrator
*
*/
public class Firm {
private int id;
private String name;
private String addr;
//hibernate规定一对多集合用set,因为set值不允许重复
private Set<User> userSet;
public Set<User> getUserSet() {
return userSet;
}
public void setUserSet(Set<User> userSet) {
this.userSet = userSet;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
}
package cn.xx.entity;
/**
* 员工类
* @author Administrator
*
*/
public class User {
private int id;
private String name;
private int sex;
//这个员工所属的公司
private Firm firm;
public Firm getFirm() {
return firm;
}
public void setFirm(Firm firm) {
this.firm = firm;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
}
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>
<!-- name里写实体类的全路径,table写表名 -->
<class name="cn.xx.entity.Firm" table="U_Firm">
<!-- 配置实体类id与表id对应
name的值是实体类唯一id的值
column为表的列名-->
<id name="id" column="id">
<!-- 设置数据库id增长策略 native -->
<generator class="native"></generator>
</id>
<!-- 配置其他属性与表的对应 -->
<property name="name" column="name"></property>
<property name="addr" column="addr"></property>
<!-- 在公司映射文件中,表示所有员工
使用set标签表示所有员工
set标签里有name属性,属性值卸载客户实体类里面表示联系人的set集合名称
-->
<set name="userSet">
<!-- 一对多建表
hibernate机制是双向维护外键,双方都要配置外键
-->
<key column="fuid"></key>
<!-- 公司所有的员工,class里写员工实体类全路径 -->
<one-to-many class="cn.xx.entity.User"/>
</set>
</class>
</hibernate-mapping>
<?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>
<!-- name里写实体类的全路径,table写表名 -->
<class name="cn.xx.entity.User" table="U_User">
<!-- 配置实体类id与表id对应
name的值是实体类唯一id的值
column为表的列名-->
<id name="id" column="id">
<!-- 设置数据库id增长策略 native -->
<generator class="native"></generator>
</id>
<!-- 配置其他属性与表的对应 -->
<property name="name" column="name"></property>
<property name="sex" column="sex"></property>
<!-- 表示员工所属公司
name属性,因为在公司实体类使用Firm对象表示,写Firm名称
class属性,Firm全路径
column属性,外键名称
-->
<many-to-one name="Firm" class="cn.xx.entity.Firm" column="fuid"></many-to-one>
</class>
</hibernate-mapping>
3.核心配置,主要是把映射文件配置到核心配置文件中
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 1.配置数据库信息 -->
<property name="hibernate.connection.driver_class">
com.microsoft.sqlserver.jdbc.SQLServerDriver
</property>
<property name="hibernate.connection.url">jdbc:sqlserver://127.0.0.1:1433;databaseName=TEST</property>
<property name="hibernate.connection.username">sa</property>
<property name="hibernate.connection.password">xx2255</property>
<!-- 2.配置hibernate信息 -->
<!-- 输出底层sql语句 -->
<property name="hibernate.show_sql">true</property>
<!-- 输出底层sql语句格式 -->
<property name="hibernate.format_sql">true</property>
<!-- hibernate帮创建表,需要配置之后
uptate:如果有表,更新,如果没有,创建 -->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- 配置数据库方言 -->
<property name="hibernate.dialect">org.hibernate.dialect.SQLServer2012Dialect</property>
<!-- 绑定本地session -->
<property name="hibernate.current_session_context_class">thread</property>
<!-- 3.把映射文件放到核心配置文件中 -->
<mapping resource="cn/xx/entity/User.hbm.xml"/>
<mapping resource="cn/xx/entity/Firm.hbm.xml"/>
</session-factory>
</hibernate-configuration>
4.弄个测试类运行一下子,然后去数据库看一下创建好的表和外键关系,ok
5.测试类搞一下级联保存数据,哦对了,还有个配置别忘了,公司映射配置里加cascade=“save-update”,这个配置是做级联保存的操作。关于一对多级联操作就到这了,不搞级联删除了,一般业务里边也没这个功能,要不一个操作把数据都删了,多恐怖,不亚于删数据库。
Firm f = new Firm();
f.setName("bat");
f.setAddr("地球");
User u = new User();
u.setName("break");
u.setSex(1);
Set<User> userSet = new HashSet<User>();
userSet.add(u);
f.setUserSet(userSet);
//保存
session.save(f);
<set name="userSet" cascade="save-update">
<!-- 一对多建表
hibernate机制是双向维护外键,双方都要配置外键
-->
<key column="fuid"></key>
<!-- 公司所有的员工,class里写员工实体类全路径 -->
<one-to-many class="cn.xx.entity.User"/>
</set>
6.关于员工跳槽,在做这儿的时候报了一个错:HHH000142: Javassist Enhancement failed: cn.xx.entity.Firm,这个错是因为一个关于懒加载机制,解决方案贴在代码下边,然后,由于是双向外键维护,性能又有损耗,所以,一般开发中不会把外键写死,而是做逻辑外键,这里呢就先这样吧
//先查到你想跳的公司
Firm f = session.get(Firm.class, 3);
//再查你自己
User u = session.get(User.class, 2);
//把自己公司(bat)改成你想跳公司
Set<User> userSet = new HashSet<User>();
userSet.add(u);
u.setFirm(f);
<many-to-one lazy="false" name="Firm" class="cn.xx.entity.Firm" column="fuid"></many-to-one>
多对多操作
根据上边一对多的操作,确实不想写死主键,所以不去搞复杂的配置了。这里描述一下,我建了三个实体类搞多对多,一个user类、一个role类、一个userRole类,把粒度搞到最小。user里只有id和name,role里只有id和role,userRole里有id、userid、roleid,就建三个普通的表即可,具体要查询的话,肯定是靠逻辑外键做联合查询,这里不贴代码了,赶紧把查询搞起来
Hibernate查询方式
Hibernate提供5种查询方式
1.对象导航查询
2.OID查询
3.hql查询
4.QBC查询
5.本地sql查询
搞到这儿,我小拇指头一哆嗦,我的发可儿?这是人干的事?一个查询都能整出来这么多花样?再不尽快搞定这Hibernate,我就要准备提桶跑路跳槽了。默默点了一根寂寞,隔着窗纱面对着黑暗,我知道,黑暗也在凝视着我,不过据我多年的算命经验,肯定有最优雅的一两种写法。
警告:要开始输出了,非战斗人员请撤离,别的都是扯淡,只有查询才是核心
1.对象导航查询:
像上边那个一对多,根据id查到了公司,又通过公司查到所有员工,这个过程就是对象导航查询
2.OID查询:
根据id查询某一条记录,返回对象,这叫OID查询
3.hql查询:
Query对象,写hql语句实现查询
4.QBC查询:
Criteria对象
5.本地sql查询:
SQLQuery对象,使用普通sql实现查询
嗯?貌似咱上边都用过。一个个弄一弄再。
Hibernate的对象导航查询
回顾一下上边一对多关系的那个业务,外键写死的那个。对象导航查询是基于配置了物理外键查询的,至于能不能用逻辑外键,我暂时还不清楚。那么,看下物理外键-对象导航查询:
Firm f = session.get(Firm.class,1);
//对象导航
f.getUserSet()
意思就是说,因为有物理外键,我先查id为1的公司,得到了这个公司的所有信息,因为公司信息里边配置了UserSet集合,所以我们可以直接拿到这个集合,就得到了这个公司所有的员工信息。over。现在我的疑问越来越强烈,逻辑外键能不能实现对象导航?YY一下,首先我们不直接操作语句,Hibernate完全替我们做了,如果想不配置物理外键而是用逻辑外键,我们就得告诉Hibernate我们的逻辑外键的存在,这个该怎么操作呢?带着这个疑问继续往下搞,这里知道了对象导航查询的原理就可以了
Hibernate的OID查询
不废话,直接贴代码,我感觉我被侮辱了,表示已当场去世。
Firm f = session.get(Firm.class,1);
没错,这就是OID查询,作为一名老祖安,我已无力吐槽
Hibernate的HQL查询
Hibernate Query Language,与SQL相似,上边貌似说过,SQL是操作数据库的表和字段,HQL是操作实体类和属性
常用hql语句
HQL查询所有
直接上代码,省略创建session,try等,跟上边一样,只贴try里的代码
结构:from 实体类名
//创建Query对象
Query query = session.createQuery("from Role");
//返回一个list
List<Role> list = query.list();
//遍历list
for (Role role : list) {
System.out.println(role);
}
输出:
Hibernate:
select
role0_.id as id1_0_,
role0_.role as role2_0_
from
U_Role role0_
1----老板
2----司机
HQL条件查询
结构:from 实体类名 where 属性名 =? and 属性名 =?
from 实体类名 like ?
Query query = session.createQuery("from User where sex = ? and name like ?");
//设置条件值,
//向?里设值,第一个参数是?的位置,第二个参数是值,位置是从0开始的
query.setParameter(0, 1);
query.setParameter(1, "b%");
//得到结果
List<User> list = query.list();
for (User user : list) {
System.out.println(user);
}
输出:
Hibernate:
select
user0_.id as id1_1_,
user0_.name as name2_1_,
user0_.sex as sex3_1_
from
U_User user0_
where
user0_.sex=?
and (
user0_.name like ?
)
2----break----1
HQL排序
结构:from 实体类名 order by 属性名 asc/desc
Query query = session.createQuery("from Role order by id desc");
List<Role> list = query.list();
for (Role role : list) {
System.out.println(role);
}
输出:
Hibernate:
select
role0_.id as id1_0_,
role0_.role as role2_0_
from
U_Role role0_
order by
role0_.id desc
2----司机
1----老板
HQL分页
Query query = session.createQuery("from Role");
//设置从哪开始
query.setFirstResult(0);
//设置每页几条数据
query.setMaxResults(2);
List<Role> list = query.list();
for (Role role : list) {
System.out.println(role);
}
HQL投影查询
其实就是查询部分字段
结构:select 属性1,属性2 from 实体类名
注意:select 后不能写*号
HQL聚集函数
count、sum、svg、max、min
count结构:select count(某属性) from 实体类名
Query query = session.createQuery("select count(id) from Role");
Object uniqueResult = query.uniqueResult();
System.out.println(uniqueResult);
HQL多表查询
首先,在sql里多表连查有内连、左外连、右外连,这里跟上边表、实体类没关系,字段全靠YY
内连:select * from user u,role r where u.roleid = r.id
select * from user u inner join role r on u.roleid = r.id
左外连:select * from user u left join role r on u.roleid = r.id
右外连:select * from user u right join role r on u.roleid = r.id
那么在HQL里呢?
HQL里有:内连、左外连、右外连、迫切内连、迫切左外连
这部分我见都是基于物理外键,我发过誓不做物理外键,所以这部分内容如果公司需要,再去移驾百度去搞一搞。
不过我做了我自己的逻辑外键
Query query = session.createQuery("select u.name from User u , Role r where u.roleid = r.id and u.id = 2 ");
效果:
Hibernate:
select
user0_.name as col_0_0_
from
U_User user0_ cross
join
U_Role role1_
where
user0_.roleid=role1_.id
and user0_.id=2
Hibernate的QBC查询
不写语句进行各种查询,用屁股想都知道不写语句就肯定必须遵循人家封装好的方法,给方法里设置各种值实现Hibernate读懂你要干啥,我吐了,刚学了写hql,又叫我不写hql????下边贴一个QBC查询所有,剩下的不搞了,hql已经是我最大的容忍了
QBC查所有
Criteria criteria = session.createCriteria(Role.class);
List list = criteria.list();
for (Object object : list) {
System.out.println(object);
}
Hibernate:
select
this_.id as id1_0_0_,
this_.role as role2_0_0_
from
U_Role this_
1----老板
2----司机
3----经理
4----宦官
5----黄上
6----站长
7----程序狗
所以总的来说,还是hql语句顺手点,毕竟跟sql类似,暂时就搞到这里,耗了两天时间,至于性能优化之类的机制、配置,也不搞了,都Hibernate了,还性能。。完后在这方面有啥经验,再做补充。