Hibernate学习笔记(一)

来源:互联网 发布:5g网络什么时候上市 编辑:程序博客网 时间:2024/06/04 19:45

J2EE标准的JSP,Servlet,通过JDBC,直接写SQL语句,访问数据库。Hibernate以JDBC为基础,提供了访问数据库的功能。Java程序访问Hibernate的接口,Hibernate内部访问数据库。这样Java程序不再直接操作数据库。

J2EE的程序结构为Java业务逻辑层和数据库层,用了Hibernate后,程序结构变为Java业务逻辑层,持久层(Hibernate)和数据库层。增加了持久层。

使用Hibernate开发程序,主要包括下面的处理:
1. 连接数据库和建立会话工厂
2. 建立表和持久类的映射
3. 利用持久类,进行数据操作(如:查询,增,删,改)

1. 连接数据库和建立会话工厂

在hibernate.cfg.xml中,配置数据库连接信息,包括driver class, URL, username, password。同时,包括表和持久类的映射文件。

也可以在hibernate.properties中,配置数据库连接信息。

如果hibernate.cfg.xml和hibernate.properties中,都配置了数据库连接信息,hibernate.cfg.xml的配置会覆盖hibernate.properties的配置,也就是hibernate.cfg.xml的配置是优先的。

连接数据库和建立会话工厂的关键代码如下:

Configuration cfg = new Configuration().configure();
sessionFactory = cfg.buildSessionFactory(
 new StandardServiceRegistryBuilder().applySettings(cfg.getProperties())
 .build()
);

第一行代码,会自动寻找hibernate.cfg.xml和hibernate.properties文件,读入配置信息。第二行代码,会创建会话工厂。从系统的提示信息看,创建会话工厂的时候,hibernaten内部创建了数据库连接池。

创建会话工厂花费的时间长,大约几秒钟时间。创建会话工厂是Hibernate的初始化,整个应用只需要创建一次会话工厂。会话工厂可以提供一个一个数据操作的Session,类似于数据库连接。需要进行数据操作时,从会话工厂取得一个Session就可以了。

取得一个Session的代码如下:

Session session = sessionFactory.openSession();

2. 建立表和持久类的映射

数据库中,建立表,例如:

(MySQL数据库)
create table tb_student (
 id int(10) auto_increment primary key,
 name varchar(50),
 age int(3)
);

定义一个持久类,Student.java

public class Student {
 private Integer id;
 private String name;
 private Integer age;

 public Integer getId() {
  return id;
 }
 public void setId(Integer id) {
  this.id = id;
 }
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 
 public Integer getAge() {
  return age;
 }
 public void setAge(Integer age) {
  this.age = age;
 }
}

定义映射文件Student.hbm.xml,建立持久类和表的映射。

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
 <class name="com.wgh.model.Student" table="tb_student">
  <id name="id">
   <generator class="native"/>
  </id>
  <property name="name"/>
  <property name="age"/>
 </class>
</hibernate-mapping>

持久类里,成员变量和表中相应的字段,类型必须匹配。如,表中varchar类型的字段,持久类里,是String类型,表中int类型的字段,持久类里是Integer类型。如果类型不匹配,程序执行时,会抛出异常。

3. 利用持久类,进行数据操作(如:查询,增,删,改)

3.1 查询数据

查询Student的所有数据。

Session session = (从会话工厂中,取得一个Session);
Query q = session.createQuery("from Student");
List students = q.list();

用Session类的createQuery方法查询,查询的是Hibernate定义的Student持久类,不是Student数据库表。Hibernate内部自动通过映射文件找到Student数据库表,从数据库表中取数据。查询的过程是:Student持久类 -> 映射文件 -> Student数据库表。Java程序看到的是Student持久类,看不到映射文件和数据库表。

用Query类的list方法得到查询结果,是一个List对象中,对象里面保存的是一个一个Student对象。

查询一行数据。

Session session = (从会话工厂中,取得一个Session);
Student student = (Student)session.get(Student.class, new Integer(1));
System.out.println("ID:" + student.getId());
System.out.println("名字:" + student.getName());
System.out.println("年龄:" + student.getAge());

用Session的get方法,查询ID为1的学生的信息,查询结果保存在Student对象中。如果Student表中不存在ID为1的记录,Hibernate会抛出异常。这一点和JDBC不同。JDBC的ResultSet rs = statement.executeQuery("select id, name, age from student where id=1"),如果数据库表中没有数据,返回的rs是null,不会抛出异常。

也可以不用Session的get方法,用Session的load方法取数据。代码为:

Session session = (从会话工厂中,取得一个Session);
Student student = (Student)session.load(Student.class, new Integer(1));
System.out.println("ID:" + student.getId());
System.out.println("名字:" + student.getName());
System.out.println("年龄:" + student.getAge());

load方法,执行的时候,不从数据库中取数据,执行到student.getId(),引用数据的时候,才实际从数据库中取数据。get方法,执行的时候,从数据库中取数据。这是Hibernate的延迟加载技术。目的是如果不引用数据,那么不会从数据库中取数据,进而提高执行的效率。在删除数据时,适合用load方法,其他情况用到的可能性不大。

3.2 增加数据

Session session = (从会话工厂中,取得一个Session);
Student student = new Student();
student.setName("张三");
student.setAge(30);
session.save(student);

创建一个Student对象,然后设置名字,年龄。调用Session的save方法,把Student对象保存到数据库中。按照书上写的,在保存Student对象前,Student对象是瞬时状态(transient)。

3.3 删除数据

Session session = (从会话工厂中,取得一个Session);
Student student = (Student)session.load(Student.class, new Integer(1));
session.delete(student);

用Session的load方法指定一条记录(这时没有实际读入数据),再用Session的delete方法删除数据。

3.4 修改数据

Session session = (从会话工厂中,取得一个Session);
Student student = (Student)session.get(Student.class, new Integer(1));
student.setAge(35);

用Session的get方法取出一条记录,然后修改数据。student对象setAge方法执行后,修改后的数据会自动保存到数据库表中。student对象是用Session的get方法得到的,Hibernate内部会管理这个对象。当对象的数据发生变化时,Hibernate会把新的数据自动保存到数据库中。student对象和Hibernate的Session联系在一起,这时,这个对象处于持久状态(Persistent)。

3.5 事务处理

各种数据操作(查询,增,删,改)都应该有事务处理,事务处理的模板如下:

Session session = null;
try {
 session = (从会话工厂中,取得一个Session);
 session.beginTransaction();

 (数据操作)
 例如:
 Student student = (Student)session.get(Student.class, new Integer(1));
 student.setAge(35);
 
 session.getTransaction().commit();
} catch (Exception e) {
 e.printStackTrace();
 session.getTransaction().rollback();
}finally{
 (关闭session)
}

3.6 对象的状态

按书上写的,对象有三种状态:瞬时(transient),持久(persistent),托管(detached)。当对象处于Session管理之下时,对象是持久状态。当对象没有处于Session管理之下,如果Session存在,则对象是瞬时状态,如果Session不存在,则对象是托管状态。我觉得对象没有处于Session管理下时,对象和Session之间没有联系,相互是独立的,这时区分瞬时状态和托管状态不太合理。我觉得对象可以分为持久状态和非持久状态,如果对象处于Session管理下,则为持久状态,如果对象不处于Session管理下,则为非持久状态。

3.7 在Hibernate中直接用SQL语句

Query q = session.createSQLQuery("select id, name, age from tb_student");
List list = q.list();

使用Session类的createSQLQuery方法。list对象中保存查询结果。list对象中,保存一个一个数组,每个数组的内容是一个学生的ID,名字和年龄。

这时,不需要持久类,不需要持久类和数据库表的映射文件。

另外,Hibernate有缓存机制。第一次查询时,从数据库读入数据,这些数据会保存在缓存中,第二次查询同样的数据时,从缓存中读取,不访问数据库。Hibernate取数据时,会先看缓存中是否有数据,如果有,则用缓存中的数据,如果没有,则从数据库中读取。Hibernate的缓存有一级缓存(Session)和二级缓存(SessionFactory)。取数据时,会先看Session的缓存中是否有数据,如果有,则用Session缓存中的数据,如果没有,则再看SessionFactory缓存中是否有数据,如果有,则用SessionFactory缓存中的数据,如果没有,则读数据库。

书上是这么写的。缓存是Hibernate内部自动管理的,具体的管理机制不是很清楚。

使用缓存,存在缓存和数据库的数据一致性问题。我测试了一下数据一致性。

测试结果:

1. 读入表的所有数据
2. 手工删除/增加数据
3. 再读入表的所有数据 => 能读入更新后的数据

1. 读入表的所有数据
2. 手工更新数据 or 在Java中,用另一个Session更新数据
3. 再读入表的所有数据 => 看不到更新后的数据

在更新数据时,Session缓存中保存的是旧的数据。这时,出现了缓存和数据库数据不一致的情况。

如果需要考虑不同用户之间的数据一致性问题,可以在每次数据操作的时候,打开一个Session,这样可以避免使用Session的缓存。打开Session的操作,也就是SessionFactory的openSession方法,执行时间约为0.05毫秒(CPU 2GHZ, 4G 内存,Windows),insert,delete,update一条记录的操作大约10几毫秒,所以每次数据操作打开一个Session,计算机用的时间很少,不会怎么增加系统负担。

0 0
原创粉丝点击