什么是Hibernate?
一个框架
一个Java领域的持久化框架
一个ORM框架
什么叫持久化操作?
和数据库相关的各种操作、对象的增删改查。
什么是ORM:对象/关系映射
ORM思想:将关系数据块中表中的记录映射成为对象,以对象的形式展现,程序员可以将数据库的操作转换为对对象的操作。
ORM采用元数据来描述对象,关系映射细节,原数据采用XML格式,并且存放在专门的对象-关系映射文字中。
ORM框架是对JDBC的封装
Hibernate开发步骤
1.创建Hibernate配置文件(Hibernate.cfg.xml)
2.创建持久化类(Hibernate和Application交互的类)
3.创建对象-关系映射文件(*.hbm.xml)
4.通过hibernate API编写访问数据库的代码(在application中进行)
持久化类(实体类)有什么要求?
1.除了setter和getter,还要有无参构造器,因为Hibernate要是使用反射功能
2.提供一个表示属性,就是OID,一般就是一个id属性
3.不能使用final类,不然Hibernate不能使用代理功能
4.尽量使用包装类
5.*可能需要重写equals和hashCode,不写也能运行,看需要
持久化类不需要任何接口和父类 ,为低侵入式设计,可以保证代码不受污染
对象关系映射文件(*.hbm.xml):
Hibernate根据这个文件生成各种各样的SQL语句
示例(eclipse可以使用工具生成,需要下载):
<!---->
<hibernate-mapping package="com.echo.h"><!--package指定包的名称-->
<class name="News" table="NEWS"><!--类名和表名写在一起-->
<!--id表示主键,length表示许可的长度-->
<id name="id" type="java.lang.Integer" length="10"><!--属性名和列名对应,还由对应数据,结构分明-->
<column name="ID" />
<!-- 数据库主键生成方式,native:使用数据库本地方式 -->
<!--其他策略:
identity;主键自增
sequence:oracle中主键生成的策略
native:上述自动选择identity获取是sequence,根据数据库自动选择
uuid:随机字符串作为主键
assigned:手动设置
-->
<generator class="native" />
</id>
<!--property 表示普通属性:
属性:not-null:不为空
-->
<property name="title" type="java.lang.String">
<column name="TITLE" />
</property>
<property name="author" type="java.lang.String">
<column name="AUTHOR" />
</property>
<property name="date" type="java.sql.Date">
<column name="DATE" />
</property>
</class>
</hibernate-mapping>
Hibernate配置文件(hibernate.cfg.xml):
文件应该位于类路径
默认名为:hibernate.cfg.xml,最好不要改动
<!---->
<hibernate-configuration>
<session-factory>
<!-- 配置连接数据库的基本信息 -->
<!--例如用户名,密码,Driver,url信息-->
<property name="hibernate.connection.username"></property>
<property name="hibernate.connection.password"></property>
<property name="hibernate.connection.driver_class"></property>
<property name="hibernate.connection.url"></property>
<!-- 配置hibernate的基本信息 -->
<!-- hibernate所使用的数据库方言 -->
<!--由于Hibernate可以和很多不同的数据库对接,每个数据库的语言可能有点不一样,所以需要该属性-->
<!--有利于Hibernate生成特定的SQl语句-->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 执行操作时是否在控制台打印SQL -->
<property name="hibernate.show_sql">true</property>
<!-- 是否对SQL进行格式化 -->
<property name="hibernate.format_sql">true</property>
<!-- 指定自动生成数据表的策略 -->
<!--就是如果Hibernate没有发现表,就会自己创建一个-->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- 指定关联的 hbm.xml 文件 -->
<!--关联的持久类需要在这里注册,路径使用/分隔-->
<mapping resource="h/News.hbm.xml"/>
</session-factory>
</hibernate-configuration>
使用HibernateAPI:
1.首先使用Configuration类加载 hibernate.cfg.xml ,因为 hibernate.cfg.xml 中配置着各种连接数据库的必要信息:
Configuration configuration = new Configuration().configure();//默认加载hibernate.cfg.xml
2.创建一个SessionFactory:
这个对象是针对单个数据库映射关系经过编译后的内存镜像,是线程安全的。
这个对象创建比较消耗资源,一般一个项目只有一个,一创建就不能修改
3.打开Session:
是应用程序和数据库之间交互操作的一个单线程对象,是Hibernate的运作中心,持久化对象必须要在session对象下才可以持久化操作,有个一级缓冲,是对JDBC的connection的包装。
Hibernate中的事务:
事务代表数据库的原子操作,所有持久层都应该在事务下进行,即使是在读操作下:
方法:
commit():提交事务
rollback():回滚事务
wasCommitted():判断是否提交事务
hibernate的一些配置:
hibernate.jdbc.fetch_size:
一次从数据库读取的数据量,默认为10,可以调节大小提高性能
hibernate.jdbc.batch_size:
一次对数据库删除、更新、插入的量,也可以提高数据库性能
以上两个对mysql无效,对oracle有效
hibernate.hbm2ddl.auto:
取值有(create、update、create-drop、validate):
create:每次都生成新的数据表,不管是否存在,即旧数据会丢失
update(常用):如果表和数据都没有变,就不动,否则更新数据。
create-drop:在create的基础上,当SessionFactory删除,表也删除
validate:会检查数据库表和*.hbm.xml中的数据是否一致,如不一样,则抛出异常,不会修改表。
<property name="hibernate.hbm2ddl.auto">update</property>
Hibernate中对象的状态:
临时对象:
没有id,session中没有缓存,数据库也没有记录,什么都没有
持久化对象:
与临时对象相反,有id有缓存,有记录,session调用方法可以改变记录,一个持久化对象对应一条记录(对持久化对象的任何操作都会保存到数据库中)
游离对象:
有id,但是从session中消失了,数据库可能记录还在
Hibernate中3种状态的转化:
什么是Session:
Session是Hibernate为程序员提供操作数据库的主要接口,提供了保存,删除,修改,加载Java对象的方法
Session中有一个缓存,里面有一个持久化对象,和数据库中的相关记录对应,称为Hibernate的一级缓存,Hibernate可以根据缓存的变化再一定时间里同步更新数据库信息,这个过程称为flush、
Session中的方法:
取得持久化对象:get()、load()
持久化对象的保存和更新、删除
save():保存
update():更新
saveOrUpdate():保存和更新
delete():删除
开启事务:
beginTransaction()
Session如何从数据库获取数据:
使用get方法,利用反射的机制,获取一个Object,强制转换即可
Session的缓存:
缓存的作用是减少数据库的访问次数,也就是说,当有访问发生,获取一条记录时,Session会把记录缓存到Hibernate的一级缓存里,当第二次访问同一个数据时,只会从缓存中取得数据,缓存实现为Java集合
Session的缓存操作:
flush():
在事务commit提交前自动调用,如果缓存内容和数据库不一致,提交事务后会修改数据库内容,可能发送SQL语句。在Transaction的commit方法中,先调用session的flush方法,再提交事务,flush可能会发送SQL语句,但不会提交事务 。
可以手动调用flush,但是再事务提交之前不会修改数据库
如果在session中查询数据,Hibernate会在查询之前更新一下数据库,以保证数据时最新的。
若记录的ID是由底层数据库使用自增的方式生成的,则在调用save方法后,就会立即提交insert语句,因为要保证对象的ID是存在的
其他情况不会发送
reflesh():
强制发送select语句检查数据库中的数据是否和缓存中的数据一致(需要设置隔离级别为读已提交)
clear():
清理缓存
Session中的
save方法(本质为是对象的状态发生变化,如果对象没有id会创建id):
将临时对象变为持久化对象 ,并为对象分配id ,flush缓存之前会发送一条insert语句修改数据库,id属性不能由用户修改和赋值,即使赋值了,也不会生效,也不会提醒,如果需要提醒,可以使用persist()方法,和save的区别就是多了个异常提醒
get方法:
可以从数据库种加载数据,如果没有数据则返回null。如果需要异常提醒,可以使用load()方法,还附加一个延迟加载策略,就是不会立刻执行操作,如果没有用这个对象,就不加载,有用才加载,返回的时代理对象,如果还没有使用对象session就关闭了,也会有异常,如果数据没有,也会有异常。
update方法(更新游离对象):
若更新持久化对象,不需要使用update,因为提交时Hibernate会检查缓存,如果数据和数据库不一样,就更新数据库信息,因为修改持久化对象时session会感知到,如果缓存里没有对象缓存,则修改不会被感知,这时需要调用update(游离对象),会将游离对象变为持久化对象(写入缓存并更新数据库)。
saveOrUpdate方法:
同时包含了save和Update,什么时候为save什么时候为update?如果时临时对象,执行save如果是游离对象,执行update
delete方法:
删除对象,包括数据库和缓存中(按照id),不存在则异常,Hibernate按照ID删除数据,只要id一致,就会删除,即使其他数据不一样,也会删除
evict方法:
将缓存中持久化对象移除
doWork方法:
获取原生的Connection(JDBC)
在Hibernate中配置c3p0:
只要导入对应的jar包然后再添加配置即可:
配置:
hibernate.c3p0.max_size: 数据库连接池的最大连接数
hibernate.c3p0.min_size: 数据库连接池的最小连接数
hibernate.c3p0.timeout: 数据库连接池中连接对象在多长时间没有使用过后,就应该被销毁
hibernate.c3p0.max_statements: 缓存 Statement 对象的数量
hibernate.c3p0.idle_test_period: 表示连接池检测线程多长时间检测一次池内的所有链接对象是否超时. 连接池本身不会把自己从连接池中移除,而是专门有一个线程按照一定的时间间隔来做这件事,这个线程通过比较连接对象最后一次被使用时间和当前时间的时间差来和 timeout 做对比,进而决定是否销毁这个连接对象。
hibernate.c3p0.acquire_increment: 当数据库连接池中的连接耗尽时, 同一时刻获取多少个数据库连接
持久化类和数据库映射文件:
一般建议一个映射文件对应一个持久化类
class标签属性:
dynamic-insert: 若设置为 true, 表示当保存一个对象时, 会动态生成 insert 语句, insert 语句中仅包含所有取值不为 null 的字段. 默认值为 false
dynamic-update: 若设置为 true, 表示当更新一个对象时, 会动态生成 update 语句, update 语句中仅包含所有取值需要更新的字段. 默认值为 false
可以用来提高性能
在对象-关系映射文件中, 元素用来设置对象标识符. 子元素用来设定标识符生成器.
HIbernate中的事务:
事务:
事务的性质:
原子性:不可再分
一致性:事务在完成时,必须保证使得所有数据保持一致的状态(与开始事务时一样的状态)
隔离性:事务不能查看到事务正在进行时数据的状态
持久性:事务完成后,对数据的影响时永久的,不能撤回
事务会出现的问题:
脏读:事务读取了另一个未提交事务中的数据
不可重复读:在同一次事务中,多次读取的同一数据(数据的值)不一致
幻读:在同一次事务中,多次读取的同一数据(数据的数量,例如5条数据变8条数据)不一致
事务的隔离级别:
读未提交:级别最低,相当于没有保护,但是速度最快
读已提交:避免了读未提交(脏读),不可重复读没解决
可重复读:解决不可重复读和脏读(mysql默认)
串行化:可以解决所有问题,但是效率最低
Hibernate中配置事务的隔离级别:
<!---->
<!--参数由来(二进制)-->
<!--读未提交(0001) 1-->
<!--读已提交(0010) 2-->
<!--可重复读(0100) 4-->
<!--串行化(1000) 8-->
<!--配置为可重复读-->
<property name="hibernate.connection.isolation">4</property>
将Session对象和当前线程绑定配置:
<property name="hibernate.current_session_context_class">thread</property>
配合以下获取session的语句
sessionFactory.getCurrentSession();
可以每次查询session的一致,可以加快速度,使用该方法获得的session会自动关闭,不用手动调用
Hibernate中的HQL、Criteria、SQL:
HQL:
HQL查询
//from 包名
String hql = "from model.User";
//查询
Query query = session.createQuery(hql);
//遍历结果集
List<User> list = query.list();
//获取单个结果
User user = (User)query.uniqueResult();
//条件查询
String hql = "from model.User where id = 1";
Query query = session.createQuery(hql);
//占位符
int s = 2;
String hql = "from model.User where id = ?0";//新版本Hibernate需要在?后添加索引
Query query = session.createQuery(hql);
query.setParameter(0, s);//按照占位符设置
//命名占位符(就是占位符可以有名字了,利用名字来定位)
int s = 2;
String hql = "from model.User where id = :id";//使用 : + 属性名称
Query query = session.createQuery(hql);
query.setParameter("id", s);
//分页查询
String hql = "from model.User";
Query query = session.createQuery(hql);
query.setFirstResult(0);//设置起始页
query.setMaxResults(3);//设置每页最大数
List list = query.list();//列表中为一页的结果
Criteria:
//Criteria查询
//基本查询
CriteriaBuilder builder = session.getCriteriaBuilder();//先获取Builder
CriteriaQuery<User> criteriaQuery = builder.createQuery(User.class);//然后根据User.class定位
Root<User> from = criteriaQuery .from(User.class);//这一步有一点多余,获取根
query.select(from);
//条件查询
query.select(from).where(root.get("id").in("1")); //where id = 1
SQL:
//基本查询
String sql = "select * from user";
NativeQuery query = session.createSQLQuery(sql);
query.addEntity(User.class);//原生sql需要封装
List<User> list = query.list();
//条件查询
String sql = "select * from user where id = ?";
NativeQuery query = session.createSQLQuery(sql);
query.addEntity(User.class);
query.setParameter(1, 4);
List<User> list = query.list();
//分页查询
String sql = "select * from user limit ?,?";
NativeQuery query = session.createSQLQuery(sql);
query.addEntity(User.class);
query.setParameter(1, 0);
query.setParameter(2, 4);
List<User> list = query.list();