Hibernate
- 什么是hibernate
Hibernate是一个开放源代码的对象关系映射框架,他对JDBC进行了非常轻量级的对象封装,他对POJO(简单java对象)与数据库表建立映射关系, 是一个全自动的ORM框架,hibernate可以自动生成SQL语句,自动执行,使得java程序员可以随心所欲的使用对象编程思想来操作数据库,hibernate 可以应用在任何使用JDBC的场合,既可以在java的客户端程序使用,也可以在Servlet或者jsp的Web应用中使用
安装hibernate的jboss tools插件:打开eclipse---help----ecilpse Marketplace----搜索jboss tools----选择JBoss tools 4.5.3.Final----选择安装(安装的时间只 选择hibernate tools工具就可以了)------然后Confirm-----选择I accept terms of the licens agreements-----然后finsh
- 第一个hibernate程序
- 创建工程(maven工程)
- 导入hibernate的jar包,导入SQL的驱动包,导入junit测试包
|
<dependencies> <!-- hibernate的jar包 --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>5.2.17.Final</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.26</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> </dependency> </dependencies> |
- 创建hibernate的hibernate.cfg.xml核心配置文件,在resources目录下,名字默认就行,选择和导入jar包相对应的版本
|
<?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">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernateTest</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">root</property> <!-- 数据库方言,是否显示sql语句 --> <property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property> <!-- 是否显示sql语句 --> <property name="hibernate.show_sql">true</property> <!-- 是否格式化sql语句 --> <property name="hibernate.format_sql">true</property> <!-- create代表每一次都会创建表,并且把旧表删除,每次都会是一张新表 crate-drop代表每次hibernate加载的时间都会创建表,但是sessionFactory一关闭的话表就会自动删除 update是最常用的属性,第一次hibernate加载的时间会根据实体类自动的生成表,(前提是创建好了数据库)以后加载hibernate的时间根据实体类自动更新表结构 validate代表每次hibernate加载的时间,验证创建数据库表结构,只会和数据库的表进行比较,不会创建新表,但是会插入新值 --> <property name="hibernate.hbm2ddl.auto">update</property> <!-- 配置映射文件 ORM映射关系 --> <mapping resource="hibernate/test/Student.hbm.xml"/> </session-factory> </hibernate-configuration> |
- 创建实体类,属性名和数据库列名一一对应
|
package hibernate.test;
public class Student { private int id; private String name; private int age; private double height; public Student(String name, int age, double height) { super(); this.name = name; this.age = age; this.height = height; } public Student(int id, String name, int age, double height) { super(); this.id = id; this.name = name; this.age = age; this.height = height; } public Student() { super();
} public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public double getHeight() { return height; } public void setHeight(double height) { this.height = height; } @Override public String toString() { return "Student [id=" + id + ", name=" + name + ", age=" + age + ", height=" + height + "]"; }
} |
- 创建对象与关系映射文件(实体类.hbm.xml)放在实体类对应的包下面,一个实体类一个xxx.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 2018-7-21 14:57:03 by Hibernate Tools 3.5.0.Final --> <hibernate-mapping> <class name="hibernate.test.Student" table="STUDENT"> <id name="id" type="int"> <column name="ID" /> <!-- 主键的生成策略, 1.assigned:主键由外部程序负责生成,在save方法之前必须指定一个,hibernate不负责维护 主键生成,与hibernate和底层数据库都无关,可以跨数据库,在存储对象前,必须使用主键的 setter方法给主键赋值,至于这个值怎么生成,完全有自己决定,这种方法应该尽量避免 2..increment:由hibernate从数据库中取出主键的最大值(每个session只去一次)以该值 为基础,每次增量为1,在内存中生成主键,不依赖于底层的数据库,因此可以跨数据库(特点:跨数据库 不适合多进程并发更新数据库,适合单一进程访问数据库,不能用于集群环境) 3.hilo:高低位方式,是hibernate中最常用的一种生成方式,需要一张 额外的表保存hi的值,保存hi值得表至少有一条记录,负责会出现错误,可以跨数据库 4.seqhilo:与hilo类似,通过hilo算法实现主键的生成机制,只是将hilo中 数据表换成了序列sequence,需要数据库中先创建sequence,适用于支持sequence 的数据库,如Oracle 5.sequence:采用数据库提供的sequence机制生成主键,需要数据库支持sequence,如 Oracle,DB,SAP DB等,mysql这种不支持sequence的数据库侧不行 6.identity:identity由数据库生成标识符,identity是由数据库自己生成的,但是这个 主键必粗设置为自增长。使用identity的前提条件是底层数据库只采自动增长字段类型,如DB2, SQLserver,mysql等,Oracle这种没有自增字段的侧不支持 7.native:native由hibernate根据使用的数据库自行判断采用的增长,其中一种作为主键 的生成方式,灵活性很强,如果能支持identity测试用identity,如果支持sequence侧支持 使用sequence 8.uuid:hibernate在保存对象的时间,生成一个uuid字符创作为主键,保证了唯一性,但其 并无任何业务逻辑意义,智能作为主键,唯一缺点长度较大,32位,但是有两个很重要的优点,hibernate在 维护主键时,不用去数据库查询,从而提高效率,而且他是跨数据库的。以后切换数据库极其方便 9.guid:hibernate在维护主键时,先查询数据库,获得一个uuid字符串,该字符串就是主键 值,该值唯一,缺点长度会较大,支持数据库有限,优点同uuid,跨数据库,但是任然需要访问数据库 10.foreiqn:使用另外一个相关联的主键作为该对象主键,主要用于一对一的关系中 11.select:使用触发器生成主键,主要用于早起的数据库主键生成机制,能用到的地方非常少
--> <generator class="native" /> </id> <property name="name" type="java.lang.String"> <column name="NAME" /> </property> <property name="age" type="int"> <column name="AGE" /> </property> <property name="height" type="double"> <column name="HEIGHT" /> </property> </class> </hibernate-mapping>
|
- 测试
|
package hibernatefirsttest;
import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.boot.MetadataSources; import org.hibernate.cfg.Configuration; import org.hibernate.service.ServiceRegistry; import org.junit.Test;
import hibernate.test.Student; import hibernate.test.User;
public class JHibernateTest {
@Test public void test1() { //创建sessionFactory工厂类,通过他建立一个与数据库的了解对话session SessionFactory sf = null; Configuration cf = new Configuration().configure(); ServiceRegistry sr = cf.getStandardServiceRegistryBuilder().build(); sf = new MetadataSources(sr).buildMetadata().buildSessionFactory(); //通过工厂类开启session对象 Session session = sf.openSession(); //开启事务 Transaction ts = session.beginTransaction(); //执行数据库的操作 Student st = new Student("张三",21,1.77); session.save(st); //提交事务 ts.commit(); //关闭session session.close(); //关闭工厂类 sf.close(); } }
|
- hibernate中的session缓存接口
Session接口是hibernate向用用程序提供的操作数据库的最主要的接口,他提供了基本的保存,更新,删除和查询的方法,session有一个缓存,又叫 hibernate的一级缓存,session缓存是由一系列的java集合构成的,当一个对象呗加入到session缓存中,这个对象的引用就加入到了java的集合中, 以后即使应用程序中的引用变量不在引用该对象,之哟啊session缓存不被清空,这个对象一直处于生命周期中
Session缓存的作用
- 减少访问数据库的频率
- 保证缓存中的对象与数据库中的相关记录保持同步
Session清理缓存的机制
- 当调用Transaction的commit()方法时,commit方法先清理缓存(前提是FlushMode.COMMIT/AUTO),然后在向数据库提交事务
- 当应用程序调用session的find()或者iterate()等查询数据的方法时,如果缓存中的持久化对象的属性发生了变化,就会线清理缓存,以保证查询结果能反应持久化对象的最新状态
- 当应用程序显示调用session的flush()方法的时候,强制清除session的缓存
|
清理缓存的模式 |
Session的查询方法 |
Session的commit()方法 |
Session的Flush()方法 |
|
FlushMode.AUTO |
清理 |
清理 |
清理 |
|
FlushMode.COMMIT |
不清理 |
清理 |
清理 |
|
FlushMode.NEVER |
不清理 |
不清理 |
不清理 |
Hibernate处理的对象在不同的程序执行阶段存在不同的状态
Hibernate里面对象在整个操作中的所属状态注要有:临时,持久,游离
Hibernate三种状态图解
Hibernate的一级缓存
对于session缓存的总结:session有缓存,缓存里的对象不会随着引用变量消失而清空,只要缓存中已经存在对象,在查询的时间,hibernate会优先从缓存中取出数据,而不会在此访问数据库,只有缓存中不存在要查询的数据的时间,才会发起SQL语句查询数据库,所以缓存极大的减少了访问数据库的频率
|
package hibernatefirsttest;
import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.boot.MetadataSources; import org.hibernate.cfg.Configuration; import org.hibernate.service.ServiceRegistry; import org.junit.After; import org.junit.Before; import org.junit.Test;
import hibernate.test.Student;
public class JHibernateTest { SessionFactory sf = null; Session session = null; Configuration cf = null; Transaction ts = null; ServiceRegistry sr = null; //初始化执行的方法 @Before public void init() { //创建sessionFactory工厂类,通过他建立一个与数据库的了解对话session cf = new Configuration().configure(); sr = cf.getStandardServiceRegistryBuilder().build(); sf = new MetadataSources(sr).buildMetadata().buildSessionFactory(); //通过工厂类开启session对象 session = sf.openSession(); //开启事务 ts = session.beginTransaction(); } //在执行完对对象的一系列操作之后 @After public void destroy() { //提交事务 ts.commit(); //关闭session session.close(); //关闭工厂类 sf.close(); } @Test public void test() { //?useUnicode=true&characterEncoding=UTF-8--数据库设置编码格式集 //SHOW VARIABLES LIKE 'character_set_database';查看数据库编码格式 //get方法、load方法,通过id,获取数据库中的数据记录,转换为对象, //保存到session的缓存中 Student st = session.get(Student.class, 1); System.out.println(st); st = null; //注意,下面的查询不会再去数据库中去查询,而是去session色缓存当中直接拿数据 Student st1 = session.get(Student.class, 1); System.out.println(st1);
} |
Hibernate提供的一些让缓存中的数据和数据库数据保持一致的方法
- Flush()方法:强制让数据库里的数据记录和session中缓存的对象数据保存一致,不一致就发起update这条sql语句修改数据让其一致
Flush方法的特点:
a在事务的commit()提交之前自动调用了session的flush()方法,然后在提交事务
b flush()可能会发送sql语句,当不会提交事务
|
@Test public void test() { //保存到session的缓存中 Student st = session.get(Student.class, 1); System.out.println(st); st = null; //注意,下面的查询不会再去数据库中去查询,而是去session色缓存当中直接拿数据 Student st1 = session.get(Student.class, 1); st1.setName("nnnr"); //判断数据库中的数据和缓存中的数据是否一致,如果不一致,就发起修改的sql语句 session.flush();//可以省略 } |
- refresh()方法:他会强制发出一条select语句,保证session缓存中的数据和数据库里数据记录是一致的,如果发现不一致它会修改缓存中的对象的数据让其一致
|
@Test public void test() { Student st1 = session.get(Student.class, 1); st1.setName("8888"); System.out.println(st1); //修改的是session里面缓存的数据,保证和数据库同步 session.refresh(st1); System.out.println(st1); } |
Flush()方法和refresh()方法的区别:
Flush()是发起sql语句修改数据库,使数据库的数据和缓存当中的数据保持一致
Refresh()是不修改数据库的数据,它修改的是缓存当中的数据,让其缓存中的数据和数据库保持一致
清空缓存里面的数据-----session.clear()方法
|
@Test public void test() { Student st1 = session.get(Student.class, 1); System.out.println(st1); session.clear();//清空缓存里面的数据 Student st2 = session.get(Student.class, 1); System.out.println(st2); } |
Hibernate对象的状态及session的核心方法
- 临时状态:当创建出一个对象的时间,这个对象就是临时的状态,这个时候并没有被session进行托管,即在session的缓存中还不存在这个对象,当执行完save()方法的时间,此时这个对象被session托管,且数据库中也存在了该对象,那么这个对象就变成了持久化对象
|
@Test public void test1() { Student st1 = new Student("hhhh",21,1.89); session.save(st1);//这时候,student对象就变成了持久化对象 System.out.println(st1); } |
- 持久状态:持久状态就是已经被session所管理这个对象,当commit()提交的时间,会把session中的对象和目前的对象进行比较,如果两个对象中的值不一致就会继续发出相应的sql语句,使之数据库中的对象和session缓存中的对象保持一致
持久状态测试一:
|
@Test public void test1() { Student st1 = new Student("hhhh",21,1.89); session.save(st1);//这时候,student对象就变成了持久化对象 /** * 当事务提交的时间,hibernate会判断session缓存中的数据和数据库中的数据是否一致 * 如果带护具不一致,就会发起修改语句,(它只会判断持久化对象) * 当执行st1.setName("333");的时间会发起修改的sql语句 */ st1.setName("333"); } |
持久化状态测试2:
|
@Test public void test() { Student st1 = session.get(Student.class, 1); st1.setName("qq"); //当提交事务的时间,如果发现session的缓存中没有数据,则不会发起任何的sql语句 session.clear(); } |
- Session的游离状态的测试:就是在没有执行save()方法之前,已经修改了这个对象得我一些属性,但是并没有提交事务,那么这个时候这个对象就是游离状态
|
@Test public void test2() { Student st1 = new Student("qwe",32,1.67); //这个时间这个student就是游离状态 st1.setName("ppp"); st1.setId(1); /**当调用update,并且事务提交的时间,这个student对象就会变为持久状态, * 并且会将id为1的这条数据的name属性修改为ppp * */ session.update(st1); } |
Session的缓存当中是不允许存在两个相同的对象,否则会报错(NonUniqueObjectException)---对象不是唯一的异常
Session中的核心方法梳理:
- save()方法:就是将临对象转换为持久对象,会把数据添加到数据库,会给这个对象分配oid值(也就是数据库中的id)
- Persist()方法:这个方法基本和save()方法差不多,唯一的区别就是在这个方法之前也不可以设置对象的 OID,否则不会执行插入操作,而是会抛出异常
- Get/load()方法:get()方法是从数据库中获取一条数据记录转换成对象放到session缓存中,load()方法也是这个功能,
Get和load二者的区别:
1.查询时间的区别
[get()方法无论如何都会立即加载去执行查询(立即加载),而load()方法是在当你需要使用这条数据的时间才会去真正的查询(延迟加载)]
Get()方法如果查询的数据在数据库中没有对应的值,返回的是null
Load()方法如果查询的数据在数据库中没有相对应的值,则直接报错(代理对象的本质)
2.当获取类名的时间的区别
[get()方法返回的指就是Student类的对象本身,而load()方法返回值不是类本身对象,而是student类的代理]
代码测试示例:
|
@Test public void testget() { Student st =session.get(Student.class, 1); //System.out.println(st); System.out.println(st.getClass().getName()); }
@Test public void testload() { Student st = session.load(Student.class, 1); //System.out.println(st); System.out.println(st.getClass().getName()); } |
- .update()方法:
- 这个方法顾明思议就是更新一个对象在数据库中的对照情况,从而使一个游离对象转换为一个持久化对象
- 若是更新一个持久化对象,不需要在显示的进行update()方法,因为在commit()方法中已经进行flush()了,他会自动发起update语句
- 若是关闭了一个session,而又打开了一个session,这是,前一个session对象相对于第二个session对象来说就是游离状态了,此时,做更新的时间,必须显示的用第二个session进行update一下才可以将这个对象变成相对于第二个session的持久化对象,才会发起sql语句
- 需要注意的是,此时在更新游离对象时无论java对象中的内容和数据库中记录是否一样都会发送update语句,若是在数据库中将update语句和某个触发器绑定在了一起,那么就会造成触发器的错误触发,而我们在更新持久化对象的hibernate会验证一下,若是java对象和数据库中对应的记录一致的话就不会发送update语句,要想避免这种在更新游离对象是多发update语句的情况,可以在hbm.xml文件的class节点设置一个属性叫做select-before-update为true,就可以避免了,通常我们不需要设置这个属性,除非多发送update语句触发触发器二者相关联使用
- 如表中没有于java对象对应的记录,则会抛出异常
- 在update语句之前用get方法获取同一个id的数据记录,update会同时将两个相同的对象网session缓存里面放,那么这种情况下会抛出异常
- saveOrUpdate方法:这个方法同时包含了前边的save和update的功能。当对象是临时的,那么执行save方法,当对象是游离的,执行update方法
- Deltet方法:顾明思议,这个发方法就是用来删除游离的或者持久化的对象及其在数据库中对应的记录
|
@Test public void testDelete() { Student st = session.get(Student.class, 2); System.out.println(st); session.delete(st); System.out.println(st); } 如果想要删除对象存在内存中的ID,就要在hibernate的核心配置文件中配置: <!-- 会删除内存中存在的这个对象的id --> <property name="hibernate.use_identifier_rollback">true</property> |
- evict()方法:这个方法就是将持久化对象从session缓存中删除,使其成为一个游离状态的对象
- doWork()方法:这个方法是在hibernate中拿到jdbc的原生的connection
|
@Test public void testDoWork() { session.doWork(new Work() { public void execute(Connection connection) throws SQLException { System.out.println(connection); } }); } |
Hibernate配置文件的扩展
在企业级应用中,都必须使用一种数据库连接池,这里使用c3p0演示hibernate中的配置
- 导入jar包---hibernate的c3p0的jar包
|
<!-- hibernate和c3p0的关联的jar包--> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-c3p0</artifactId> <version>5.3.0.Final</version> </dependency> |
- 配置c3p0----配置DataSource的:hibernate.connection.provider_class
|
<!-- c3p0数据连接池的配置 --> <property name="hibernate.connection.provider_class">org.hibernate.c3p0.internal.C3P0ConnectionProvider</property> <!-- 当连接池的数量用完的时间,hibernate一次性向数据库拿出多少条数据 --> <property name="hibernate.c3p0.acquire_increment">20</property> <!-- 每格多长时间检查判断是不是把该连接池干掉 --> <property name="hibernate.c3p0.idle_test_period">2000</property> <!-- 指定最大的连接数量 --> <property name="hibernate.c3p0.max_size">20</property> <!--指定连接池最大连接多少个statement对象 --> <property name="hibernate.c3p0.max_statements">10</property> <!-- 指定最小的连接数量 --> <property name="hibernate.c3p0.min_size">5</property> <!-- 指定连接池里连接的超时时长 --> <property name="hibernate.c3p0.timeout">2000</property> <!-- 让hibernate访问数据的性能更加好,对于mysql不起作用--> <property name="hibernate.jdbc.batch_size">30</property> <property name="hibernate.jdbc.fetch_size">100</property> 测试C3P0是否配置成功 @Test public void testDoWork() { session.doWork(new Work() { public void execute(Connection connection) throws SQLException { System.out.println(connection); } }); } 注:如果在控制台能够输出C3P0相关的对象,就代表配置成功 |