Hibernate
Hibernate概述
Hibernate对JDBC进行了轻量级的封装,程序员可以通过面向对象的变成思想来操作数据库。Hibernate将Java中对象与对象之间的关系映射成数据库中表与表之间的关系。
Hibernate 版本区别
- buildSessionFactory()方法改变
在 hibernate4中buildSessionFactory()已经被buildSessionFactory(ServiceRegistry serviceRegistry)代替了。
Configuration configuration = new Configuration();
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
.applySettings(configuration.getProperties())
.buildServiceRegistry();
SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
Hibernate 应用
Hibernate 配置文件
- hibernate核心配置文件
hibernate核心配置文件放在类文件夹中,有两种格式:(1)hibernate.properties:properties文件(采用“键=值”形式)
(2)hibernate.cfg.xml:XML配置文件(官方推荐)
hibernate.cfg.xml详细
<?xml version="1.0" encoding="GBK"?>
<!-- 指定Hibernate配置文件的DTD信息 -->
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<!-- hibernate- configuration是连接配置文件的根元素 -->
<hibernate-configuration>
<session-factory>
<!-- 指定连接数据库所用的驱动 -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- 指定连接数据库的url,hibernate连接的数据库名 -->
<property name="connection.url">jdbc:mysql://localhost/数据库名</property>
<!-- 指定连接数据库的用户名 -->
<property name="connection.username">root</property>
<!-- 指定连接数据库的密码 -->
<property name="connection.password">123456</property>
<!-- 指定连接池里最大连接数 -->
<property name="hibernate.c3p0.max_size">20</property>
<!-- 指定连接池里最小连接数 -->
<property name="hibernate.c3p0.min_size">1</property>
<!-- 指定连接池里连接的超时时长 -->
<property name="hibernate.c3p0.timeout">5000</property>
<!-- 指定连接池里最大缓存多少个Statement对象 -->
<property name="hibernate.c3p0.max_statements">100</property>
<property name="hibernate.c3p0.idle_test_period">3000</property>
<property name="hibernate.c3p0.acquire_increment">2</property>
<property name="hibernate.c3p0.validate">true</property>
<!-- 指定数据库方言 -->
<property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>
<!-- 根据需要自动创建数据表 -->
<property name="hbm2ddl.auto">update</property>
<!-- 显示Hibernate持久化操作所生成的SQL -->
<property name="show_sql">true</property>
<!-- 将SQL脚本进行格式化后再输出 -->
<property name="hibernate.format_sql">true</property>
<!-- 罗列所有的映射文件 -->
<mapping resource="映射文件路径/News.hbm.xml"/>
</session-factory>
</hibernate-configuration>
- Java实体类对应的配置文件,该文件需要和实体类放在一个文件夹下
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!--
<hibernate-mapping>一般不去配置,采用默认即可。
schema:指定映射数据库的schema(模式/数据库),如果指定该属性,则表名会自动添加该schema前缀
package:指定包前缀 指定持久化类所在的包名 这样之后calss子元素中就不必使用全限定性的类名
default-cascade="none":默认的级联风格,表与表联动。
default-lazy="true":默认延迟加载
-->
<hibernate-mapping>
<!--
<class>:使用class元素定义一个持久化类。
name="cn.javass.user.vo.UserModel":持久化类的java全限定名;
table="tbl_user":对应数据库表名,默认持久化类名作为表名;
proxy:指定一个接口,在延迟装载时作为代理使用,也可在这里指定该类自己的名字。
mutable="true":默认为true,设置为false时则不可以被应用程序更新或删除,等价于所有<property>元素的update属性为false,表示整个实例不能被更新。
dynamic-insert="false":默认为false,动态修改那些有改变过的字段,而不用修改所有字段;
dynamic-update="false":默认为false,动态插入非空值字段;
select-before-update="false":默认为false,在修改之前先做一次查询,与用户的值进行对比,有变化都会真正更新;
optimistic-lock="version":默认为version(检查version/timestamp字段),取值:all(检查全部字段)、dirty(只检查修改过的字段);
none(不使用乐观锁定),此参数主要用来处理并发,每条值都有固定且唯一的版本,版本为最新时才能执行操作;
如果需要采用继承映射,则class元素下还会增加<subclass.../>元素等用于定义子类。
-->
<class name="cn.javass.user.vo.UserModel" table="tbl_user" >
<!--
<id>:定义了该属性到数据库表主键字段的映射。
type 指定该标识属性的数据类型,该类型可以是Hibernate的内建类型,也可以是java类型,如果是java类型则需要使用全限定类名(带包名)。该属性可选,如果没有指定类型, 则hibernate自行判断该标识属性数据类型。通常建议设定。
name="userId":标识属性的名字;
column="userId":表主键字段的名字,如果不填写与name一样;
-->
<id name="userId">
<!-- <generator>:指定主键由什么生成,推荐使用uuid,assigned指用户手工填入。设定标识符生成器
适应代理主键的有:
increment:有Hibernat自动以递增的方式生成标识符,每次增量1;
identity:由底层数据库生成标识符,前提条件是底层数据库支持自动增长字段类型。(DB2,MYSQL)
uuid:用128位的UUID算法生成字符串类型标识符。
适应自然主键:
assigned:由java程序负责生成标识符,为了能让java应用程序设置OID,不能把setId()方法设置成private类型。
让应用程序在save()之前为对象分配一个标识符。相当于不指定<generator.../>元素时所采用的默认策略。
应当尽量避免自然主键
-->
<generator class="uuid"/>
</id>
<!--
<version/>:使用版本控制来处理并发,要开启optimistic-lock="version"和dynamic-update="true"。
name="version":持久化类的属性名,column="version":指定持有版本号的字段名;
-->
<version name="version" column="version"/>
<!--
<property>:为类定义一个持久化的javaBean风格的属性。
name="name":标识属性的名字,以小写字母开头;
column="name":表主键字段的名字,如果不填写与name一样;
update="true"/insert="true":默认为true,表示可以被更新或插入;
access="property/field":指定Hibernate访问持久化类属性的方式。默认property。property表示使用setter/getter方式。field表示运用java反射机制直接访问类的属性。
formula="{select。。。。。}":该属性指定一个SLQ表达式,指定该属性的值将根据表达式类计算,计算属性没有和它对应的数据列。
formula属性允许包含表达式:sum,average,max函数求值的结果。
例如:formula="(select avg(p.price) from Product P)"
-->
<property name="name" column="name" />
<property name="sex" column="sex"/>
<property name="age" column="age"/>
<!--
组件映射:把多个属性打包在一起当一个属性使用,用来把类的粒度变小。
<component name="属性,这里指对象">
<property name="name1"></property>
<property name="name2"></property>
</component>
-->
<!--
<join>:一个对象映射多个表,该元素必须放在所有<property>之后。
<join table="tbl_test:子表名">
<key column="uuid:子表主键"></key>
<property name="name1:对象属性" column="name:子表字段"></property>
</join>
-->
</class>
</hibernate-mapping>
Hibernate 核心接口
- Configuration
负责启动并配置hibernate,创建SessionFactory对象。
Configuration configuration = new Configuration().configure();
执行上面代码Hibernate会自动在类路径下收缩并读取hibernate.cfg.xml文件,作为后续操作的基础配置。
- SessionFactory
负责初始化Hibernate创建Session实例,通过Configuration来实例构建SessionFactory。一个应用通过只需要一个SessionFactory。一般使用单例模式来使用。
Configuration configuration = new Configuration().configure();
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
.applySettings(configuration.getProperties())
.buildServiceRegistry();
SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
- Session
提供众多持久化对象的crud操作。注意:Session是线程不安全的,一个Session实例只能由一个线程使用。
Session session = sessionFactory.openSession();
- Transation
进行事务操作的接口
//开启事务
Transaction transaction = session.beginTransaction();
//提交事务
transaction.commit();
//回滚事务
transaction.rollback();
- Query和Criteria
Query和Criteria两个接口相似,都是hibernate提供的查询接口。Query实例封装了HQL(Hibernate Query Language)查询语句,和SQL查询语句相似。Criteria接口完全封装了基于字符串查询语句,比Query接口更加面向对象,Criteria擅长进行动态查询。
查询方式
HQL查询
- 一般查询
//select * from user;
Query query=session.createQuery(“from user”);
List<User> list=query.list();
//select * from user where sex=? and age > ?;
Query query=session.createQuery(“from user where sex=? and age > ?”);
query.setParameter(0,”男”);
query.setParameter(1,”23”);
List<User> list=query.list();
- 分页查询
在MySQL中主要通过limit来分页,但是不同的数据库不一样,所以hibernate不识别limit。
Query query=session.createQuery(“from user”);
query.setFirstResult(0);//起始位置
query.setMaxResults(30);//最大查询数
List<User> list=query.list();
- 投影查询
Query query=session.createQuery(“select name from user”);
这里需要注意:hql不支持 select * from user 中的 *
- 聚集函数
Query query=session.createQuery(“select count(*) from user”);
//返回唯一值
Object obj=query.uniqueResult();
Criteria查询
- 查询所有
Criteria criteria=session.createCriteria(User.class);
List<User> userlist=criteria.list();
- 条件查询
Criteria criteria=session.createCriteria(User.class);
criteria.add(Restrictions.eq("uname","张三"));
List<User> userlist=criteria.list();
方法 | 说明 |
---|---|
Restrictions.eq | = |
Restrictions.allEq | 利用Map来进行多个等于的限制 |
Restrictions. gt | > |
Restrictions. ge | >= |
Restrictions .lt | < |
Restrictions.le | <= |
Restrictions.between | BETWEEN |
Restrictions.like | LIKE |
Restrictions .in | in |
Restrictions.and | and |
Restrictions.or | or |
Restrictions.sqlRestriction | 用SQL限定查询 |
- 分页查询
Criteria criteria=session.createCriteria(“from user”);
criteria.setFirstResult(0);//起始位置
criteria.setMaxResults(30);//最大查询数
List<User> userlist=criteria.list();
- 统计查询
Criteria criteria=session.createCriteria(“from user”);
criteria.setProjection(Projections.rowCount);//起始位置
Object obj=criteria.uniqueResult();
- 离线查询
离线查询其实就是动态查询DetachedCriteria
web开发都会碰到多条件查询,可以使用DetachedCriteria来构造查询条件,然后将这个DetachedCriteria作为方法调用参数传递给业务层对象。而业务层对象获得DetachedCriteria之后,可以在session范围内直接构造Criteria,进行查询。就此,查询语句的构造完全被搬离到web层实现,而业务层则只负责完成持久化和查询的封装即可。换句话说,业务层代码是不变化的。我们不必为了查询条件的变化而去频繁改动查询语句了。
//创建对象
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(User.class);
//执行时传入session
Criteria executableCriteria = detachedCriteria.getExecutableCriteria(session);
List<User> list = executableCriteria.list();
HQL多表查询
这里使用 多对多关系的用户表(user)和角色表(role)举例。
对应的实体类
User.java
public class User {
private int uid;
private String uname;
private int uage;
private String usex;
private Set<Role> roleSet=new HashSet<>();
public Set<Role> getRoleSet() {
return roleSet;
}
public void setRoleSet(Set<Role> roleSet) {
this.roleSet = roleSet;
}
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public int getUage() {
return uage;
}
public void setUage(int uage) {
this.uage = uage;
}
public String getUsex() {
return usex;
}
public void setUsex(String usex) {
this.usex = usex;
}
}
Role.java
public class Role {
private int rid;
private String rname;
private int authority;
private Set<User> userSet=new HashSet<User>();
public int getRid() {
return rid;
}
public void setRid(int rid) {
this.rid = rid;
}
public String getRname() {
return rname;
}
public void setRname(String rname) {
this.rname = rname;
}
public int getAuthority() {
return authority;
}
public void setAuthority(int authority) {
this.authority = authority;
}
public Set<User> getUserSet() {
return userSet;
}
public void setUserSet(Set<User> userSet) {
this.userSet = userSet;
}
}
- 内连接
Query query=session.createQuery(“from User u inner join u.role”);
List<User> list=query.list();
返回的结果是数组
- 迫切内连接
Query query=session.createQuery(“from User u inner join fetch u.role”);
List<User> list=query.list();
返回的结果是对象
左外连接
Query query=session.createQuery(“from User u left outer join u.role”);
List<User> list=query.list();
返回结果是数组
- 迫切左外连接
Query query=session.createQuery(“from User u left outer join fetch u.role”);
List<User> list=query.list();
返回结果是对象
- 右外连接
Query query=session.createQuery(“from User u right outer join u.role”);
List<User> list=query.list();
返回的结果是对象
hibernate 查询策略
立即查询
User user=session.get(“uid”,2);
执行后立即查询
延迟查询
类级别延迟
User user=session.load(“uid”,2);
//不发送数据库查询语句
System.out.printl(user.getUid);
//发送数据库查询语句
System.out.printl(user.getUname);
需要uid以外的数据时,才会发出数据库查询语句
关联级别延迟
//发送语句
User user=session.get(“uid”,2);
System.out.printl(user.getUid);
//没有发送语句
Role role=user.getRole();
//发送语句
System.out.printl(role.getRname);
延迟可以在实体类对应的配置文件中配置
批量抓取
在实体类配置文件中的set属性中配置
batch-size=”10”;值越大,发送查询语句越少
hibernate开发实例
环境搭建
-
jar包
本文使用hibernate4.2.21,导入其中的required文件夹中的jar包 -
核心配置文件
<?xml version="1.0" encoding="GBK"?>
<!-- 指定Hibernate配置文件的DTD信息 -->
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<!-- hibernate- configuration是连接配置文件的根元素 -->
<hibernate-configuration>
<session-factory>
<!-- 指定连接数据库所用的驱动 -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- 指定连接数据库的url,hibernate连接的数据库名 -->
<property name="connection.url">jdbc:mysql://localhost/sql_test</property>
<!-- 指定连接数据库的用户名 -->
<property name="connection.username">root</property>
<!-- 指定连接数据库的密码 -->
<property name="connection.password">123456</property>
<!-- 指定数据库方言 -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 根据需要自动创建数据表 -->
<property name="hbm2ddl.auto">update</property>
<!-- 显示Hibernate持久化操作所生成的SQL -->
<property name="show_sql">true</property>
<!-- 将SQL脚本进行格式化后再输出 -->
<property name="hibernate.format_sql">true</property>
<!-- 罗列所有的映射文件 -->
<mapping resource="cn/edu/sicau/domain/User.hbm.xml"/>
<mapping resource="cn/edu/sicau/domain/Role.hbm.xml"/>
<mapping resource="cn/edu/sicau/domain/Teacher.hbm.xml"/>
<mapping resource="cn/edu/sicau/domain/School.hbm.xml"/>
</session-factory>
</hibernate-configuration>
- 一对多关系配置
这里以学校(school)和老师(teacher)多为一对多关系案例配置。
School.java
public class School {
private int sid;
private String sname;
private String address;
private Set<Teacher> teacherSet=new HashSet<>();
public int getSid() {
return sid;
}
public void setSid(int sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Set<Teacher> getTeacherSet() {
return teacherSet;
}
public void setTeacherSet(Set<Teacher> teacherHashSet) {
this.teacherSet = teacherHashSet;
}
}
Teacher.java
public class Teacher {
private int tid;
private String tname;
private int tage;
private School school;
public int getTid() {
return tid;
}
public void setTid(int tid) {
this.tid = tid;
}
public String getTname() {
return tname;
}
public void setTname(String tname) {
this.tname = tname;
}
public int getTage() {
return tage;
}
public void setTage(int tage) {
this.tage = tage;
}
public School getSchool() {
return school;
}
public void setSchool(School school) {
this.school = school;
}
}
School.hbm.xml
在学校对应的配置文件中,学校是一对多中的一方,使用< set />来配置,具体内容查看配置文件内容。
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.edu.sicau.domain.School" table="school">
<!--学校id-->
<id name="sid">
<generator class="native"></generator>
</id>
<property name="sname"/>
<property name="address"/>
<!--学校与老师是一对多
set中的name为school中teacher集合对象名称
key column 对应的是school与teacher共同维护的外键的名称
-->
<set name="teacherSet" cascade="save-update">
<key column="tsid"></key>
<one-to-many class="cn.edu.sicau.domain.Teacher"/>
</set>
</class>
</hibernate-mapping>
Teacher.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.edu.sicau.domain.Teacher" table="teacher">
<id name="tid">
<generator class="native"></generator>
</id>
<property name="tname"/>
<property name="tage"/>
<!--
name 对应School对象的名称
column="tsid" 为学校与老师两张表共同维护的外键名称
-->
<many-to-one name="school" class="cn.edu.sicau.domain.School" column="tsid"/>
</class>
</hibernate-mapping>
测试类 test.java
public class test {
/**
* shcool-teacher 一对多
*/
@Test
public void oneToManyAdd(){
//SessionFactory sessionFactory=new Configuration().configure().buildSessionFactory();
Configuration configuration = new Configuration().configure();
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
.applySettings(configuration.getProperties())
.buildServiceRegistry();
SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
try {
School school=new School();
school.setSname("计算机学校");
school.setAddress("中国");
Teacher teacher1=new Teacher();
teacher1.setTname("张三");
teacher1.setTage(23);
Teacher teacher2=new Teacher();
teacher2.setTname("李四");
teacher2.setTage(25);
school.getTeacherSet().add(teacher1);
school.getTeacherSet().add(teacher2);
//添加
session.save(school);
//提交事务
transaction.commit();
}catch (Exception e){
//回滚事务
transaction.rollback();
e.printStackTrace();
}finally {
//关闭
session.close();
sessionFactory.close();
}
}
}
- 多对多关系配置
这里使用用户表(User)与角色表(Role)来举例,实体类已在HQL多表查询中给出。
User.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.edu.sicau.domain.User" table="user">
<id name="uid">
<generator class="native"></generator>
</id>
<property name="uname"/>
<property name="uage"/>
<property name="usex"/>
<!--
name="roleSet" 对应在User类中,角色集合对象的名称
table="user_role" User和Role表共同维护的第三张表的名称
key column="userid" User表在第三张表中对应的外键的名称
column="roleid" Role表在第三张表中对应的外键的名称
-->
<set name="roleSet" table="user_role">
<key column="userid"></key>
<many-to-many class="cn.edu.sicau.domain.Role" column="roleid"/>
</set>
</class>
</hibernate-mapping>
Role.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.edu.sicau.domain.Role" table="role">
<id name="rid">
<generator class="native"></generator>
</id>
<property name="rname"/>
<property name="authority"/>
<!--
name="userSet" 对应在User类中,角色集合对象的名称
table="user_role" User和Role表共同维护的第三张表的名称
key column="roleid" Role表在第三张表中对应的外键的名称
column="userid" User表在第三张表中对应的外键的名称
-->
<set name="userSet" table="user_role" cascade="save-update">
<key column="roleid"></key>
<many-to-many class="cn.edu.sicau.domain.User" column="userid"/>
</set>
</class>
</hibernate-mapping>
测试类test.java
public class test {
/**
* user-role 多对多
*/
@Test
public void ManyToOneAdd(){
//SessionFactory sessionFactory=new Configuration().configure().buildSessionFactory();
Configuration configuration = new Configuration().configure();
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
.applySettings(configuration.getProperties())
.buildServiceRegistry();
SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
try {
User user1=new User();
user1.setUname("张三");
user1.setUsex("男");
user1.setUage(23);
User user2=new User();
user2.setUname("李四");
user2.setUsex("女");
user2.setUage(20);
User user3=new User();
user3.setUname("王五");
user3.setUsex("男");
user3.setUage(25);
Role role1=new Role();
role1.setRname("管理员");
role1.setAuthority(0);
Role role2=new Role();
role2.setRname("用户");
role2.setAuthority(1);
Role role3=new Role();
role3.setRname("游客");
role3.setAuthority(2);
role1.getUserSet().add(user1);
role1.getUserSet().add(user2);
role2.getUserSet().add(user2);
role2.getUserSet().add(user3);
role3.getUserSet().add(user1);
role3.getUserSet().add(user3);
session.save(role1);
session.save(role2);
session.save(role3);
transaction.commit();
}catch (Exception e){
transaction.rollback();
e.printStackTrace();
}finally {
session.close();
sessionFactory.close();
}
}
}