简介:
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 String sname;
private String age;
3.新建实体类的映射文件com.hanjun.entity.Student.hbm.xml:
<!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:
<!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>
annotation:mapping配置class属性:
5.测试代码:
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:
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();
}
}
private StudentPK pk;
public StudentPK getPk() {
return pk;
}
public void setPk(StudentPK pk) {
this.pk = 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) 更新对象,对象为transient或detached状态时主键属性必须赋值,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="外键列名")
<many-to-one name="外键对象属性名" column="外键列名" />
2.one-to-many单向,在主类中添加从类的set对象集合并在其get方法上注解:
@OneToMany
@JoinColumn(name="从表外键列名") //这里这个注解不能少,否刚hibernate会当做many-to-many
<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>
@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
说明:后提交的事务会报错。