要去面试了,复习一下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> <property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property> <property name="hibernate.connection.password">tiger</property> <property name="hibernate.connection.url">jdbc:oracle:thin:@localhost:1521:orcl</property> <property name="hibernate.connection.username">scott</property> <!-- <property name="hibernate.default_schema">hibernate5</property> --> <property name="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</property> <property name="show_sql">true</property> <property name="format_sql">true</property> <!-- 指定数据表的生成策略 create: 每次操作都重新删除新建表 update: 如果表结构有改变,将进行更新,但不会删除原来被更新的行与列 create-drop: 每次操作时新建表,SESSION关闭时会删除表 validate: 会检查表结构是否发生变化,若有变化会抛出异常,但不会修改表结构 --> <property name="hbm2ddl.auto">update</property> <!-- 设置hibernate的事务隔离级别 1: 读未提交 2: 读已提交 4: 可重复读 8: 序列化 --> <property name="hibernate.connection.isolation">2</property> <!-- 删除对象后使其OID置为null --> <property name="hibernate.use_identifier_rollback">true</property> <!-- 配置管理session的方式--> <property name="current_session_context_class">thread</property> <!-- 启用二级缓存 --> <property name="cache.use_second_level_cache">true</property> <!-- 配置使用的二级缓存的产品 --> <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property> <!-- 配置启用查询缓存 --> <property name="cache.use_query_cache">true</property> <!-- <property name="hibernate.c3p0.max_size">10</property> <property name="hibernate.c3p0.min_size">5</property> <property name="hibernate.c3p0.acquire_increment">2</property> <property name="hibernate.c3p0.idle_test_period">2000</property> <property name="hibernate.c3p0.timeout">2000</property> <property name="hibernate.c3p0.max_statements">10</property> --> <!-- invalid for mysql, valid for oracle --> <property name="hibernate.jdbc.fetch_size">100</property> <property name="hibernate.jdbc.batch_size">30</property> <!-- 指定关联的 .hbm.xml 文件 --> <!-- <mapping resource="hibernate/helloworld/News.hbm.xml" /> <mapping resource="hibernate/helloworld/Worker.hbm.xml"/> <mapping resource="ntoone/Customer.hbm.xml"/> <mapping resource="ntoone/Order.hbm.xml"/> --> <!-- <mapping resource="ntoone/both/Customer.hbm.xml"/> <mapping resource="ntoone/both/Order.hbm.xml"/> --> <!-- <mapping resource="onetoone/foreign/Department.hbm.xml"/> <mapping resource="onetoone/foreign/Manager.hbm.xml"/> <mapping resource="onetoone/primary/Department.hbm.xml"/> <mapping resource="onetoone/primary/Manager.hbm.xml"/> --> <!-- <mapping resource="nton/Category.hbm.xml"/> <mapping resource="nton/Item.hbm.xml"/> <mapping resource="unionclass/Person.hbm.xml"/> <mapping resource="strategy/Customer.hbm.xml"/> <mapping resource="strategy/Order.hbm.xml"/>--> <mapping resource="twolevelcache/Department.hbm.xml"/> <mapping resource="twolevelcache/Employee.hbm.xml"/> <!-- 二级缓存相关配置 --> <class-cache usage="read-write" class="twolevelcache.Employee"/> <class-cache usage="read-write" class="twolevelcache.Department"/> <collection-cache usage="read-write" collection="twolevelcache.Department.emps"/> </session-factory> </hibernate-configuration>
其中我觉得比较重要少见的是:
<!-- 删除对象后使其OID置为null --> <property name="hibernate.use_identifier_rollback">true</property>
<!-- 设置hibernate的事务隔离级别 1: 读未提交 2: 读已提交 4: 可重复读 8: 序列化 --> <property name="hibernate.connection.isolation">2</property>
<!-- 配置管理session的方式--> <property name="current_session_context_class">thread</property>
<!-- 二级缓存相关配置 --> <class-cache usage="read-write" class="twolevelcache.Employee"/> <class-cache usage="read-write" class="twolevelcache.Department"/> <collection-cache usage="read-write" collection="twolevelcache.Department.emps"/>
先看下hibernate包下面的内容。
大致看一下吧,不去找什么资料解释了,权且知其然吧,也没有什么延伸扩展,文末会有相关代码的下载地址。
先看第一个dao子包:
HibernateTest.java
package hibernate.dao; import java.sql.Connection; import java.sql.SQLException; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import org.hibernate.jdbc.Work; import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistryBuilder; import org.junit.After; import org.junit.Before; import org.junit.Test; import hibernate.util.HibernateUtils; public class HibernateTest { private SessionFactory sessionFactory; private Session session; private Transaction transaction; @Before public void init() { System.out.println("init"); // 1. 创建一个SessionFactory对象 sessionFactory = null; Configuration configuration = new Configuration().configure(); // before 4.0 // sessionFactory = configuration.buildSessionFactory(); ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()) .buildServiceRegistry(); sessionFactory = configuration.buildSessionFactory(serviceRegistry); // 2. 创建一个Session 对象 session = sessionFactory.openSession(); // 3. 开启事务 transaction = session.beginTransaction(); } @After public void destory() { System.out.println("destory"); // 5. 提交事务 transaction.commit(); // 6. 关闭Session session.close(); // 7. 关闭SesssionFactory sessionFactory.close(); } @Test public void testManageSession() {
// 这个session是通过getCurrentSession()方法创建的,所以是通过thread来管理的 Session session = HibernateUtils.getInstance().getSession(); System.out.println("-->" + session.hashCode()); Transaction transaction = session.beginTransaction(); DepartmentDao departmentDao = new DepartmentDao(); Department dept = new Department(); dept.setName("ATGUIGU"); departmentDao.save(dept); // 若SESSION是由THREAD来管理的,则提交事务或回滚时SESSION已经关闭 transaction.commit(); System.out.println(session.isOpen()); } @Test public void testBatch() {
// 通过该方法可以使用原生的onnection对象来进行批处理操作 session.doWork(new Work(){ @Override public void execute(Connection connection) throws SQLException { // according to primitive jdbc api, most efficient, swiftest // see http://blog.csdn.net/huaguoming/article/details/8496398 for help } }); } }
下面看一下hibernate映射文件。
Department.hbm.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2017-7-29 2:43:46 by Hibernate Tools 3.5.0.Final --> <hibernate-mapping package="hibernate.dao"> <class name="Department" table="DEPARTMENT"> <id name="id" type="java.lang.Integer"> <column name="ID" /> <generator class="native" /> </id> <property name="name" type="java.lang.String"> <column name="NAME" /> </property>
<!-- 字段名是emps,对应的表是EMPLOYEE表,inverse="true"说明由Employee对象来维持一对多的关系,即通过
EMPLOYEE表的DEPT_ID即Employee类的deptId字段来联系DEPARTMENT,lazy="true"说明是懒加载,在获取
Department对象的时候并不会同时获取关联表EMPLOYEE中相应的存储在emps中的内容,只有真正用到emps对象
的时候才会查询数据库获取相应数据。
--> <set name="emps" table="EMPLOYEE" inverse="true" lazy="true"> <key><column name="DEPT_ID" /></key> <one-to-many class="Employee" /> </set> </class> </hibernate-mapping>
其中生成id的方式设置成了native,也就是由相应数据库自己决定生成id的方式。
其中还有一个一对多映射。
Employee.hbm.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2017-7-29 2:43:46 by Hibernate Tools 3.5.0.Final --> <hibernate-mapping package="hibernate.dao"> <class name="Employee" table="EMPLOYEE"> <id name="id" type="java.lang.Integer"> <column name="ID" /> <generator class="native" /> </id> <property name="name" type="java.lang.String"> <column name="NAME" /> </property> <property name="salary" type="float"> <column name="SALARY" /> </property> <property name="email" type="java.lang.String"> <column name="EMAIL" /> </property>
<!--
dept属性对应多个Employee对象,关于fetch几种方式的不同,可以见下面的文章
http://blog.csdn.net/haolongabc/article/details/21629889
--> <many-to-one name="dept" class="Department" fetch="join"><!-- join select subselect--> <column name="DEPT_ID" /> </many-to-one> </class>
<!-- 一种HQL检索方式,参见下面的文章
https://www.cnblogs.com/linyueshan/p/5819801.html
--> <query name="salaryEmps"><![CDATA[FROM Employee e WHERE e.salary > :minSal AND e.salary < :maxSal]]></query> </hibernate-mapping>
相应的有一个多对一关系, 上面有相关解释。
下面再看一下helloworld子包。
HibernateTest.java
package hibernate.helloworld; import java.io.FileInputStream; import java.io.InputStream; import java.sql.Blob; import java.sql.Connection; import java.sql.Date; import java.sql.SQLException; import org.hibernate.Hibernate; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import org.hibernate.jdbc.Work; import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistryBuilder; import org.junit.After; import org.junit.Before; import org.junit.Test; import com.sun.media.jfxmedia.control.VideoDataBuffer; public class HibernateTest { private SessionFactory sessionFactory; private Session session; private Transaction transaction; @Before public void init() { System.out.println("init"); // 1. 创建一个SessionFactory对象 sessionFactory = null; Configuration configuration = new Configuration().configure(); // before 4.0 // sessionFactory = configuration.buildSessionFactory(); ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()) .buildServiceRegistry(); sessionFactory = configuration.buildSessionFactory(serviceRegistry); // 2. 创建一个Session 对象 session = sessionFactory.openSession(); // 3. 开启事务 transaction = session.beginTransaction(); } @After public void destory() { System.out.println("destory"); // 5. 提交事务 transaction.commit(); // 6. 关闭Session session.close(); // 7. 关闭SesssionFactory sessionFactory.close(); }
/**
* get方法
*/ @Test public void testGet() { System.out.println("test"); // 4. 执行保存操作 // News news = new News("Java", "ATGUIGU", new Date(new java.util.Date().getTime())); // session.save(news); News new2 = (News)session.get(News.class, 1); System.out.println(new2); News new3 = (News)session.get(News.class, 1); System.out.println(new3); } /** * flush: 使数据表中的记录和session缓存中的对象的状态保持一致, * 为了保持一致,则是可能会发送对应的sql语句 * 1. 调用transaction的commit()方法中:先调用session的 * flush方法,再提交事务。 * 2. flush方法可能会发送sql,不会提交事务 * 3. 注意:在未提交事务或显示的调用session.flush()方法之前,也有可 * 能会进行flush操作。 * 1)执行HQL或QBC查询,会先进行flush操作,以得到数据表的最新的记录 * 2)若记录的ID是由底层数据库使用自增的方式生成的,则在调用save()方 * 法后会立即发送insert()语句,因为save()方法后,必须保证对象的 * ID是存在的 */ @Test public void testSessionFlush() { News news = (News)session.get(News.class, 1); news.setAuthor("Oracle"); // commit之前会调用flush() News news2 = (News) session.createCriteria(News.class).uniqueResult(); System.out.println(news2); } @Test public void testSessionFlush2() { News news = new News("Java", "SUN", new Date(new java.util.Date().getTime())); session.save(news); } /** * refresh会强制发送SELECT语句,以使SESSION缓存中对象的状态和数据表中对 * 应的记录保持一致!
* 关于clear方法,可以查看下面的文章:
* https://www.cnblogs.com/L-a-u-r-a/p/7049666.html */ @Test public void testRefresh() { News news = (News)session.get(News.class, 1); System.out.println(news); // session.refresh(news); session.clear(); News news2 = (News)session.get(News.class, 1); System.out.println(news2); } /** * 对象的四种状态 */ /** * 1.save()方法 * 1)使一个临时对象变为持久化对象 * 2)为对象分配ID * 3)在flush缓存时会发送一条INSERT语句 * 4)在save方法之前的id是无效的 * 5)持久化对象的ID是不能被修改的 */ @Test public void testSave() { News news = new News(); news.setTitle("CC"); news.setAuthor("cc"); news.setDate(new Date(new java.util.Date().getTime())); // news.setId(100); System.out.println(news); session.save(news); // news.setId(200); System.out.println(news); } /** * persist():也会执行INSERT操作 * 和save()的区别: * 在调用persist方法之前,若对象已经有id了,则不会执行insert,而相反的,会抛出一个 * 异常 */ @Test public void testPersist() { News news = new News(); news.setTitle("EE"); news.setAuthor("dd"); news.setDate(new Date(new java.util.Date().getTime())); news.setId(200); session.persist(news); } /** * 1. 执行 get 方法会立即加载对象, * 而执行 load 方法,若不使用该对象,则不会立即执行查询操作,而返回一个代理对象。 * get 是立即检索,load 是延迟检索 * 2. 若数据库中没有对应的记录,Session也没有被关闭 * get 返回null * load 若不使用该对象的任何属性,没问题;若需要初始化了,抛出异常 * 3. load方法可能会抛出LazyInitializationException异常:在需要初始化代理对象之前已经关闭了session */ @Test public void testLoad() { News news = (News)session.load(News.class, 10); System.out.println(news.getClass().getName()); session.close(); System.out.println(news); } @Test public void testGet() { News news = (News)session.get(News.class, 1); session.close(); System.out.println(news); } /** * update: * 1. 若更新一个持久化对象,不需要显示的调用update方法,因为在调用 * transaction的commit方法时,会先执行session的flush方法 * 2. 更新一个游离对象,需要显示的调用session的update方法,可以把 * 一个游离对象变为持久化对象。
* hibernate对象的四种状态:
* https://www.cnblogs.com/cat36/p/5553232.html * 需要注意的: * 1. 无论要更新的游离对象和数据表的记录是否一致,都会发送update语句 * 如何能让update方法不再盲目的触发update语句呢?在.hbm.xml文件 * class节点设置一个属性,叫select-before-update为true,但通常不用 * 设置该属性 * 2. 若数据表中没有对应的记录,但还调用了update方法,会抛出异常 * 3. 当update方法关联一个游离对象时,如果在session的缓存中已经存在相同的OID持久化对象,会抛出异常,因为在session缓存中不能有两 * 个OID相同的对象 */ @Test public void testUpdate() { News news = (News)session.get(News.class, 1); transaction.commit(); session.close(); // news.setId(300); session = sessionFactory.openSession(); transaction = session.beginTransaction(); // news.setAuthor("SUN"); News news2 = (News)session.get(News.class, 1); session.update(news); } /** * 注意: * 1. 若OID不为null, 但数据表中没有相应记录,会抛出异常 * 2. 了解:OID值等于id的unsaved-value属性值的对象,也认为是一个游离对象 */ @Test public void testSaveOrUpdate() { News news = new News("FF", "ff", new Date(1111111111111L)); news.setId(11); session.saveOrUpdate(news); } /** * delete:执行删除操作。只要OID和数据表中的一条记录对应,就会准备执行delete操作,若OID在 * 数据表中没有对应的记录,则抛出异常 * 可以通过设置hibernate配置文件的hibernate.use_identifier_rollback属性,使执行删除后的对象的id为null */ @Test public void testDelete() { // News news = new News(); // news.setId(1); News news = (News) session.get(News.class, 2); session.delete(news); System.out.println(news); } /** * evict: 从session缓存中把指定的持久化对象移除 */ @Test public void testEvict() { News news1 = (News)session.get(News.class, 1); News news2 = (News)session.get(News.class, 2); news1.setTitle("AA"); news2.setTitle("BB"); session.evict(news1); }
/**
* 进行批处理
*/ @Test public void testDoWork() { session.doWork(new Work() { @Override public void execute(Connection arg0) throws SQLException { System.out.println(arg0); // 调用存储过程 } }); }
/**
* 动态更新,参看下面的文章:
* http://blog.csdn.net/tctctttccc/article/details/76785199
*/ @Test public void testDynamicUpdate() { News news = (News) session.get(News.class, 1); news.setAuthor("ABCD"); }
/**
* 主键id生成策略
* http://ryxxlong.iteye.com/blog/612446
*/ @Test public void testIdGenerator() throws InterruptedException { News news = new News("AA", "aa", new Date(22222222222222L)); session.save(news); Thread.sleep(5000); }
/**
* 属性更新或插入,参见下面的文章:
* http://blog.csdn.net/lengxingxing_/article/details/68925879
*/ @Test public void testPropertyUpdate() { News news = (News) session.get(News.class, 1); news.setAuthor("AAA"); news.setTitle("aaaaa"); System.out.println(news.getDesc()); System.out.println(news.getDate()); }
/**
* 二进制大数据的处理
*/ @Test public void testBlob() throws Exception { // News news = new News(); // news.setAuthor("cc"); // news.setContent("CONTENT"); // news.setDate(new Date(11111111111L)); // news.setDesc("DESC"); // news.setTitle("CC"); // // InputStream stream = new FileInputStream(this.getClass().getResource("/").getPath() + "/jvhua.jpg"); // Blob image = Hibernate.getLobCreator(session) // .createBlob(stream, stream.available()); // news.setImage(image); // session.save(news); News news2 = (News)session.get(News.class, 1); Blob image2 = news2.getImage(); InputStream inputStream = image2.getBinaryStream(); System.out.println(inputStream.available()); }
/**
* Component的相关使用:
* http://blog.csdn.net/itzyjr/article/details/8505789
* http://fehly.iteye.com/blog/636409
*/ @Test public void testComponent() { Worker worker = new Worker(); Pay pay = new Pay(); pay.setMonthlyPay(1000); pay.setYearlyPay(50000); pay.setVocationWithPay(55); worker.setName("abcd"); worker.setPay(pay); // session.save(worker); Worker worker2 = (Worker)session.get(Worker.class, 1); System.out.println("parent work name: " + worker2.getPay().getWorker().getName()); } }
相应的映射文件:
Work.hbm.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2017-7-22 11:36:52 by Hibernate Tools 3.5.0.Final --> <hibernate-mapping package="hibernate.helloworld"> <class name="Worker" table="WORKER"> <id name="id" type="int"> <column name="ID" /> <generator class="native" /> </id> <property name="name" type="java.lang.String"> <column name="NAME" /> </property> <!-- 映射组成关系 --> <component name="pay" class="Pay"> <parent name="worker"/> <!-- 指定组成关系的组件的属性 --> <property name="monthlyPay" column="MONTHLY_PAY"></property> <property name="yearlyPay" column="YEARLY_PAY"></property> <property name="vocationWithPay" column="VOCATION_WITH_PAY"></property> </component> </class> </hibernate-mapping>
有一个component
News.hbm.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2017-7-17 12:21:26 by Hibernate Tools 3.5.0.Final --> <hibernate-mapping package="hibernate.helloworld">
<!-- 动态插入,动态更新,更新前先选择 --> <class name="News" table="NEWS" dynamic-insert="true" dynamic-update="true" select-before-update="false"> <id name="id" type="java.lang.Integer" unsaved-value="11"> <column name="ID" /> <!-- 指定主键的生成方式,native:使用数据库本地的方式 increment hilo sequence identity --> <generator class="native" /> </id>
<!-- 唯一性,不可更新 --> <property name="title" type="java.lang.String" unique="true" update="false" index="news_index"> <column name="TITLE" length="10" /> </property> <property name="author" type="java.lang.String" index="news_index"> <column name="AUTHOR" /> </property> <property name="date" type="date"> <column name="DATE" /> </property> <!-- 映射派生属性 --> <property name="desc" formula="(SELECT concat(author, ': ', title) FROM NEWS N WHERE n.id = id)"></property>
<!-- 大文本 --> <property name="content"> <column name="CONTENT" sql-type="mediumtext"></column> </property>
<!-- 二进制图片 --> <property name="image" type="blob"> <column name="IMAGE" sql-type="mediumblob"></column> </property> </class> </hibernate-mapping>
有不少新东西,可以看一下
内容太多了,就先讲到这里吧,其它的放到下一篇中继续说。