一、简介
使用Hibernate可以以面向对象的方式来操作数据库,而不需要书写sql语句。
Hibernate是一种ORM(Object relation mapping):使用映射文件来配置实体类对象与表之间的映射关系,对象名对应表明,属性对应列。使用中只需要操作实体类对象即可,不需要操作表。
ORM分四个等级:Hibernate为4级ORM;MyBatis为2级ORM;DBUtils为1级ORM
二、环境搭建
1、导包(hibernate必须的包+驱动包)
2、创建数据库,准备表
3、准备实体类以及书写ORM元数据(对象与表的映射配置文件)
(1)导入约束(可以存在本地或者从网上导入——preferences中XML catalog)
(2)实体
(3)ORM元数据
4、书写主配置文件
5、书写代码测试
三、配置文件详解
orm元数据(xxx.hbm.xml)
<hibernate-mapping package="">
<!--
class元素: 配置实体与表的对应关系的
name: 完整类名
table:数据库表名
-->
<class name="" table="" >
<id name="" >
<!-- 主键生成策略 -->
<generator class=""></generator>
</id>
<!--
property元素:除id之外的普通属性映射
name: 填写属性名
column(可选): 填写列名
type(可选):填写列(属性)的类型.hibernate会自动检测实体的属性类型.
每个类型有三种填法: java类型(java.lang.String)|hibernate类型(string)|数据库类型(如下注释中的指定方法)
not-null(可选):配置该属性(列)是否不能为空. 默认值:false
length(可选):配置数据库中列的长度. 默认值:使用数据库类型的最大长度
-->
<!--
<column name="cust_name" sql-type="varchar" ></column>
-->
<property name="cust_name" column="cust_name" ></property>
<property name="cust_source" column="cust_source" ></property>
<property name="cust_industry" column="cust_industry" ></property>
<property name="cust_level" column="cust_level" ></property>
<property name="cust_linkman" column="cust_linkman" ></property>
<property name="cust_phone" column="cust_phone" ></property>
<property name="cust_mobile" column="cust_mobile" ></property>
</class>
</hibernate-mapping>
hibernate.cfg.xml
<hibernate-configuration>
<session-factory>
必须配置:
<property name="hibernate.connection.driver_class">xxxxxxxx</property>
<property name="hibernate.connection.url">xxxxxxx</property>
<property name="hibernate.connection.username">xxxxxxx</property>
<property name="hibernate.connection.password">xxxxxxx</property>
<!-- 数据库方言 -->
<!-- 不同数据库中,sql语句略有不同,指定数据库方言可以让hibernate框架在生成sql语句时针对数据库方言生成。 -->
<!-- 选择mysql方言时选择最短的那条 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
可选配置:
<property name="hibernate.show_sql">true</property>
<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=""/>
</session-factory>
</hibernate-configuration>
四、API详解
读取配置:Configuration
创建session:SessionFactory
获得事务操作对象,执行增删改查操作:session
控制事务:Transaction
五、Hibernate中的实体规则
1、实体类创建的注意事项
(1)持久化类提供无参的构造(因为在Hibernate底层需要使用反射生成类的实例)
(2)成员变量私有化,提供get/set方法来对外提供属性。
(3)持久化类中的属性,应尽量使用包装类型。
(4)持久化类需要提供oid,与数据库中主键列对应。
(5)不要用final修饰实体类。(由于hibernate中使用的cglib对象代理是通过继承实现的)
2、主键类型
(1)自然主键:表的业务列中,有某列符合非空且唯一的特征,则该列就可以作为主键使用,叫做自然主键。
(2)代理主键:表的业务列中没有符合非空且唯一的特征时,则创建一个没有业务意义的列作为主键,叫做代理主键。
3、主键生成策略
identity:主键自增,有数据库来维护主键值,录入时不需要手动指定
increment:主键自增,有hibernate维护,每次插入前会先查询表中id最大值,+1作为新主键值(有线程安全问题)
sequence:Oracle中的主键生成策略。
hilo:高低位算法(数据库中主键自增算法)。主键自增,有数据库来维护主键值
native:hilo+sequence+identity中自动选择一个合适的。
uuid:用来产生唯一的随机字符串作为主键。必须用String作为主键类型才能使用。
assigned:自然主键生成策略,hibernate不会管理主键值,由开发人员自己录入。
六、Hibernate中的对象状态
1、瞬时状态
没有id,没有与session关联(没有在session缓存中)。
2、持久化状态
有id,与session有关联(在session缓存中)。
3、游离(托管)状态
有id,没有与session关联(没有在session缓存中)。
状态转换图:
七、Hibernate实现。
1、Hibernate一级缓存
Hibernate中会有一个session缓存。会将实体对象存在缓存中。重复调用时只从缓存中拿。
2、Hibernate中的事务
在hibernate.cfg.xml中指定事务的隔离级别
## specify a JDBC isolation level
#hibernate.connection.isolation 1|2|4|8
0001 1 读未提交
0010 2 读已提交
0100 4 可重复读
1000 8 串行化
<property name="hibernate.connection.isolation">4</property>
事务都是要在业务层使用,在业务开始前打开事务,业务结束后提交事务,出错则回滚。
需要将session绑定在线程上,使得service层和dao层中使用的session是同一个session:
在Hibernate中,使用sessionFactory.getCurrentSession()方法即可获得与当前线程绑定的session对象。
注意:1.调用getCurrentSession方法必须配合主配置文件中的一段配置。
<property name="hibernate.current_session_context_class">thread</property>
2.通过getCurrentSession方法获得的session对象当事务提交时会自动关闭,因此不要手动调用close关闭。
补充学习:
之前学习中获取的connection对象也需要保证在不同的层获取到的是同一个,使用ThreadLocal对象。ThreadLocal对象实际上是一个map,它内部实现会默认使用执行它的线程作为key;我们只需要在连接池工具类中将获取的连接绑定到某个线程上,在同一线程中下次使用的时候就可以通过ThreadLocal对象中直接取出这个连接。
下面为相应的代码。
public class MyDataSourceUtils {
// 获得Connecton---------从连接池中获取
private static ComboPooledDataSource dataSource = new ComboPooledDataSource();
// 创建ThreadLocal
private static ThreadLocal<Connection> tl = new ThreadLocal<>();
// 开启事务
public static void startTransaction() throws SQLException {
Connection conn = getCurrentConnection();
conn.setAutoCommit(false);
}
// 获得当前线程上绑定的conn
public static Connection getCurrentConnection() throws SQLException {
// 从ThreadLocal中寻找当前是否有现成的Connection
Connection conn = tl.get();// 执行这个代码需要某个线程执行,因此就相当于以该线程名为key,get到里面的value
if (conn == null) {
// 如果没有现成的Connection,就获取一个新的
conn = getConnection();
// 将connection资源绑定到ThreadLocal(map)上
tl.set(conn);
}
return conn;
}
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
// 回滚事务
public static void rollback() throws SQLException {
getCurrentConnection().rollback();
}
// 提交事务
public static void commit() throws SQLException {
Connection conn = getCurrentConnection();
conn.commit();
// 本次操作结束了,将Connection从ThreadLocal中移除
tl.remove();
conn.close();
}
}