一:简单使用hibernate
1.什么是hibernate
- 什么是数据持久层:关于三层架构中的业务逻辑层中分出的一个数据持久层,这个数据持久层用于对于对数据库来进行操作的。
- orm是什么: 全称叫做object relation mapping ,中文叫做对象关系映射,这是将数据库中的关系表等内容转换成对象的一种方式。这种转换成的对象叫做一般叫做domain对象(或者javabean,或者pojo)).
- hibernate所处在的地位: 就是在数据持久层,这个主要就是完成对jdbc的轻量封装,用来操作数据库,只不过,不需要了解底层数据库的种类(是sql servel还是mysql)和sql语言,就直接在业务层中写代码,就可以完成对数据库的操作。这种在业务层通过对象模型对数据库操作的语言叫做hibernate query language(hql).
2.使用hibernate做一个简单的插入数据
配置hibernate.hbm.xml文件:
<?xml version="1.0" encoding="UTF-8"?> <!-- 指定hibernate的标签库文件 --> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 5.3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.zzh.dbTest"> <class name="Student" table="Student"> <!-- table标签是对应数据库表的名称 --> <id name="id" column="id"> <!-- id标签用来申明持久化类标识属性,也就类似于主键 --> <generator class="native"></generator><!-- 指定标识属性的生成策略 --> </id> <property name="name" type="java.lang.String"> <column name="name" not-null="false"></column> </property><!-- property 使用来定义常规属性 --> <property name="age" type="java.lang.Integer"> <column name="age" not-null="false"></column> </property> <property name="createDate" type="java.util.Date"> <column name="createDate" not-null="false"></column> </property> </class> </hibernate-mapping>
配置hibernate.cfg.xml文件:
注意,这个文件如果是默认的话,就是configuration中不写路径,需要将文件写在项目根目录。<?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.dialect">org.hibernate.dialect.MySQLDialect</property> <!-- 数据库驱动 --> <property name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driver</property> <!-- 数据库连接信息 --> <property name="hibernate.connection.url"><![CDATA[jdbc:mysql://localhost:3306/school?characterEncoding=utf8&useSSL=true&serverTimezone=UTC]]></property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">yz1998</property> <!-- 打印SQL语句 --> <property name="hibernate.show_sql">false</property> <!-- 不格式化SQL语句 --> <property name="hibernate.format_sql">false</property> <!-- 为Session指定一个自定义策略 --> <property name="hibernate.current_session_context_class">thread</property> <!-- C3P0 JDBC连接池 --> <!-- 映射文件 --> <mapping resource="com/zzh/dbTest/hibernate.hbm.xml"/> </session-factory> </hibernate-configuration>
TestDao中的代码:
package com.zzh.dbTest; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import java.util.Date; public class TestDao { public static void main(String[] args) { Configuration configuration = new Configuration().configure();//这个配置文件如果不设置路径,默认找的是根目录下的hibernate.cfg.xml文件 //创建会话工厂 SessionFactory sessionFactory = configuration.buildSessionFactory(); //通过会话工厂创建会话 Session session =sessionFactory.openSession(); //进行增加,删改的时候必须使用事物进行提交 Transaction transaction =session.beginTransaction(); //添加一个学生 Student student = new Student(); student.setName("什么神奇"); student.setAge(20); student.setCreateDate(new Date()); session.save(student); transaction.commit(); } }
项目的文件图
3.操作持久层代码的详解
1.创建Configuration,改对象用于读取hibernate.cfg.xml,并完成初始化
Configuration configuration= new Configuration().configure("hibernate.cfg.xml");//这里可以指定路径文件,也可以不用,默认找的就是hibernate.cfg.xml
2.创建SessionFactory会话工厂,是一个重量级的对象
SessionFactory sessionFactory = configuration.bulidSessionFactory();
3.创建Session相当于jdbc connection
Session session = sessionFactory.openSession();
4.使用事物提交
Transaction transaction = session.beginTransaction();//要求程序员在增删改的时候使用事物提交
5.保存
session.save(employee);//这个会被hibernate封装
6.事物提交
transaction.commit();
6.关闭资源
session.close();
杂项
hibernate处在的是持久层的位置,当session.save()的时候,就是把对象保存到数据库中。
4.将SessionFactory封装成单例以及删除和修改的应用
将SessionFactory封装成单例
SessionFactory对象一旦通过Configuration对象创建,消耗资源是特别大的,为此,我们对一个数据库使用一个SessionFactory就够了。
下面来看代码package com.zzh.dbutil; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class MySessionFactory{ //使用单例模式来建立sessionFactory private static SessionFactory sessionFactory; private MySessionFactory(){ } static{ sessionFactory =new Configuration().configure().buildSessionFactory(); } public static SessionFactory getSessionFactory(){ return sessionFactory; } }
Student对象一定要序列化,我们在之前的代码中是没有序列化的,但是在这里修改或者删除某个记录的时候一定要序列化。
因为序列化之后,Student的对象就是唯一的了,正好对应某一条记录,不然很有可能出现多个对象对应一条记录的情况。准备来说,就是可以唯一的标识Student对象。
我们在Student
类中只需要加上public class Student implements Serializable
就好了。修改某条记录
先是通过主键来获得该对象实例,运用了发射机制,直接上代码import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import java.util.Date; import com.zzh.dbutil.MySessionFactory; public class TestDao { public static void main(String[] args) { //1.创建会话,因为会话工厂是很耗费资源的,所以我们采用单例模式来对一个数据库使用一个sessionFactory就够了 Session session = MySessionFactory.getSessionFactory().openSession(); //使用会话建立事物对象 Transaction transaction = session.beginTransaction(); //通过主键属性获取该对象实例 Student student = (Student)session.load(com.zzh.dbTest.Student.class,1); student.setName("什么神奇很帅"); transaction.commit(); } }
需要注意的问题是,这里直接
student.setName()
就可以了,hibernate将Pojo分成了4个不同的状态,后面会详细解释,当这个对象处于某个状态的时候,就会直接调用对应的代码。这里实际上已经做了Update操作了。删除
删除也需要注意一个问题,在使用student.load()
方法时,要知道,这就是将某条记录给加载给student对象。所以删除某条记录,其实就是要删除这个对象就好了。
所以- 获取某条记录然后变成Student对象
使用Session对象删除这条记录
public class TestDao { public static void main(String[] args) { Session session = MySessionFactory.getSessionFactory().openSession(); Transaction transaction = session.beginTransaction(); //1.先获取学生的某条记录 Student student = (Student)session.load(com.zzh.dbTest.Student.class, 2); //2.将这个记录删除 session.delete(student); transaction.commit(); } }
5.将hibernate配置另外一个数据库和hibernate自动创建新表
配置另外一个数据库
<!-- 数据库方言 --> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <!-- 数据库驱动 --> <property name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driver</property> <!-- 数据库连接信息 --> <property name="hibernate.connection.url"><![CDATA[jdbc:mysql://localhost:3306/school?characterEncoding=utf8&useSSL=true&serverTimezone=UTC]]></property>
只需要修改这上面的信息,这里面如何改都能在/project/etc/hibernate.properties文件中找到
hibernate自动创建表
这里需要注意mysql的版本号的问题,mysql5以上使用用如下方言,不然会很坑,所以,将方言改成如下形式
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
改变hibernate.cfg.xml里面的信息,添加
<!--自动创建表 --> <property name="hibernate.hbm2ddl.auto">create</property>
create
是无论什么情况下,都会创建过表,改成update的话,就会表示如果表存在且属性没有改变(表结构没啥改变),就不会再创建过表。杂项:
Configuration
是类,SessionFactory
是接口,Session
是接口,Transaction
是接口。(听说面试题会考?我特么的)- 总结:hibernate是一个位于持久层的将jdbc做了轻量级封装的orm框架。
- orm:全称object relation mapping,叫对象关系映射,orm框架通过对象-关系映射技术将dao层与业务层中间分出数据持久层,可以在业务层中直接操作数据持久层来完成对数据库的操作,hibernate就是担任数据持久层的位置。
6.回滚机制和创建crud操作的常用模板
- 为何要用?:因为怕在事物提交之前出现异常,进而导致运行终止和异常抛出数据却错误写入的问题所以使用者套模板来进行crud操作,需要用到回滚机制。
- 什么是回滚:hibernate中使用
Transaction
对象的rollback()方法,将数据回到之前没有修改的时候,就是回滚。 具体用法
public static void updateStudent2() { //关于回滚机制模板 //说明:MySessionFactory是我写的封装会话工厂的类,详细看上面 Session session= MySessionFactory.getSessionFactory().openSession(); Transaction transaction =null; try { transaction = session.beginTransaction(); Student student = (Student)session.load(Student.class, 1); student.setName("什么神奇真的是啊"); student.setAge(20); int a = 3/0;//此处抛了异常 transaction.commit(); } catch (Exception e) { if(transaction!=null){ //回滚 transaction.rollback(); } throw new RuntimeException(e.getMessage()); } finally{ if(session!=null&&session.isOpen()){ session.close(); } } }
session不关闭的后果:会导致jdbc连接不关闭,占用大量的数据库资源,导致内存被大量使用,所以,一定要关闭。
二:java hibernate中的核心类和接口
1.Configuration类
作用:
+ 1.加载hibernate.cfg.xml文件的配置信息
+ 2.负责管理hibernate的配置x信息
+ 3.加载hibernate.cfg.xml文件中的驱动,url,用户名,密码,连接池.
+ 4.管理*.hbm.xml文件
使用方法
Configuration configuration = new Configuration().configure(yourpath);
yourpath
中写的是hibernate.cfg.xml
配置文件所在的位置,如果不填写,默认就是在根目录下的hibernate.cfg.xml文件。当然,你也可以将这个文件改名字,并且放在另外一个包中。实验如下:
- 我新建了一个叫做
com.zzh.config
的包,并且将原来的hibernate.cfg.xml文件改名成zzhStudent.cfg.xml
文件名放入这个包中。如图:
还记得我们在
com.zzh.dbutil
包中写的MySessionFactory
类吗?这里面是用单例来获得sessionFactory的。我们先看看代码。package com.zzh.dbutil; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class MySessionFactory{ //使用单例模式来建立sessionFactory private static SessionFactory sessionFactory; private MySessionFactory(){ } static{ sessionFactory =new Configuration().configure().buildSessionFactory(); } public static SessionFactory getSessionFactory(){ return sessionFactory; } }
修改configure()配置路径。
package com.zzh.dbutil; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class MySessionFactory{ //使用单例模式来建立sessionFactory private static SessionFactory sessionFactory; private MySessionFactory(){ } static{ sessionFactory =new Configuration().configure("com/zzh/config/zzhStudent.cfg.xml").buildSessionFactory(); } public static SessionFactory getSessionFactory(){ return sessionFactory; } }
然后运行就Ok了。
- 我新建了一个叫做
2.SessionFactory接口
基础:
作用:
- sql语句和数据的缓存
- 因为SessionFactory占用的资源极大,所以每个数据库最好只配置一个SessionFactory
用法:
sessionFactory =new Configuration().configure("com/zzh/config/zzhStudent.cfg.xml").buildSessionFactory()
重点:通过SessionFactory获取session的两个方法(面试会出)
- openSession()
新建一个session会话,在查询的时候不需要事物提交且不需要配置hibernate.cfg.xml文件。 - getCurrentSession()
获得在本线程的session,(从web端访问到后台整个流程就是一个线程),这样多个控制器可以获得一个session来对不同的功能进行同一个事物提交。这是要配置xml文件的,并且在使用查询的时候一定要事物提交。 不同点详细说明
)getCurrentSession需要对.cfg.xml文件进行配置,这里面不只是可以用
thread
一种配置,详细看文档,这里的thread表示线程使用同一个session代码如下<!--getCurrentSession配置 --> <property name="current_session_context_class">thread</property>
)每次使用openSession获得的session都是不同的,而getCurrentSession是相同的,可以写一段代码来比较hashcode值:
package com.zzh.dbTest; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import java.util.Date; import com.zzh.dbutil.MySessionFactory; public class TestDao { public static void main(String[] args) { Session session = MySessionFactory.getSessionFactory().getCurrentSession(); Session session2 = MySessionFactory.getSessionFactory().getCurrentSession(); System.out.println(session.hashCode()+ " : " +session2.hashCode()); Session session3 = MySessionFactory.getSessionFactory().openSession(); Session session4 = MySessionFactory.getSessionFactory().openSession(); System.out.println(session3.hashCode()+" : "+session4.hashCode()); } }
输出结果:
356691811 : 356691811 248495761 : 552416003
不难发现,每次openSession()的时候都分配了一个新的session.顾而hashcode不同,但是getCurrentSession使用的都是改线程内的session.是相同的session,所以hashcode值相同。
)事物提交不同,在使用查询的时候,如果是openSession获得的session是不用在使用事物提交的(是在查询的时候!!),但是如果是getCurrentSession的话,是一定要进行事物提交的,我们来看下面的代码。
public class TestDao { public static void main(String[] args) { Session session = MySessionFactory.getSessionFactory().openSession(); Student student = (Student)session.load(Student.class, 1); String name = student.getName(); System.out.println(name); if(session!=null&&session.isOpen()) session.close(); }} //通过opensession方法获得的session能不用事物提交来查询 //输出 什么神奇
再来看看使用getCurrentSession().
Session session = MySessionFactory.getSessionFactory().getCurrentSession();
Exception in thread "main" org.hibernate.HibernateException: Calling method 'load' is not valid without an active transaction (Current status: NOT_ACTIVE) at org.hibernate.context.internal.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:346) at com.sun.proxy.$Proxy33.load(Unknown Source) at com.zzh.dbTest.TestDao.main(TestDao.java:13)
会抛出异常,我们在这里必须使用事物提交。代码如下:
public class TestDao { public static void main(String[] args) { Session session = MySessionFactory.getSessionFactory().getCurrentSession(); Transaction ts = session.beginTransaction(); Student student = (Student)session.load(Student.class, 1); String name = student.getName(); ts.commit(); System.out.println(name); if(session!=null&&session.isOpen()) session.close(); }}
运行成功,注意,ts.commit()得写到student.getName()的后面
)openSession获得的session是需要关闭的,否则大量占用资源,而getCurrentSession获得的session是不用手写关闭的,因为,它会自动关闭,但是我们一般就直接使用代码
if(session!=null&&session.isOpen()) session.close();
如果不加判断,使用getCurrentSession获得的session去关闭的话,会报错,因为,这里的session已经关闭了。
- )使用规则。我们在同一线程中需要不同session就使用openSession,使用相同session就使用getCurrentSession.
3.Session接口
主要功能和作用:
- Session一个实例代表与数据库一次操作(可以是crud组合)
- Session实例通过SessionFactory获取,用完需要关闭。
- Sessions是线程不同步的,因此要保证在同一线程中使用,可以用getCurrentSession().
- Session可以看做是持久化管理器,它是与持久化操作相关的接口。
点击此处浏览下面文章