一.Hibernate 的环境搭建、配置及 HelloWorld
1.在 Eclipse 中搭建 Hibernate 环境
下载 Hibernate 离线 jar 包(jbosstools-4.4.4.Final-updatesite-core.zip) --》 Eclipse 中 Help --》 Install New Software --》 add ...
2.开发 Hibernate 的 Helloworld
> 准备 Hibernate 的环境
1)导入必须的 jar 包,在 hibernate-release-4.2.4.Final\lib\required 目录下
2)加入数据库驱动的 jar 包
> Hibernate 的开发步骤
1)在类路径的根目录下创建 Hibernate 的配置文件 hibernate.cfg.xml
<hibernate-configuration> <session-factory> <!-- 配置连接数据库的基本信息 --> <property name="connection.username">root</property> <property name="connection.password">qiqingqing</property> <property name="connection.driver_class">com.mysql.jdbc.Driver</property> <property name="connection.url">jdbc:mysql://localhost:3306/hibernate</property>
<!-- 配置 hibernate 的基本信息 --> <!-- hibernate 所使用的数据库方言 --> <property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property> <!-- 执行操作时是否在控制台打印 SQL --> <property name="show_sql">true</property> <!-- 是否对 SQL 进行格式化 --> <property name="format_sql">true</property> <!-- 指定自动生成数据表的策略 --> <property name="hbm2ddl.auto">update</property> <!-- 指定关联的 .hbm.xml 文件 注意不是 . 的形式,而是 / --> <mapping resource="qi/hibernate/helloworld/News.hbm.xml"/> </session-factory> </hibernate-configuration>
2)创建持久化的类 News
同普通的 JavaBean 创建方式一样
3)在 JavaBean 对应的包中创建 对象-关系映射文件(自动生成) News.hbm.xml
<class name="qi.hibernate.helloworld.News" table="NEWS"> <id name="id" type="java.lang.Integer"> <column name="ID" /> <!-- 指定主键生成方式 native:使用数据库本地方式 --> <generator class="native" /> </id> <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>
4)通过 Hibernate API 编写访问数据库的代码
@Test public void test() //1. 创建一个 SessionFactory 对象 SessionFactory sessionFactory = null; //1). 创建 Configuration 对象: 对应 hibernate 的基本配置信息和 对象关系映射信息 Configuration configuration = new Configuration().configure(); //4.0 之前这样创建 // sessionFactory = configuration.buildSessionFactory(); //2). 创建一个 ServiceRegistry 对象: hibernate 4.x 新添加的对象 //hibernate 的任何配置和服务都需要在该对象中注册后才能有效. ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()) .buildServiceRegistry(); //3). sessionFactory = configuration.buildSessionFactory(serviceRegistry); //2. 创建一个 Session 对象 Session session = sessionFactory.openSession(); //3. 开启事务 Transaction transaction = session.beginTransaction(); //4. 执行保存操作 News news = new News("Java", "qiqingqing", new Date(new java.util.Date().getTime())); session.save(news); //5. 提交事务 transaction.commit(); //6. 关闭 Session session.close(); //7. 关闭 SessionFactory 对象 sessionFactory.close(); }
> Hibernate 的单元测试类的写法(一般固定)
public class HibernateTest { private SessionFactory sessionFactory; private Session session; private Transaction transaction;
@Before public void init(){ Configuration configuration = new Configuration().configure(); ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()) .buildServiceRegistry(); sessionFactory = configuration.buildSessionFactory(serviceRegistry); session = sessionFactory.openSession(); transaction = session.beginTransaction(); }
@After public void destory(){ transaction.commit(); session.close(); sessionFactory.close(); }
@Test public void test() { System.out.println("test"); } }
注: 若忘记在 hibernate.cfg.xml文件中配置 News.hbm.xml 文件,则报 org.hibernate.InvalidMappingException: Unable to read XML
二. Session
1. Session 缓存(Hibernate的一级缓存):表现在同样的查询操作,因为 Session缓存的存在只执行一次 SQL语句
1) 操作 Session 缓存的三种方式
> flush() 使数据表中的数据和 Session 缓存中的对象状态保持一致,为了达到一致的效果,则可能会发送 SQL 语句
① 在 Transaction 的 commit() 方法中:先调用 flush() 方法,再提交事务
② flush() 方法可能会发送 SQL 语句,但不会提交事务
③ 注意: 在未提交事务或显式的调用 flush() 之前,也有可能进行 flush() 操作
>执行 HQL 或 QBC 操作,会先进行 flush() 操作,以保证得到数据表的最新记录
>例外的情况: 若记录的 ID 是由数据库使用自增的方式生成的,则在调用 save() 方法时,会立即发送 insert 语句。因为 save() 方法后,必须保证对象的 ID 是存在的
> refresh() 一定会强制发送 select 语句,以使缓存中的对象的状态和数据表中对应的记录保持一致。
注意:当调用了 Session 的 refresh() 方法后,若与预期结果不一样,需要设置 Hibernate 的数据库事务隔离级别,在 Hibernate.cfg.xml 文件中如下设置
<!-- 设置 Hibernate 的事务隔离级别 --> <property name="connection.isolation">2</property>
> clear() 清理缓存 表现:若两次相同的 get() 查询操作,第一次之后调用了 Session 的 clear() 方法,则会发送两个 SQL 语句
2) Session 的核心方法
> save() 方法:临时对象 变为 持久化对象
作用: ① 为对象分配 id ②在 flush() 缓存时会发送一条 insert 语句 ③在 save() 方法之前设置 id 是无效的 ④持久化对象的 id 不能被修改,若在 save() 方法后重新设置 id 会报异常
> persist() 方法:也会执行 insert 操作,和 save() 方法不同的是,在调用 persist() 方法之前,若已经设置了 id,则不会执行 insert 语句,会抛异常
> get() VS load() 方法:
①执行 get() 方法会立即加载对象; 执行 load() 方法,若不使用该对象,则不会立即执行查询操作,而返回一个代理对象,即: get()是立即检索 load() 是延迟检索
②若在需要初始化代理对象之前已经关闭了 Session,load() 方法可能会抛出 LazyInitializationException 异常
③若数据表中没有查询对应的记录,且 Session 也没有被关闭,同时需要使用对象时,get() 返回 null; load() 若需使用该对象 抛异常
> update() 游离对象 变为 持久化对象
①更新一个持久化对象,不需要显式的调用 update() 方法,因为在调用 Transaction 的 commit() 方法时,会先执行 Session 的 flush() 方法(使数据表中的数据和 Session 缓存中的对象状态保持一致,若不一致了,则发送 update 语句)
②更新一个游离对象,需要显式的调用 Session 的 update() 方法,可以把一个游离对象变为持久化对象。何为 游离对象?
注意:
①无论要更新的游离对象和数据表是否一致,都会发送 update 语句,但对于持久化对象,不更新数据就不会发送 update 语句。如何能让 update() 方法不再盲目的发送 update 语句呢?解决方案:在 .hbm.xml 文件的 class 节点设置 select-before-update=true (默认为false,但通常不需要设置此属性)
②若数据表中没有对应的对象,调用了 update() 方法会抛出异常
③当 update() 方法关联一个游离对象时,如果在 Session 的缓存中存在相同 id属性的持久化对象,会抛出异常(NonUniqueObjectException),即在 Session 缓存中不能存在两个相同 id 的对象
> saveOrUpdate() 同时包含了 save() 和 update() 方法的功能
对游离对象执行 update() 方法,对 临时对象执行 save() 方法,根据对象有无 id来区分游离对象和临时对象(创建一个对象,没有设置 id),有 id为游离对象,无 id为临时对象
注意:若 id 不为 null,但数据表中没有和其对应的记录,则抛异常
> delete() 方法,执行删除操作
①只要要删除对象的 id 和数据表中的一条记录的 id 对应,无论是持久化对象还是游离对象都准备执行 delete 操作 删除
②若 id 在数据表中没有对应的记录,则抛出一个异常
③设置 Hibernate 配置文件的 hibernate.use_identifier_rollback 属性, 为 true,则删除对象后就会把其 id 属性置为 null
> evict() 方法 从缓存中移出指定的持久化对象
> 调用存储过程,需要先获取 JDBC 原生的 API ,如何获取 Connection 对象?
@Test public void testDoWork(){ session.doWork(new Work() { @Override public void execute(Connection connection) throws SQLException { System.out.println(connection); } }); }
三、Hibernate 的配置文件
1.在 hibernate 中使用 c3p0数据源
1)导入 jar 包 hibernate-release-4.2.4.Final\lib\optional\c3p0 下所有的
2)加入配置
–c3p0.max_size: 数据库连接池的最大连接数
–c3p0.min_size: 数据库连接池的最小连接数
–c3p0.acquire_increment: 当数据库连接池中的连接耗尽时, 同一时刻获取多少个数据库连接
–c3p0.timeout: 数据库连接池中连接对象在多长时间没有使用过后,就应该被销毁
–c3p0.idle_test_period: 表示连接池检测线程多长时间检测一次池内的所有链接对象是否超时. 连接池本身不会把自己从连接池中移除,而是专门有一个线程按照一定的时间间隔来做这件事,这个线程通过比较连接对象最后一次被使用时间和当前时间的时间差来和 timeout 做对比,进而决定是否销毁这个连接对象。
–c3p0.max_statements: 缓存 Statement 对象的数量
2.只对 Oracle 数据库有效的两个配置项
1)jdbc.fetch_size:设定 JDBC 的 Statement 读取数据的时候每次从数据库中取出的记录条数 最优:100
2)jdbc.batch_size:设定对数据库进行批量删除,批量更新和批量插入的时候的批次大小 最优:30 ,其余的配置项可参看 Hibernate 的官方文档
/hibernate-release4.3.11.Final/documentation/manual/en-US/html/index.html
四、对象关系映射文件 即 *.hbm.xml 注:一般一个 .hbm.xml 文件最好只对应一个类
1. hibernate-mapping 是 hibernate 映射文件的根元素
> package (可选): 指定一个包前缀,如果在映射文档中没有指定全限定的类名, 就使用这个作为包名。
2. class
> name:指定该持久化类映射的持久化类的类名
> table:指定该持久化类映射的表名, Hibernate 默认以持久化类的类名作为表名
> dyanmic-update:若设置为 true, 表示当更新一个对象时, 会动态生成 update 语句, update 语句中仅包含所有取值需要更新的字段. 默认值为 false
> dyanmic-insert: 若设置为 true, 表示当保存一个对象时, 会动态生成 insert 语句, insert 语句中仅包含所有取值不为 null 的字段. 默认值为 false
> select-before-update: 设置 Hibernate 在更新某个持久化对象之前是否需要先执行一次查询. 默认值为 false
3.id
> name: 标识持久化类 OID 的属性名
> column: 设置标识属性所映射的数据表的列名(主键字段的名字).
> unsaved-value:若设定了该属性, Hibernate 会通过比较持久化类的 OID 值和该属性值来区分当前持久化类的对象是否为临时对象
4.generator: 设定持久化类标识符生成器
> class: 指定使用的标识符生成器全限定类名或其缩写名
> 主键生成策略:
①increment 标识符生成器由 Hibernate 以递增的方式为代理主键赋值(因并发问题,开发中一般不用)
② identity 标识符生成器由底层数据库来负责生成标识符, 它要求底层数据库把主键定义为自动增长字段类型
③sequence 标识符生成器利用底层数据库提供的序列来生成标识符.(MySQL 不支持)
④hilo 标识符生成器由 Hibernate 按照一种 high/low 算法生成标识符, 它从数据库的特定表的字段中获取 high 值.
⑤native 标识符生成器依据底层数据库对自动生成标识符的支持能力, 来选择使用 identity, sequence 或 hilo 标识符生成器.(常用)
> property :用于指定类的属性和表的字段的映射
> 映射派生属性: 在相应 JavaBean 中定义相应字段,但不用在数据表中添加字段,在 .hbm.xml 文件中配置
<!-- 映射派生属性 --> <property name="desc" fornula="(select concat(author,': ',title) from News n where n.id = id)"></property>
测试使用: System.out.print(news.getDesc());
5.映射 Java 的日期 和 时间类型
1)两个基础知识:
①在 Java 中, 代表时间和日期的类型包括: java.util.Date 和 java.util.Calendar. 此外, 在 JDBC API 中还提供了 3 个扩展了 java.util.Date 类的子类: java.sql.Date, java.sql.Time 和 java.sql.Timestamp, 这三个类分别和标准 SQL 类型中的 DATE, TIME 和 TIMESTAMP 类型对应
②在标准 SQL 中, DATE 类型表示日期, TIME 类型表示时间, TIMESTAMP 类型表示时间戳,(同时包含日期和时间信息).
2)如何进行映射:
I. 因为 java.util.Date 是 java.sql.Date, java.sql.Time 和 java.sql.Timestamp 的父类, 所以 java.util.Date可以对应标准 SQL 类型中的 DATE, TIME 和 TIMESTAMP
II. 基于 I, 所以在设置持久化类的 Date 类型时, 设置为 java.util.Date.
III. 如何把 java.util.Date 映射为 DATE, TIME 和 TIMESTAMP ?可以通过 property 的 type 属性来进行映射,例如:
<property name="date" type="timestamp"> <column name="DATE" /> </property> <property name="date" type="data"> <column name="DATE" /> </property> <property name="date" type="time"> <column name="DATE" /> </property>
其中 timestamp, date, time 既不是 Java 类型, 也不是标准 SQL 类型, 而是 hibernate 映射类型.
6.Java 大对象类型的 hibernate 映射(了解即可)
1)存储二进制图片
2)读取二进制图片
注:stream 的 available() 方法可以获取文件的大小
7.映射组成关系
1)Hibernate 把持久化类的属性分为两种:
– 值(value)类型: 没有 OID, 不能被单独持久化, 生命周期依赖于所属的持久化类的对象的生命周期
– 实体(entity)类型: 有 OID, 可以被单独持久化, 有独立的生命周期
2)映射 使用 <component> 来映射
Hibernate 使用 <component> 元素来映射组成关系, 该元素表名 pay 属性是 Worker 类一个组成部分, 在 Hibernate 中称之为组件(没有 Id,实体类有 Id 属性的不能使用 component 来映射)