hibernate从入门到精通

简介:

Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的J2EE架构中取代CMP,完成数据持久化的重任。

Hibernate的核心接口一共有6个,分别为:Session、SessionFactory、Transaction、Query、Criteria和Configuration。这6个核心接口在任何开发中都会用到。通过这些接口,不仅可以对持久化对象进行存取,还能够进行事务控制。下面对这6个核心接口分别加以介绍。

Session接口 Session接口负责执行被持久化对象的CRUD操作(CRUD的任务是完成与数据库的交流,包含了很多常见的SQL语句。)。但需要注意的是Session对象是非线程安全的。同时,Hibernate的session不同于JSP应用中的HttpSession。这里当使用session这个术语时,其实指的是Hibernate中的session,而以后会将HttpSession对象称为用户session。

SessionFactory接口 SessionFactory接口负责初始化Hibernate。它充当数据存储源的代理,并负责创建Session对象。这里用到了工厂模式。需要注意的是SessionFactory并不是轻量级的,因为一般情况下,一个项目通常只需要一个SessionFactory就够,当需要操作多个数据库时,可以为每个数据库指定一个SessionFactory。

Configuration接口 Configuration接口负责配置并启动Hibernate,创建SessionFactory对象。在Hibernate的启动的过程中,Configuration类的实例首先定位映射文档位置、读取配置,然后创建SessionFactory对象。

Transaction接口

  Transaction接口负责事务相关的操作。它是可选的,开发人员也可以设计编写自己的底层事务处理代码。

Query和Criteria接口

  Query和Criteria接口负责执行各种数据库查询。它可以使用HQL语言或SQL语句两种表达方式。

一、简单例子

说明:本实例基于hibernate3.3.2。

1.引入hibernate所需的jar。hibernate3.jar;antlr-2.7.6.jar、commons-collections-3.1.jar、dom4j-1.6.1.jar、javassist-3.9.0.GA.jar、jta-1.1.jar、slf4j-api-1.5.8.jar;slf4j-nop-1.5.8.jar;如果要用annotation,还需要引入hibernate-annotations.jar、hibernate-commons-annotations.jar、ejb3-persistence.jar。

2.新建com.hanjun.entity.Student类,有以下属性及getter、setter方法:

 private int sid;
 private String sname;
 private String age;

3.新建实体类的映射文件com.hanjun.entity.Student.hbm.xml:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
 <class name="com.hanjun.entity.Student" table="student" catalog="test">
  <id name="sid" type="java.lang.Integer">
   <column name="sid" /><!-- 如果属性名和数据库列名相同,column属性可不配置 -->
   <generator class="native" /><!-- ID生成策略 native:自动识别-->
  </id>
  <property name="age" type="java.lang.String">
   <column name="age" />
  </property>
  <property name="sname" type="java.lang.String">
   <!-- Hibernate会自动根据实体类属性类型生成数据库表中字段类型 -->
   <column name="sname" />
  </property>
 </class>
</hibernate-mapping>

annotation:实体类上注解@javax.persistence.Entity,如果类名和表名不一致,加注解@Table(name="表名");主键字段的get方法上注解@javax.persistence.Id,如果属性名和列表不一致,加注解@Column(name="列名"),如果不是数据库字段,在get方法上注解@Transient。枚举类型:@Enumerated(EnumType.STRING);时间类型:@Temporal(TemporalType.TIMESTAMP)可省略。

4.src下新建hibernate.cfg.xml:

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
          
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
          "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
 <session-factory>
  <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
  <property name="connection.url">jdbc:mysql://localhost:3306/test</property>
  <property name="connection.username">root</property>
  <property name="connection.password">root</property>
  <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
  <property name="show_sql">true</property><!-- 打印sql语句 -->
  <property name="format_sql">true</property><!-- 打印出的sql语句格式化输出 -->
  <property name="hbm2ddl.auto">update</property>
  <!-- 配置实体类映射文件位置 -->
  <mapping resource="com/hanjun/entity/Student.hbm.xml" />
 </session-factory>
</hibernate-configuration>
hbm2ddl.auto值为create:自动根据实体类配置新建表;create-drop:sessionFactory关闭时删除表;validate:检查,类配置和数据库不对应时报错;update:以类配置为准更新表。
new SchemaExport(new AnnotationConfiguration().configure()).create(true, true);如果不配置hbm2ddl.auto属性,hibernate不会自动建表,可以把此代码写在main方法中运行生成表,create方法第一个参数指定是否打印建表语句,第二个参数指定是否执行建表语句。

annotation:mapping配置class属性:

<mapping class="com.hanjun.entity.Student"/>

5.测试代码:

  Configuration conf=new Configuration().configure();
  SessionFactory factory=conf.buildSessionFactory();
  Session session=null;
  try {
   session = factory.openSession();
   session.beginTransaction();
   session.save(new Student("张三""27"));
   session.getTransaction().commit();
  } catch (Exception e) {
   e.printStackTrace();
   session.getTransaction().rollback();
  }finally{
   session.close();
  }

annotation:创建SessionFactory时用AnnotationConfiguration:

SessionFactory sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
二、id生成策略

1.在主键字段的get方法上注解@javax.persistence.GeneratedValue。GeneratedValue有属性strategy,默认值为GenerationType.AUTO,hibernate跟据数据库自动识别id生成策略。如果为oracle数据库,hibernate会固定建一个名为hibernate_sequence的序列。如果要使用指定序列:

在class上注解:@SequenceGenerator(name="seq_teacher", sequenceName="seq_teacher")  name指定该注解的名字,sequenceName指定数据库序列的名字。name和sequenceName的值可以不同。

在主键的get方法上:@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seq_teacher") generator的值为SequenceGenerator的name值。

xml方式指定oracle序列:

  <id name="id">
   <generator class="sequence">
    <param name="sequence">seq_student</param>
   </generator>
  </id>

2.表生成器,在class上注解:

@javax.persistence.TableGenerator(
     name="Teacher_GEN",
     table="GENERATOR_TABLE",
     pkColumnName = "pk_key",
     valueColumnName = "pk_value",
     pkColumnValue="Teacher",
     allocationSize=1
 )

name指定该注解的名字;table指定数据库中用来产生ID的表的名字;pkColumnName指定该表第一列的列名;valueColumnName指定该表第二列的列名;pkColumnValue指定一条数据的第一列的值,第二列的值默认为1;allocationSize指定每次增加的值。

在主键字段的get方法上注解:

@GeneratedValue(strategy=GenerationType.TABLE, generator="Teacher_GEN")

generator指定TableGenerator的name值。

3.联合主键(xml方式),如student表的id和name为联合主健,新建类StudentPK,声明id和name属性,实现Serializable接口,重写equals和hashCode方法:

public class StudentPK implements java.io.Serializable{
 private int id;
 private String name;
 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;
 }
 @Override
 public boolean equals(Object o) {
  if(o instanceof StudentPK) {
   StudentPK pk = (StudentPK)o;
   if(this.id == pk.getId() && this.name.equals(pk.getName())) {
    return true;
   }
  }
  return false;
 }
 @Override
 public int hashCode() {
  return this.name.hashCode();
 }
}

Student类中去掉id和name属性,增加StudentPK类属性及get、set方法:

 private StudentPK pk;
 public StudentPK getPk() {
  return pk;
 }
 public void setPk(StudentPK pk) {
  this.pk = pk;
 }

Student.hbm.xml中去掉id和name属性的配置,增加pk属性的配置:

  <composite-id name="pk" class="com.hanjun.entity.StudentPK">
   <key-property name="id"></key-property>
   <key-property name="name"></key-property>
  </composite-id>

annotation方式有三种:

在类上注解@Embeddable,在getPk方法上注解@Id

在getPk方法上注解@EmbeddedId

在类上注解@IdClass(StudentPK.class),不需要pk属性,保留id和name属性并分别在其get方法上注解@Id

三、session的方法

1.获取Session时openSession和getCurrentSession的区别:openSession每次打开一个新的session,在commit后需手动close。getCurrentSession如果当前已经有session,则不重新打开新session,commit后会自动关闭。使用getCurrentSession需要在hibernate.cfg.xml中配置:

<property name="current_session_context_class">thread</property> 如果用jboss及以上服务器,可以设置为jta

2.hibernate持久化对象的三种状态:

transient : hibernate缓存中没有,数据库中没有。刚new出来的对象、delete后。

persistent : hibernate缓存中有,数据库中有。get或load后session关闭前、save或update后提交前。

detached :hibernate缓存中没有,数据库中有。get或load的session关闭后、save或update提交后。

3.Session:

save(Object o) 保存一个对象

delete(Object o) 删除一个对象,对象的主键属性需赋值。

load(Class c,Serializable s) 查询主键的值为s的对象。如load(Student.class,1),查询id为1的Student,hibernate会把简单类型打包成对象类型。load方法返回的对象数据要在session关闭前使用,因为它返回的是一个代理对象,load时并不发出sql语句,在使用到返回的对象数据时才发出sql语句。load方法如果查不到数据,使用返回对象时会报错。

get(Class c,Serializable s) 与load方法功能相同。get方法即时发出sql语句,可以在session关闭后使用返回对象。get方法如果查不到数据则返回null,不报错。

update(Object o) 更新对象,对象为transientdetached状态时主键属性必须赋值,hibernate会依次更新对象的每一个字段。对象为persistent状态时可直接改变对象的属性,事务提交时hibernate会自动执行更新操作,hibernate会依次更新对象的每一个字段。

针对persistent状态对象的update,在实体类配置文件的class标签增加属性dynamic-update="true",事务提交时hibernate只更新改变了的字段。annotation方式:在类上注解@org.hibernate.annotations.Entity(dynamicUpdate=true),和@javax.persistence.Entity一起使用。

如果有一属性绝对不想更新可以在其get方法上注解@Column(updatable=false),xml方式:property的update="false"

saveOrUpdate(Object o) 如果对象的主键属性有值,则执行update,否则执行save。

merge(Object o) 先根据参数对象的主键查出一个persistent状态的对象,再把参数对象的其它属性值合并进去,然后更新数据库。

clear() 清除session缓存。

flush() 强制使session缓存中的对象和数据库做同步。

四、组件映射

组件映射就是将一个属性类里的属性做为自己的属性。数据库中对应一个表,表中有两个类的属性。

public class Wife implements Serializable{
 private String wifeName;
 private int age; 
 
 public String getWifeName() {
  return wifeName;
 }
 public void setWifeName(String wifeName) {
  this.wifeName = wifeName;
 }
 public int getAge() {
  return age;
 }
 public void setAge(int age) {
  this.age = age;
 }
}

@Entity
public class Husband {
 private int id;
 private String name;
 private Wife wife;
 @Id
 @GeneratedValue
 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;
 }
 @Embedded
 public Wife getWife() {
  return wife;
 }
 public void setWife(Wife wife) {
  this.wife = wife;
 }
}

xml方式:

 <class name="com.lyy.model.Husband">
  <id name="id">
   <generator class="native"></generator>
  </id>
  <property name="name"></property>
  <component name="wife">
   <property name="wifeName"></property>
   <property name="age"></property>
  </component>
 </class>

五、one-to-one

1.one-to-one外键关联单向:在从类外键对象属性的get方法上注解:

 @OneToOne
 @JoinColumn(name="外键字段名(表)") //省略此注解,hibernate用默认的外键字段名。

xml方式从类配置many-to-one:

<many-to-one name="外键对象字段名" column="外键字段名(表)" unique="true"></many-to-one>

说明:unique="true":此列唯一约束。

2.one-to-one外键关联双向:在主类中增加一个从类属性并在其get方法上注解:

@OneToOne(mappedBy="从类中外键对象属性名")  //表示这个是主表,只对从表对应属性生成外键列。

xml方式主类配置one-to-one:

<one-to-one name="本类中从类属性名" property-ref="从类中外键对象属性名"></one-to-one>

3.one-to-one单主键关联单向:去掉从类的ID生成策略配置,在外键对象属性的get方法上注解:

 @OneToOne(optional=false)
 @PrimaryKeyJoinColumn

说明:保存从类对象时,主键值要手动赋值,该值从外键对象中获取。

4.xml方式:

  <id name="id">
   <generator class="foreign">
    <param name="property">外键对象属性名</param>
   </generator>
  </id>

  <one-to-one name="外键对象属性名" constrained="true"></one-to-one>

说明:xml方式可以配置ID生成策略为外键,hibernate会自动去外键对象中取。注意上面配置的两个外键对象属性名的大小写要一致。

5.one-to-one单主键关联双向:在主类中增加一个从类属性并在其get方法上注解:

 @OneToOne
 @PrimaryKeyJoinColumn

xml方式:主类配置和one-to-one外键关联双向一样:

 <one-to-one name="本类中从类属性名" property-ref="从类中外键对象属性名"></one-to-one>

6.one-to-one外键关联联合主键单向,在从类外键对象的get方法上注解:

 @OneToOne
 @JoinColumns( { @JoinColumn(name = "wifeId", referencedColumnName = "id"),
   @JoinColumn(name = "wifeName", referencedColumnName = "name") })

说明:name指定外键列的列名,referencedColumnName指定主类主键属性名。

xml方式:

  <id name="id">
   <generator class="foreign">
    <param name="property">student</param>
   </generator>
  </id>
  <many-to-one name="student" unique="true">
   <column name="sid"></column>
   <column name="spass"></column>
  </many-to-one>

7.one-to-one外键关联联合主键双向,在主类增加从类属性并在其get方法上注解:

 @OneToOne(mappedBy= "从类中外键对象属性名")

xml方式:

<one-to-one name="本类中从类属性名" property-ref="从类中外键对象属性名"></one-to-one>

六、many-to-one

1.many-to-one单向,在从类的外键对象的get方法上注解:

 @ManyToOne
 @JoinColumn(name="外键列名")

xml方式:

  <many-to-one name="外键对象属性名" column="外键列名" />

2.one-to-many单向,在主类中添加从类的set对象集合并在其get方法上注解:

 @OneToMany
 @JoinColumn(name="从表外键列名")  //这里这个注解不能少,否刚hibernate会当做many-to-many

xml方式:

  <set name="从类集合属性名">
   <key column="从表外键列名"></key>
   <one-to-many class="从类包名.类名" />
  </set>

3.one-to-many、many-to-one双向:

@ManyToOne  //在从类的外键对象属性的get方法上注解。

@OneToMany(mappedBy="从表中外键对象属性名") //在主类中添加从类set集合并在其get方法上注解。

xml方式:

 <many-to-one name="外键对象属性名" column="外键列名"></many-to-one>  //从类配置

  <set name="从类集合属性名">
   <key column="从表外键列名"></key>
   <one-to-many class="从类包名.类名" />
  </set>  //主类配置

说明:从类配置中的外键列名要和主类配置中的从表外键列名配置一样,否则会有两个外键列。

4.one-to-many的对象用List或Map:

 @OneToMany(mappedBy="group")
 @OrderBy("name ASC")  //可以通过此注解指定排序字段和排序方式
 public List<User> getUsers() {
  return users;
 }

 @OneToMany(mappedBy="group")
 @MapKey(name="id")  //指定key用哪列的值
 public Map<Integer, User> getUsers() {
  return users;
 }

七、many-to-many

1.many-to-many单向,在其中一个类中添加另一个类的set集合并在其get方法上注解:

 @ManyToMany
 @JoinTable(name="中间表表名",
   joinColumns={@JoinColumn(name="中间表指向本类的外键列名")},
   inverseJoinColumns={@JoinColumn(name="中间表指向另一个类的外键列名")}
 )

xml方式:

  <set name="另一个类的set集合属性名" table="中间表表名">
   <key column="中间表指向本类的外键列名"></key>
   <many-to-many class="另一个类的包名.类名" column="中间表指向另一个类的外键列名" />
  </set>

2.mony-to-many双向,在另一个类中添加本类的set集合属性并在其get方法上注解:

@ManyToMany(mappedBy="本类中另一个类的set集合属性名")

xml方式:两边配置方法一样,所配置的值刚好相反。

3.中间表的映射:

@Entity
public class Course {
 private int id;
 private String name;
 @Id
 @GeneratedValue
 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;
 }
}

@Entity
public class Student {
 private int id;
 private String name;
 private Set<Course> courses = new HashSet<Course>();
 @Id
 @GeneratedValue
 public int getId() {
  return id;
 }
 public void setId(int id) {
  this.id = id;
 }
 @ManyToMany
 @JoinTable(name="score",
  joinColumns=@JoinColumn(name="student_id", referencedColumnName="id"),
  inverseJoinColumns=@JoinColumn(name="course_id", referencedColumnName="id")
  )
 public Set<Course> getCourses() {
  return courses;
 }
 public void setCourses(Set<Course> courses) {
  this.courses = courses;
 }
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
}

@Entity
@Table(name="score")
public class Score {
 private int id;
 private int score;
 private Student student;
 private Course course;
 @Id
 @GeneratedValue
 public int getId() {
  return id;
 }
 public void setId(int id) {
  this.id = id;
 }
 public int getScore() {
  return score;
 }
 public void setScore(int score) {
  this.score = score;
 }
 @ManyToOne
 @JoinColumn(name="student_id")
 public Student getStudent() {
  return student;
 }
 public void setStudent(Student student) {
  this.student = student;
 }
 @ManyToOne
 @JoinColumn(name="course_id")
 public Course getCourse() {
  return course;
 }
 public void setCourse(Course course) {
  this.course = course;
 }
}

说明:Student和Course为多对多关系,中间表为Score。需要注意的是,hibernate生成Score表时会以Student的配置为主导,student_id和course_id为联合主键,而Score本身的主键ID变为一个一般的not null字段,所以需要手动修改Score表结构。

八、继承映射

1.在数据库中用一张表,用一个字段指定这是哪个类的属性:

@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="discriminator", discriminatorType=DiscriminatorType.STRING)
@DiscriminatorValue("person")
public class Person {
 private int id;
 private String name;
 
 @Id
 @GeneratedValue
 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;
 }
}

@Entity
@DiscriminatorValue("teacher")
public class Teacher extends Person {
 private String title;
 public String getTitle() {
  return title;
 }
 public void setTitle(String title) {
  this.title = title;
 }
}

@Entity
@DiscriminatorValue("student")
public class Student extends Person {
 
 private int score;
 public int getScore() {
  return score;
 }
 public void setScore(int score) {
  this.score = score;
 }
}

2.在数据库中父类和每个子类各一张表,每个子类表中都有父类的字段,各表之间没有关系:

@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
//@TableGenerator(
//  name="t_gen",
//  table="t_gen_table",
//  pkColumnName="t_pk",
//  valueColumnName="t_value",
//  pkColumnValue="person_pk",
//  initialValue=1,
//  allocationSize=1
//  )
public class Person {
 private int id;
 private String name;
 
 @Id
 @GeneratedValue//(generator="t_gen", strategy=GenerationType.TABLE)
 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;
 }
}

@Entity
public class Teacher extends Person {
 private String title;
 public String getTitle() {
  return title;
 }
 public void setTitle(String title) {
  this.title = title;
 }
}

@Entity
public class Student extends Person {
 
 private int score;
 public int getScore() {
  return score;
 }
 public void setScore(int score) {
  this.score = score;
 }
}

3.在数据库中父类和每个子类各一张表,子类表里只有自己的字段,子类主键关联父类主键:

@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public class Person {
 private int id;
 private String name;
 
 @Id
 @GeneratedValue
 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;
 }
}

@Entity
public class Teacher extends Person {
 private String title;
 public String getTitle() {
  return title;
 }
 public void setTitle(String title) {
  this.title = title;
 }
}

@Entity
public class Student extends Person {
 
 private int score;
 public int getScore() {
  return score;
 } //Person p = Person(load(1));
 public void setScore(int score) {
  this.score = score;
 }
}

九、树状映射

1.class:

@Entity
public class Org {
 private int id;
 private String name;
 private Set<Org> children = new HashSet<Org>();
 private Org parent;
 @Id
 @GeneratedValue
 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;
 }
 @OneToMany(mappedBy="parent",cascade=CascadeType.ALL)
 public Set<Org> getChildren() {
  return children;
 }
 public void setChildren(Set<Org> children) {
  this.children = children;
 }
 @ManyToOne
 @JoinColumn(name="parent_id")
 public Org getParent() {
  return parent;
 }
 public void setParent(Org parent) {
  this.parent = parent;
 }
}

2.testSave:

 @Test
 public void testSave() {
  Org o = new Org();
  o.setName("总公司");
  Org o1 = new Org();
  o1.setName("分公司1");
  Org o2 = new Org();
  o2.setName("分公司2");
  Org o11 = new Org();
  o11.setName("分公司1下部门1");
  Org o12 = new Org();
  o12.setName("分公司1下部门2");
  
  o.getChildren().add(o1);
  o.getChildren().add(o2);
  o1.getChildren().add(o11);
  o1.getChildren().add(o12);
  o11.setParent(o1);
  o12.setParent(o1);
  o1.setParent(o);
  o2.setParent(o);
    
  Session session = sessionFactory.openSession();
  session.beginTransaction();
  session.save(o);
  session.getTransaction().commit();
  session.close();
 }

3.testLoad:

 @Test
 public void testLoad() {
  testSave();
  Session session = sessionFactory.openSession();
  session.beginTransaction();
  Org o = (Org)session.load(Org.class, 1);
  print(o, 0);
  session.getTransaction().commit();
  session.close();
 }
 
 private void print(Org o, int level) {
  String preStr = "";
  for(int i=0; i<level; i++) {
   preStr += "----";
  }
  System.out.println(preStr + o.getName());
  for(Org child : o.getChildren()) {
   print(child, level+1);
  }
 }

十、关联对象的属性

1.cascade:关键对象的属性,指定对象在做增删改操作时,是否自动级联更改这个关联对象:

@ManyToOne(cascade={CascadeType.ALL}) //不设定cascade属性则不级联更改。

cascade的值:

CascadeType.ALL      增删改时都级联

CascadeType.MERGE    调用meger方法时级联(更新)

CascadeType.PERSIST  调用persist方法时级联(新增)

CascadeType.REFRESH  调用refresh方法时级联

CascadeType.REMOVE   调用delete方法时级联

xml方式:casade="all" 或 "save-update" 或 "delete" 或 "none"

2.fetch:关键对象的属性,指定本类查询时,这个关联对象是否一起查出来:

@ManyToOne(fetch=FetchType.EAGER) //多对一的fetch默认值为EAGER,一对多的fetch默认值为LAZY

fetch的值:

FetchType.EAGER  即时关联查询

FetchType.LAZY   用到这个关联对象事时再查询,要在session关闭之前。

xml方式:lazy="true"或"false"

3.fetch = "select"或"join",多对一的属性,设置为join时不会产生1加N。

4.inverse="true",一对多或多对多的属性,交出控制权,级连操作时由对方来维护关联关系。

十一、QL

1.java类:版块表、主贴表、回贴表。

@Entity
public class Category {
 private int id;
 private String name;
 @Id
 @GeneratedValue
 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;
 }
}

@Entity
public class Topic {
 private int id;
 private String title;
 private Category category;
 private Date createDate;
 
 public Date getCreateDate() {
  return createDate;
 }
 public void setCreateDate(Date createDate) {
  this.createDate = createDate;
 }
 @ManyToOne
 public Category getCategory() {
  return category;
 }
 public void setCategory(Category category) {
  this.category = category;
 }
 @Id
 @GeneratedValue
 public int getId() {
  return id;
 }
 public void setId(int id) {
  this.id = id;
 }
 public String getTitle() {
  return title;
 }
 public void setTitle(String title) {
  this.title = title;
 }
}

@Entity
public class Msg {
 private int id;
 private String cont;
 private Topic topic;
 @ManyToOne
 public Topic getTopic() {
  return topic;
 }
 public void setTopic(Topic topic) {
  this.topic = topic;
 }
 @Id
 @GeneratedValue
 public int getId() {
  return id;
 }
 public void setId(int id) {
  this.id = id;
 }
 
 public String getCont() {
  return cont;
 }
 public void setCont(String cont) {
  this.cont = cont;
 }
}

2.查询所有版块:

  Query q = session.createQuery("from Category");
  List<Category> categories = q.list();

3.查询所有name大于c5的版块:

  Query q = session.createQuery("from Category c where c.name > 'c5'");
  List<Category> categories = q.list();

4.查询所有版块并按name倒序:

  Query q = session.createQuery("from Category c order by c.name desc");
  List<Category> categories = q.list();

5.查询ID大地2小于8的版块:

  Query q = session.createQuery("from Category c where c.id > :min and c.id < :max");
  q.setParameter("min", 2);
  q.setParameter("max", 8);
  List<Category> categories = q.list();

6.分页查询,查询4个版块,从第2条数据开始:

  Query q = session.createQuery("from Category c order by c.name desc");
  q.setMaxResults(4);
  q.setFirstResult(2);
  List<Category> categories = q.list();

7.查询任意一些列:

  Query q = session.createQuery("select c.id, c.name from Category c");
  List<Object[]> categories = q.list();
  for(Object[] o : categories) {
   System.out.println(o[0] + "-" + o[1]);
  }

8.查询ID为1的版块下的所有主贴:

  Query q = session.createQuery("from Topic t where t.category.id = 1");
  List<Topic> topics = q.list();

9.查询ID为1的版块下的反有回贴:

  Query q = session.createQuery("from Msg m where m.topic.category.id = 1");
  List<Msg> msgs=q.list();

10.查询任意一些列,并把它封装进自定义类:

public class MsgInfo { //VO DTO Value Object username p1 p2 UserInfo->User->DB
 private int id;
 private String cont;
 private String topicName;
 private String categoryName;
 public MsgInfo(int id, String cont, String topicName, String categoryName) {
  super();
  this.id = id;
  this.cont = cont;
  this.topicName = topicName;
  this.categoryName = categoryName;
 }
 public String getTopicName() {
  return topicName;
 }
 public void setTopicName(String topicName) {
  this.topicName = topicName;
 }
 public String getCategoryName() {
  return categoryName;
 }
 public void setCategoryName(String categoryName) {
  this.categoryName = categoryName;
 }
 public int getId() {
  return id;
 }
 public void setId(int id) {
  this.id = id;
 }
 public String getCont() {
  return cont;
 }
 public void setCont(String cont) {
  this.cont = cont;
 }
}

  Query q = session.createQuery("select new com.bjsxt.hibernate.MsgInfo(m.id, m.cont, m.topic.title, m.topic.category.name) from Msg");
  List<MsgInfo> msgs = q.list();

11.连接查询:

  Query q = session.createQuery("select t.title, c.name from Topic t join t.category c ");
  List<Object[]> objs = q.list();

12.唯一查询,条件指定对象:

  Query q = session.createQuery("from Msg m where m = :MsgToSearch "); //不重要
  Msg m = new Msg();
  m.setId(1);
  q.setParameter("MsgToSearch", m);
  Msg mResult = (Msg)q.uniqueResult();

13.唯一查询,返回唯一结果:

  Query q = session.createQuery("select count(*) from Msg m");
  long count = (Long)q.uniqueResult();//int类型必须用Long接收

14.唯一查询,返回单条记录:

  Query q = session.createQuery("select max(m.id), min(m.id), avg(m.id) from Msg m");
  Object[] o = (Object[])q.uniqueResult();

15.在主贴中添加回贴集合的映射,查询没有回贴的主贴,集合类型判断为空用is empty:

 private List<Msg> msgs = new ArrayList<Msg>();
 @OneToMany(mappedBy="topic")
 public List<Msg> getMsgs() {
  return msgs;
 }
 public void setMsgs(List<Msg> msgs) {
  this.msgs = msgs;
 }

Query q = session.createQuery("from Topic t where t.msgs is empty");

16.查询数据库日期和时间:

  Query q = session.createQuery("select current_date,current_timestamp from Topic");
  q.setMaxResults(1);
  Object[] o=(Object[]) q.uniqueResult();
  Date date = (Date) o[0];
  Timestamp ts=(Timestamp) o[1];

17.解决1+N问题:

List<Topic> topics=session.createQuery("from Topic t left join fetch t.category c").list();

List<Topic> topics = session.createCriteria(Topic.class).list();

18.QBC:

  Criteria c = session.createCriteria(Topic.class)// from Topic
    .add(Restrictions.gt("id", 12)) // id > 2
    .add(Restrictions.lt("id", 18)) // id < 8
    .add(Restrictions.like("title", "t_")) // title like 't_'
    .addOrder(Order.desc("title")) //在最后加上 order by title desc
    .createCriteria("category") // left join category
    .add(Restrictions.between("id", 1, 5)); // category.id between 1 and 5
  List<Topic> list = c.list();

Restrictions.eq("id", 12)  // id = 12 [ne : !=] [ge : >=] [le : <=]

Restrictions.in("id", new Object[]{12,13,14})  // id in(12,13,14)

Restrictions.isNull("title")  // title is null  [isNotNull]

Restrictions.isEmpty("msgs") // not exists (select 1 from Msg  where id=t.id) [isNotEmpty]

Restrictions.sizeEq("msgs", 10)  // (select count(*) from Msg where id=t.ip) = 10 类似的还有sizeGe sizeGt sizeLe sizeLt sizeNe 方法。

Restrictions.not(Restrictions.eq("id", 12))  // not id = 12

Restrictions.or(Restrictions.isNotNull("title"), Restrictions.like("title", "t_")) [and]

19.QBE:

  Topic tExample = new Topic();
  tExample.setTitle("T_");
  Example e = Example.create(tExample).ignoreCase().enableLike(); //忽略大小写、使用模糊查询
  Criteria c = session.createCriteria(Topic.class)
      .add(Restrictions.gt("id", 12))
      .add(Restrictions.lt("id", 18))
      .add(e);
  List<Topic> list = c.list();

十二、缓存

1.query.list与query.iterate的区别:

list取全部字段,iterate只取主键,用到时再跟据主键查对象。

list每次从数据库中查,iterate首先从缓存中查找。

2.一级缓存(同一个session内),二级缓存(跨session)。

3.使用ehcache,引入ehcache-core.jar,低版本还需要commons-logging.jar,在hibernate.cfg.xml中增加配置:

  <property name="cache.use_second_level_cache">true</property>
  <property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>

复制jar中的ehcache-failsafe.xml到src下并更名为ehcache.xml。在需要缓存的类上注解:

@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)

说明:Cache中另一个参数region="ehcache.xml中配置的其它cache的name"指定使用自定义cache属性配置。get load iterate使用二级缓存,list只往缓存中存数据,不从缓存中取数据。

4.查询缓存,依赖于二级缓存,在hibernate.cfg.xml中增加配置:

<property name="cache.use_query_cache">true</property>

使用前提,两条查询语句相同,使用方法:

  Session session = sf.openSession();
  session.beginTransaction();
  List<Category> categories = session.createQuery(
    "from Category c where c.id between 1 and 12").setCacheable(true).list();
  session.getTransaction().commit();
  session.close();

  Session session2 = sf.openSession();
  session2.beginTransaction();
  List<Category> categories2 = session2.createQuery(
    "from Category c where c.id between 1 and 12").setCacheable(true).list();
  session2.getTransaction().commit();
  session2.close();

十三、事务隔离机制

1.hibernate设定事务级别:

<property name="connection.isolation">2</property>

事务级别:1.read-uncommitted 2.read-committed 4.repeatable read 8.serializable

2.悲观锁:

Account a = (Account)session.get(Account.class, 1, LockMode.UPGRADE); //相当于for update

3.乐观锁,在表中增加一列,用来记录版本,在实体类中的对应属性的get方法上注解:

@Version

说明:后提交的事务会报错。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值