随笔 - 40  文章 - 4  评论 - 1  阅读 - 25615

Hibernate学习笔记

Hibernate

一、hibernate是什么

  1. 一种框架,一种orm框架
    (objext relation mapping)对象关系映射框架。
  2. hibernate处于项目的持久层位置,又叫持久化框架。
  3. hibernate实际上是对jdbc进行轻量级的封装。

二、需求

  1. 切换数据库需要重写编写sql。
  2. 使用jdbc操作数据库语句编写比较麻烦。
  3. 让程序员只关注业务逻辑,不再关心数据库。

三、快速入门案例

使用手动配置hibernate方式开发一个hibernate项目,完成相关操作。

开发流程

  1. 创建一个项目
  2. 画出简单的项目框架图
  3. 引入Hibernate包
  4. 开发Hibernate三种方式
  • 由Domain object --> mapping --> db。 (官方推荐)
  • 由DB开始,用工具生成Mapping的Domain object。(使用较多)
  • 由映射文件开始。

我们使用第二种方式
首先在hibernate数据库下创建student表

create table student(
id int primary key,
name varchar(20) not null,
age varchar(20) not null,
);

5.开发domain对象 和对象关系映射
对象关系映射文件,用于指定domain对象和表的映射关系,该文件的取名有规范,domain对象hbm.xml,一般和domain对象同一包下。

<?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE hibernate-mapping PUBLIC
     "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
     "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
  
 <hibernate-mapping package="com.sun.hibernate.model">
     <class name="Student">
         <id name="id"></id>
         <property name="name"></property>
         <property name="age"></property>
     </class>
 </hibernate-mapping>

6.手动配置hibernate.cfg.xml文件,该文件用于配置连接的数据库类型,diver,用户名,密码....同时管理对象关系映射文件和该文件的名称,这个文件一般放在src目录下。

<?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>
    
   <!-- Database connection settings -->
   <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
   <property name="connection.url">jdbc:mysql://localhost/hibernate</property>
   <property name="connection.username">root</property>
   <property name="connection.password"></property>
        
   <!-- JDBC connection pool (use the built-in) -->
   <!-- <property name="connection.pool_size">1</property> -->
        
   <!-- SQL dialect -->
   <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
        
   <!-- Echo all executed SQL to stdout -->
   <property name="show_sql">true</property>
        
   <!-- Enable Hibernate's automatic session context management -->
   <!--<property name="current_session_context_class">thread</property>-->
        
   <!-- Drop and re-create the database schema on startup -->
   <!-- <property name="hbm2ddl.auto">create</property> -->
        
   <!-- Disable the second-level cache -->
   <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
    
   <mapping resource="com/sun/hibernate/model/Student.hbm.xml"/>
      
     </session-factory>
 </hibernate-configuration>

7. 创建对应的student模型

package com.sun.hibernate.model;

public class Student {
	private int id;
	private String name;
	private int age;
	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;
	}
	

}

8.测试方法(向数据库中添加一条数据)

package com.sun.hibernate.model;


import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.classic.Session;

public class StudentTest {
	public static void main(String[] args){
		Student s = new Student();
		s.setId(1);
		s.setName("s1");
		s.setAge(2);
		
		//1.创建Configuration ,该对象用于读取hibernate.cfg.xml,并完成初始化
		Configuration  cfg = new Configuration();
		//2.创建SessionFactory<这是一个会话工厂,是一个重量级对象>
		SessionFactory sf = cfg.configure().buildSessionFactory();
		//3.创建一个session,相当于jdbc Connection<不是jsp中的那个session>
		Session session = sf.openSession();
		//4.对hibernate而言,要求程序员在进行增加,删除,修改时必须使用事物提交
		session.beginTransaction();
		session.save(s);	//insert into ...  <sql语句被hibernate封装了>
		session.getTransaction().commit();
		session.close();
		sf.close();
	}
}

直接运行StudentTest就可以将数据插入到数据库中了。

四、 hibernate缓存原理

详细信息

1.session缓存(一级缓存)

Session内置不能被卸载,Session的缓存是事务范围的缓存(Session对象的生命周期通常对应一个数据库事务或者一个应用事务)。
一级缓存中,持久化类的每个实例都具有唯一的OID。

2.二级缓存

第二级缓存是可选的,是一个可配置的插件,默认下SessionFactory不会启用这个插件。
Hibernate提供了org.hibernate.cache.CacheProvider接口,它充当缓存插件与Hibernate之间的适配器。

什么样的数据适合存放到第二级缓存中?   

  1. 很少被修改的数据   
  2. 不是很重要的数据,允许出现偶尔并发的数据   
  3. 不会被并发访问的数据   
  4. 常量数据   
    不适合存放到第二级缓存的数据?   
  5. 经常被修改的数据   
  6. 绝对不允许出现并发访问的数据,如财务数据,绝对不允许出现并发   
  7. 与其他应用共享的数据。

Hibernate查找对象如何应用缓存?
当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;
查不到,如果配置了二级缓存,那么从二级缓存中查;
如果都查不到,再查询数据库,把结果按照ID放入到缓存删除、更新、增加数据的时候,同时更新缓存。

五、 Query接口

通过Query接口我们可以完成更加复杂的查询任务

快速入门

/*获取query应用【这里student不是表,而是domain】
where后面的条件可以是一个类的属性名,也可以是表的字段,按照hibernate规定,我们还是应该使用类的属性名*/
	Query query = session.createQuery("from Teacher  where id = 2");
	//通过list方法获取结果,这个list会自动将封装成对应的dommain
	//所以我们jdbc进行二次封装的工作没有
	List<Teacher> list = query.list();
		for(Teacher tea: list){
			System.out.println(tea.getName() + "  " + tea.getAge());
		}

六、 criteria接口简单使用

快速入门

Criteria cri = session.createCriteria(Teacher.class).setMaxResults(4);
			List<Teacher> list = cri.list();
			for(Teacher tea:list){
				System.out.println(tea.getId() + "  " + tea.getName());
			}

七、HQL

(hibernate query language)

1.需求

在现有的只是基础上,对对象的批量删除,修改,查询还不能很方便的实现。

模拟创建表

2.list

查询所有对象

List<Student> list =  session.createQuery("from Student").list();

3.uniqueResult

如果检索一个对象,明确知道最多只有一个,则建议使用该方法。
具体用法:

Student stu =  (Student) session.createQuery(" from Student where sid = 20040001").uniqueResult();

4.HQL常见用法

distinct的用法:(用于过滤重复记录)

List<Object[]> list =  (List<Object[]>)session.createQuery(" select distinct ssex,sage from Student").list();

between...and的用法

List<Object[]> list =  (List<Object[]>)session.createQuery(" select distinct sname,ssex,sage from Student where sage between 22 and 24").list();

in 和 not in的用法

List<Student> list = session.createQuery("from Student where sdept in ('数学系','计算机系')").list();

group 和 having 和 order by
group用法:(查询各个系的学生的平均年龄)

List<Object[]> list = session.createQuery("select avg(sage),sdept from Student group by sdept").list();

having的使用(显示人数大于等于2的系)

List<Object[]> list = session.createQuery("select count(*),sdept from Student group by sdept having count(*) >= 2").list();

(查询各个系女生的人数)

List<Object[]> list = session.createQuery("select count(*),sdept from Student where ssex='M' group by sdept").list();

order by用法(查询所有学生的成绩拼按照成绩高低排序)

List<Object[]> list = session.createQuery("select student.sname,course.cname,grade from Studcourse order by grade DESC").list();

查询计算机系共有多少学生
如果返回一列数据,取出数据是必须用Object,而不是Object[]

List<Object[]> list = session.createQuery("select count(*) from Student where sdept='计算机系'").list();

查询总成绩是多少

List<Object[]> list = session.createQuery("select sum(grade) from Studcourse").list();

查询课程号为1001的课程名称,最高分和最低分

List<Object[]> list = session.createQuery("select course.cname,min(grade),max(grade) from Studcourse where course.cid=1011").list();

查询各科大于80分的学生的名字,科目,分数

List<Object[]> list =session.createQuery("select student.sname,course.cname,grade from Studcourse where grade > 80").list();

计算各科大于80分的的学生数量

List<Object[]> list = session.createQuery("select course.cname,count(*) from Studcourse where grade>80 group by course.cid").list();

5.HQL分页技术

查询所有学生成绩进行高低排序并只分页显示。

//获取页数
int pageCount = Integer.parseInt(session.createQuery("select count(*) from Studcourse").uniqueResult().toString()) /3 +1;
System.out.println("一共有"+pageCount+"页");
//开始查询
for(int i = 0; i < pageCount; i++){
	List<Object[]> list = session.createQuery("select student.sname,course.cname,grade from Studcourse order by grade DESC").
			setFirstResult(i*3).setMaxResults(3).list();
	for(int j = 0; j < list.size(); j ++) {
		Object[] obj = list.get(j);
		System.out.println(obj[0].toString() + "  " + obj[1].toString() + " " + obj[2].toString());
	}
	System.out.println("*******第"+(i+1)+"页**********");
}

6.参数绑定

好处:
1.可读性高
2.效果好
3.防止sql注入漏洞

一般写法:

List<Student> list = session.createQuery("from Student where sage = 22 and sname = '张三'").list();

参数绑定写法:
如果我们的参数是冒号形式给出的,则可以这样写:

List<Student> list = session.createQuery("from Student where sage=:sage andsname=:sname").setString("sage", "22").setString("sname", "张三").list();

如果我们的参数是问号形式给出的,则可以这样写:

List<Student> list = session.createQuery("from Student where sage=? and sname=?").setString(0, "22").setString(1, "张三").list();

将绑定分开写:

Query query = session.createQuery("from Student where sage=? and sname=?");
query.setInteger(0, 22);
query.setString(1, "张三");
List<Student> list = query.list();

映射文件中得到hql语句

hibernate提供了一种更加灵活的查询方法。
把hql语句配置到对象关系映射文件

<query name="myquerytest">
    from Student
</query>

八、hibernate对象的三种关系映射

  1. one-to-one: 身份证<---->人 丈夫<--->妻子
  2. one-to-many: 部门<--->员工
  3. many-to-one:员工<--->部门
  4. many-to-many:学生<--->老师(尽量避免)
    (在实际开发过程中,如果出现了多对多的关系,我们应该尽量装换为两个一对多或者多对一的关系,这样程序好控制,同时不会有冗余)

1.one-to-one

一对一两种方式:
(1)基于主键的一对一
(人<--->身份证 one<--->one)

//Person.java,略去了相关的get/set方法
private Integer id;
private String name;
private IdCard idCard;
//Person.hbm.xml
<hibernate-mapping package="com.sun.hibernate.model">
  <class name="Person">
	<id name="id" type="java.lang.Integer">
		<!-- 我们手动分配id -->
		<generator class="assigned" />
	</id>
	<property name="name" type="java.lang.String">
		<column name="name" length="128" />
	</property>
	<!-- 这里配置person和idcard属性是一对一关系 -->
	<one-to-one name="idCard"></one-to-one>
</class>
</hibernate-mapping>
//IdCard.java,略去了相关的get/set方法
private Integer id;
private Date validata;
private Person person;
//IdCard.hbm.xml
<hibernate-mapping package="com.sun.hibernate.model">
  <class name="IdCard">
	<id name="id" type="java.lang.Integer">
		<!-- 因为我们这里是基于主键的one-to-one, 所以我们使用外键策略 -->
		<generator class="foreign">
			<!-- 这里值,是指跟那个属性ont-to-one -->
			<param name="property">person</param>
		</generator>
	</id>
	<property name= "validata" type="java.util.Date">
		<column name="validata" />
	</property>
	<!-- constrained设置为true才会使得在数据提交的时候同时提交相关的关联关系,在此例中如果没有IdCard表将不会有外键-->
	<one-to-one name="person" constrained="true" />
</class>
</hibernate-mapping>

(2)基于外键的一对一
(人<--->身份证 one<--->one)

//Person.java,略去了相关的get/set方法(和基于主键的Person.java一致)
private Integer id;
private String name;
private IdCard idCard;
//Person.hbm.xml(和基于主键的Person.hbm.xml一致)
<hibernate-mapping package="com.model.one2one">
	<class name="Person">
		<id name="id" type="java.lang.Integer">
			<!-- 我们手动分配id -->
			<generator class="assigned" />
		</id>
		<property name="name" type="java.lang.String">
			<column name="name" length="128" />
		</property>
		<!-- 这里配置person和idcard属性是一对一关系 -->
		<one-to-one name="idCard"></one-to-one>
	</class>
</hibernate-mapping>
//IdCard.java,略去了相关的get/set方法(和基于主键的IdCard.java一致)
private Integer id;
private Date validate;
private Person person;
//IdCard.hbm.xml(注意与基于主键的IdCard.hbm.xml的区别)
<hibernate-mapping package="com.model.one2one">
	<class name="IdCard">
		<!-- 基于外键的one-one -->
		<id name="id" type="java.lang.Integer">
			<generator class="assigned" />
		</id>
		<property name="validate" type="java.util.Date">
			<column name="validate" />
		</property>
		<many-to-one name="person" unique="true" />
	</class>
</hibernate-mapping>

//测试函数
//添加一组Person/idcard
Session session = null;
Transaction ts = null;
try {
    session = MySessionFactory.getSessionFactory().openSession();
    ts = session.beginTransaction();
    Person p1 = new Person();
    p1.setId(100);
    p1.setName("小明");
    IdCard idCard = new IdCard();
    idCard.setId(2015001);//如果是基于主键的one-to-one关系则不用设置
    idCard.setValidate(new Date());
    idCard.setPerson(p1);
    session.save(p1);
    session.save(idCard);
    ts.commit();
} catch (Exception e) {
    e.printStackTrace();
}finally{
    if(session != null && session.isOpen()){
        session.close();
    }
}

2.one-to-many

配置文件示例
(学生<----->选课表 one<--->many)

//Student.hbm.xml配置文件
//Student.java定义Studcourse代码:private Set<Studcourse> studcourses = new HashSet<Studcourse>(0);
<set name="studcourses" inverse="true">
    <key>
        <column name="sid" />
    </key>
    <one-to-many class="com.domain.Studcourse" />
</set>
//Studcourse.hbm.xml配置文件
//Studcourse定义Student代码:private Student student;
<many-to-one name="student" class="com.domain.Student" fetch="select">
    <column name="sid" />
</many-to-one>

3.many-to-many

学生<--->课程
many-to-many在实际开发过程中,如果出现这种多对多的关系,我们应该尽量转换为两个一对多或者多对一的关系,这样程序好控制,同时不会有冗余。
所以学生<--->课程我们转换为学生<--->选课记录表(many<--->one) 选课记录表<--->课程(one<--->many)

学生表(student)

sid sname sdept
2001 李华 计科

选课表(studcourse)

idstudcourse sid cid grade
1 2001 A101 87

课程表(course)

cid cname ccredit
A101 java编程课 3
如上所示,我们使用选课表将学生<--->课程多对多的关系化解成了两个多对一的关系。

代码及配置

//Student.java
private Integer sid;
private String sname;
private String sdept;
private Set<Studcourse> studcourses = new HashSet<Studcourse>(0);
//Student.hbm.xml
<hibernate-mapping>
    <class name="com.domain.Student" table="student" catalog="course">
        <id name="sid" type="java.lang.Integer">
            <column name="sid" />
            <generator class="identity" />
        </id>
        <property name="sname" type="string">
            <column name="sname" length="20" not-null="true" />
        </property>
        <property name="sdept" type="string">
            <column name="sdept" length="10" not-null="true" />
        </property>
        <set name="studcourses" inverse="true">
            <key>
                <column name="sid" />
            </key>
            <one-to-many class="com.domain.Studcourse" />
        </set>
    </class>
</hibernate-mapping>

//Studcourse.java
private Integer idstudCourse;
private Course course;
private Student student;
private Integer grade;
<hibernate-mapping>
    <class name="com.domain.Studcourse" table="studcourse" catalog="course">
        <id name="idstudCourse" type="java.lang.Integer">
            <column name="idstudCourse" />
            <generator class="identity" />
        </id>
        <many-to-one name="course" class="com.domain.Course" fetch="select">
            <column name="cid" />
        </many-to-one>
        <many-to-one name="student" class="com.domain.Student" fetch="select">
            <column name="sid" />
        </many-to-one>
        <property name="grade" type="java.lang.Integer">
            <column name="grade" />
        </property>
    </class>
</hibernate-mapping>


Course.java
private Integer cid;
private String cname;
private Integer ccredit;
private Set<Studcourse> studcourses = new HashSet<Studcourse>(0);
//Course.hbm.xml
<hibernate-mapping>
    <class name="com.domain.Course" table="course" catalog="course">
        <id name="cid" type="java.lang.Integer">
            <column name="cid" />
            <generator class="identity" />
        </id>
        <property name="cname" type="string">
            <column name="cname" length="40" not-null="true" />
        </property>
        <property name="ccredit" type="java.lang.Integer">
            <column name="ccredit" />
        </property>
        <set name="studcourses" inverse="true">
            <key>
                <column name="cid" />
            </key>
            <one-to-many class="com.domain.Studcourse" />
        </set>
    </class>
</hibernate-mapping>
//Test.java
Student stu = new Student();	
stu.setSname("王宝强");
stu.setSsex("M");
stu.setSdept("表演系");
stu.setSage(32);
stu.setSaddress("河南");


Course course = new Course();
course.setCid(1016);
course.setCname("asp.net");
course.setCcredit(4);

Studcourse stucourse = new Studcourse();
stucourse.setIdstudCourse(20009);
stucourse.setStudent(stu);
stucourse.setCourse(course);
stucourse.setGrade(79);

Session session = null;
Transaction ts = null;
try {
	session = HibernateUtil.openSession();
	ts = session.beginTransaction();
	
	session.save(course);
	
	session.save(stu);	
	
	session.save(stucourse);
	
	ts.commit();
} catch (Exception e) {
	if(ts != null){
		ts.rollback();
	}
	throw new RuntimeException(e.getMessage());
}finally{
	//关闭session
	if(session != null && session.isOpen()){
		session.close();
	}
}

九、 对象的三种状态

1. 瞬时状态(transient)

数据库中没有数据与之对应,超过作用域会被JVM垃圾回收器回收,一般是new出来且且与session没有关联的对象。

2.持久状态(persitent)

数据库中有数据与之对应,与当前session有关联,并且相关联的session没有关闭,事物没有提交;持久对象状态发生改变,在事务提交时会影响数据库(hibernate能检测得到)

3.托管状态/游离状态(datached)

数据库中有数据与之对应,但当前没有session与之关联;脱管状态发生改变时,hibernate不能检测到。

Student stu = new Student();	//stu就是瞬时状态
		stu.setSname("王宝强");
		stu.setSsex("M");
		stu.setSdept("表演系");
		stu.setSage(32);
		stu.setSaddress("河南");
		
		Session session = null;
		Transaction ts = null;
		try {
			session = HibernateUtil.openSession();
			ts = session.beginTransaction();
			
			session.save(stu);	
			//stu既处于session管理下,
			//同时stu被保存到数据库中,因此stu此时是持久态
			stu.setSname("唐国强");//hibernate能检测到,并且会提交到数据库中去
			ts.commit();
			session.close();
			//这是stu被保存到数据库中,没有处于session的管理之下
			//此时stu就是脱管状态(游离态)
			
		} catch (Exception e) {
			if(ts != null){
				ts.rollback();
			}
			throw new RuntimeException(e.getMessage());
		}finally{
		}

十、级联操作

所谓级联操作就是说,当你进行某个操作(添加、修改、删除),就有hibernate自动给你完成。
比如删除一个学生那么这个学生相应的选课表也要被删除。
配置(配置cascade对应的属性就可以实现相应的级联操作)cascade:create,merge,save-update,delete,lock,refresh,evict,replicate;默认值为none,也可以设置为all
级联操作一般在one-to-one和one-to-many中比较有用,在many-to-one和many-to-many中没有什么意义。

<set name="studcourses" inverse="true" cascade="save-update">

十一、 其他

1. 什么是pojo类,他有什么要求?

  • pojo类和一张表对应
  • 一般放在com.XXX.domain下
  • pojo需要一个主键属性(用于标示一个pojo对象)
  • 除了主键属性,它还应当有其他属性,属性的访问权限为private
  • 提供get/set方法
  • 它应当有一个无参构造方法(hibernate反射)
  • pojo类其实就是一个javaBean(有时也叫Data对象)

2. hibernate核心类和接口
1. configuration类
2. 读取配置文件
3. 管理关系映射文件
4. 加载hibernate的驱动,url,用户
5. 管理hibernate配置信息

3. hibernate.cfg.xml
4. 对象关系映射文件
5. SessionFactory接口(会话工厂)
1. 可以缓存sql语句和数据(称为session级缓存)
2. 是一个重量级的类,因此需要保证一个数据库有一个SessionFactory
6. 通过SessionFactory获取Session的两个方法
1. openSession()是获取一个新的Session
2. getCurrentSession()获取和当前绑定的session,换言之,在同一个线程中,我们获取的session是同一个session。(如果希望使用getCurrentSession需要配置hibernate.cfg.xml)

<property name="current_session_context_class">thread</property>

3.如何选择
如果在同一个线程中,保证使用同一个session,则使用getCurrentSession(),如果在同一个线程中需要使用不同的Session,则使用opentSession()
4. openSession() 和 getCurrentSession()的区别
* 通过getCurrentSession获取的session在事务提交以后会自动关闭,通过openSession获取的session则必须手动关闭,但是我们建议不管什么形式获取的session都进行判断后手动关闭。
* 如果是通过getCurrentSession()获取session进行查询时,也要进行事务提交。

7. 本地事务:针对一个数据库的事物
全局事务:跨数据库的事物(jta)

8. session接口:
主要功能和作用
1. session一个实例代表与数据库的一次操作。(当然一次操作可以使crud组合)
2. session实例是通过SessionFactory获取,用完需要关闭。
3. session实例是线程不同步的(不安全),因此要保证在同一线程中使用,可以用getCurrentSession()
4.session可以看做是持久化管理器,它与持久化操作相关的接口。

get() 和load()区别
1. get()方法直接返回实体类,如果找不到数据则返回null。load()会返回一个实体代理对象(当前这个对象可以自动转化为实体对象)。但当代理对象被调用时,如果数据不存在,就会抛出org.hibernate.ObjectNotFoundException异常。
2. load先到缓存(session缓存/二级缓存)中去查,如果没有则返回一个代理对象(不马上到DB中去找),等后面使用这个代理对象操作的时候,才到DB中去查询,这就是我们常说的load在默认情况下支持延迟加载(lazy--->我们可以在配置文件中关闭懒加载功能).

懒加载

简述:当我们查询一个对象的时候,在默认情况下返回的只是该对象普通属性,当用户使用哪个对象属性是才会向数据库中发出一次查询,这种现象我们成为懒加载现象
禁用懒加载功能
方法一:(配置文件中进行禁用)

<class name="Student" lazy="false">

方法二:(显示初始化,Hibernate.initizlize(代理对象))

Hibernate.initlalize(stu.getDept());

方法三:(通过过滤器openSessionview解决)
3. get先到缓存(session缓存/二级缓存)中去查,如果没有就到DB中去查(即马上发出sql)。总之,如果你确定DB中有这个对象就用load(),不确定就用get()(这样效率高)

posted on   snail-lb  阅读(2648)  评论(0编辑  收藏  举报
< 2025年2月 >
26 27 28 29 30 31 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 1
2 3 4 5 6 7 8

点击右上角即可分享
微信分享提示