Hibernate 是一个优秀的持久层的ORM(对象关系映射)框架,对JDBC进行了轻量级的封装,由于它可以自动生成sql语句自动执行,因此可以让我们使用对象编程的思想来操作数据库,简化了数据操作的繁杂性。
R: 关系数据库领域的Relational(数据库中表的结构)
M: 映射Mapping(XML的配置文件)
使用Hibernate操作数据库要配置Bean类的映射文件和Hibernate的核心配置文件
先来看一个Hibernate的核心配置文件hibernate.cfg.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>
<!-- hibernate的必配选项 -->
<!-- 配置数据库驱动 -->
<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">root</property>
<!-- 配置数据密码 -->
<property name="hibernate.connection.password">123456</property>
<!-- 配置数据库方言 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- hibernate的必配选项结束 -->
<!-- hibernate的可选配置项 -->
<!-- 配置运行过程中的sql语句展示 -->
<property name="hibernate.show_sql">true</property>
<!-- 美化展示的sql语句 -->
<property name="hibernate.format_sql">true</property>
<!-- 使用第三方连接池 -->
<property name="hibernate.connection.provider_class">org.hibernate.c3p0.internal.C3P0ConnectionProvider</property>
<!-- 设置第三方连接池的属性 -->
<property name="hibernate.c3p0.max_size">20</property>
<property name="hibernate.c3p0.min_size">5</property>
<property name="hibernate.c3p0.timeout">5000</property>
<!-- 配置DDL数据库规则 -->
<!--
none:不用Hibernate自动生成表(默认)
create-drop:每次都会创建一个新的表,执行程序结束后删除这个表.
create:每次都会创建一个新的表.
update:有(表, 列)就使用, 没有(表, 列)就创建
validate:只会使用原有的表.对映射关系进行校验.
-->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- 配置与本地线程绑定的Session对象(操作事务保证session的同一性) -->
<property name="hibernate.current_session_context_class">thread</property>
<!-- 配置映射文件 -->
<mapping resource="com/yanghao/bean/Actor.hbm.xml"/>
<mapping resource="com/yanghao/bean/Category.hbm.xml"/>
<mapping resource="com/yanghao/bean/Product.hbm.xml"/>
<mapping resource="com/yanghao/bean/Student.hbm.xml"/>
<mapping resource="com/yanghao/bean/Course.hbm.xml"/>
<!-- hibernate的可选配置项结束-->
</session-factory>
</hibernate-configuration>
一个简单的bean(Actor.java)的映射文件Actor.hbm.xml:
类中的属性:
private Integer aid;
private String name;
private String age;
private String birthday;
private String area;
Actor.hbm.xml
<?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>
<class name="com.yanghao.bean.Actor" table="actor" >
<!-- 配置主键 -->
<!-- 主键生成策略 -->
<!--
increment:检索自增,会先查询表中的最大的下标,然后下标加一作为下一个新插入的数据的主键,在多线程和集群中有很大的问题,不推荐使用
assigned(指定的、赋值的):自定义主键
identity:自动增长,有索引的增长,Mysql的自动增长策略
sequence:自动增长,有索引的增长,Oracle的自动增长策略
native:本地策略,根据不同数据库能进行不同的自动增长策略的选择,如果是mysql,就用identity,如果是Oracle就用sequence
uuid:随机的字符串策略,用于生成唯一的字符串主键的策略
-->
<id name="aid" column="aid">
<generator class="native"></generator>
</id>
<!-- 配置对应表中的属性 -->
<property name="name" column="name"/>
<property name="age" column="age"/>
<property name="birthday" column="birthday"/>
<property name="area" column="area"/>
</class>
</hibernate-mapping>
将一个Actor的对象映射到数据库中,获取一个Session对象,使用其save方法保存到数据库
@Test
public void test() {
//使用hibernate操作数据库的步骤
//读取配置文件,载入配置默认的配置文件
Configuration configuration = new Configuration();
configuration.configure();
//创建SessionFactory获取Session对象
SessionFactory sessionFactory = configuration.buildSessionFactory();
Session session = sessionFactory.openSession();
//开启事务
Transaction transaction = session.beginTransaction();
//执行数据库操作
Actor actor = new Actor();
actor.setName("黑妞");
actor.setAge("2");
actor.setBirthday("20000102");
actor.setArea("繁荣山丘");
session.save(actor);
//提交事务
transaction.commit();
//关闭资源
session.close();
sessionFactory.close();
}
为了使操作方便,把获取session对象的操作封装到工具类中
public class HibernateUtil {
private static SessionFactory sessionFactory;
private static Configuration configuration;
static {
configuration = new Configuration();
configuration.configure();
sessionFactory = configuration.buildSessionFactory();
}
//私有构造方法,不能通过构造方法创建对象
private HibernateUtil(){
}
//获取新的Session对象
public static Session getSession(){
Session session = sessionFactory.openSession();
return session;
}
//获取当前线程中唯一的Session对象
public static Session getCurrentSession(){
return sessionFactory.getCurrentSession();
}
}
持久化对象(持久化对象创建出来的)在Hibernate的管理中有三种状态:(OID就是对象的ID)
1、瞬时态---没有标识OID,没有纳入session的管理
2、持久态--有标识的OID,纳入了session的管理-------------持久态的对象内容发生变化会把数据库中对应的记录修改
3、托管态--有标识的OID,没有纳入session的管理
三种状态也可以互相转换:
@Test
public void test3(){
Session session = HibernateUtil.getSession();
Transaction transaction = session.beginTransaction();
Actor actor = new Actor();
actor.setName("皮蛋");
actor.setAge("3");
actor.setBirthday("20000101");
actor.setArea("繁荣山丘");
session.save(actor);
transaction.commit();
Transaction transaction2 = session.beginTransaction(); //此处加了断点,为了查看数据的变化
actor.setAge("6");
actor.setName("蛋皮"); //此处加了断点
//session.update(actor);//不需要更新或者保存当前的对象就能更新数据库里面的值
transaction2.commit();
session.close();
}
在断点之前当我们查看session里面的内容时是这样的
数据库里面是这样的:
当我们执行到修改这个持久态对象的年龄和名字的时候缓存区和快照区的内容发生了变化:
此时,数据库里面的数据也发生了更改,注意此时并没有更新和保存对象:
实现原理:当第一次保存完该对象到数据库的时候,这个对象已经纳入了session的管理,对象此时是持久态的,此时Hibernate会把当前对象的状态保存一份到一级缓存的快照区,当我们修改当前持久态对象的数据的时候,Hibernate会比对当前的快照区,如果发现数据不一致,就自动更新数据库并重新保存当前对象的新状态到快照区.
前面都是操作单表的数据,就是一对一的关系,那么如何关联一对多、多对一、多对多和表之间的数据呢?---建立对象和表之间的映射。
一对多(分类和商品)Category.hbm.xml和Product.hbm.xml
//分类属性 //商品属性
private Integer cid; private Integer pid;
private String cname; private String pname;
private Set<Product> products; private String price;
private String number;
private Category category;
<?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>
<class name="com.yanghao.bean.Category" table="category" >
<!-- 配置主键 -->
<!-- 主键生成策略 -->
<!--
increment:检索自增,会先查询表中的最大的下标,然后下标加一作为下一个新插入的数据的主键,在多线程和集群中有很大的问题,不推荐使用
assigned(指定的、赋值的):自定义主键
identity:自动增长,有索引的增长,Mysql的自动增长策略
sequence:自动增长,有索引的增长,Oracle的自动增长策略
native:本地策略,根据不同数据库能进行不同的自动增长策略的选择,如果是mysql,就用identity,如果是Oracle就用sequence
uuid:随机的字符串策略,用于生成唯一的字符串主键的策略
-->
<id name="cid" column="cid">
<generator class="native"></generator>
</id>
<!-- 配置对应表中的属性 -->
<property name="cname" column="cname"/>
<!-- key的column表示多的一方的外键名,inverse=true让一方级联保存多方时多方放弃外键维护,因为外键是属于多的一方的,两方都维护外键会产生重复的sql操作-->
<set name="products" cascade="delete" inverse="true">
<key column="cid"/>
<one-to-many class="com.yanghao.bean.Product"/>
</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>
<class name="com.yanghao.bean.Product" table="product" >
<!-- 配置主键 -->
<!-- 主键生成策略 -->
<!--
increment:检索自增,会先查询表中的最大的下标,然后下标加一作为下一个新插入的数据的主键,在多线程和集群中有很大的问题,不推荐使用
assigned(指定的、赋值的):自定义主键
identity:自动增长,有索引的增长,Mysql的自动增长策略
sequence:自动增长,有索引的增长,Oracle的自动增长策略
native:本地策略,根据不同数据库能进行不同的自动增长策略的选择,如果是mysql,就用identity,如果是Oracle就用sequence
uuid:随机的字符串策略,用于生成唯一的字符串主键的策略
-->
<id name="pid" column="pid">
<generator class="native"></generator>
</id>
<!-- 配置对应表中的属性 -->
<property name="pname" column="pname"/>
<property name="price" column="price"/>
<property name="number" column="number"/>
<many-to-one name="category" class="com.yanghao.bean.Category" column="cid" cascade="save-update"/>
</class>
</hibernate-mapping>
多对多(学生和选课)Student.hbm.xml和Course.hbm.xml
//学生类属性 //课程属性
private Integer sid; private Integer cid;
private String sname; private String cname;
private Set<Course> courses = new HashSet<Course>(); private Set<Student> students = new HashSet<Student>();
<?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>
<class name="com.yanghao.bean.Student" table="student" >
<!-- 配置主键 -->
<!-- 主键生成策略 -->
<!--
increment:检索自增,会先查询表中的最大的下标,然后下标加一作为下一个新插入的数据的主键,在多线程和集群中有很大的问题,不推荐使用
assigned(指定的、赋值的):自定义主键
identity:自动增长,有索引的增长,Mysql的自动增长策略
sequence:自动增长,有索引的增长,Oracle的自动增长策略
native:本地策略,根据不同数据库能进行不同的自动增长策略的选择,如果是mysql,就用identity,如果是Oracle就用sequence
uuid:随机的字符串策略,用于生成唯一的字符串主键的策略
-->
<id name="sid" column="sid">
<generator class="native"></generator>
</id>
<!-- 配置对应表中的属性 -->
<property name="sname" column="sname"/>
<set name="courses" table="s_c_table">
<key column="sid"/>
<many-to-many column="cid" class="com.yanghao.bean.Course"/>
</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>
<class name="com.yanghao.bean.Course" table="course" >
<!-- 配置主键 -->
<!-- 主键生成策略 -->
<!--
increment:检索自增,会先查询表中的最大的下标,然后下标加一作为下一个新插入的数据的主键,在多线程和集群中有很大的问题,不推荐使用
assigned(指定的、赋值的):自定义主键
identity:自动增长,有索引的增长,Mysql的自动增长策略
sequence:自动增长,有索引的增长,Oracle的自动增长策略
native:本地策略,根据不同数据库能进行不同的自动增长策略的选择,如果是mysql,就用identity,如果是Oracle就用sequence
uuid:随机的字符串策略,用于生成唯一的字符串主键的策略
-->
<id name="cid" column="cid">
<generator class="native"></generator>
</id>
<!-- 配置对应表中的属性 -->
<property name="cname" column="cname"/>
<!-- 双向关联,让一方放弃外键的维护,否则双方生成中间表会产生一样的sql语句,导致主键冲突 -->
<set name="students" table="s_c_table" cascade="save-update" inverse="true">
<key column="cid"></key><!-- 外键名 -->
<many-to-many column="sid" class="com.yanghao.bean.Student"/>
</set>
</class>
</hibernate-mapping>
Hibernate的查询方式汇总:
OID检索 (get load save update delete)
get方法的特点
get方法采用的是立即检索策略(查询):执行到这行的时候,马上发送SQL查询
get方法查询后返回的是真实对象的本身
load方法的特点
load方法采用的是延迟加载(懒加载lazy:什么时候使用,才会去加载)的策略:执行到这行的时候,不会发送 SQL语句,什么时候使用这个对象,才会发送SQL语句。
load查询后返回的是代理对象
对象导航检索(通过一个对象获得其关联对象)
例如:
Category category = session.get(Category.class, 1);
Set<Product> products = category.getProducts();
HQL检索 (Hibernate查询语言,与SQL类似,hql是面向对象的 )
String hql =...
Query query = session.createQuery(hql);
list(); 查询多个
uniqueResult();查询一个
原则: 把表名改成类名, 把数据库的列改成Java里面的属性
QBC,Query By Criteria(条件查询,更加面向对象的查询方式)
语法:
//创建QBC条件查询
Criteria criteria = session.createCriteria(Class);
//添加条件
criteria.add(Restrictions.api...);
//查询多个
List list = criteria.list();
//或者查询一个
Object object = criteria.uniqueResult();
条件API | 描述 | |
---|---|---|
= | Restrictions.eq() | 等于 |
> | Restrictions.gt() | 大于 |
< | Restrictions.lt() | 小于 |
>= | Restrictions.ge() | 大于等于 |
<= | Restrictions.le() | 小于等于 |
between | Restrictions.between() | 在某个范围内 |
like | Restrictions.like() | 模糊查询 |
in | Restrictions.in() | 在...中 |
and | Restrictions.and() | 并且 |