Hibernate
Hibernate 简单应用(单表)
1. 导入hibernate所需要的jar包:
antlr.jar:用来解析 HQL 语句。
cglib.jar:用来修改类。
asm.jar:在运行时候修改类。依赖于 cglib.jar。commons-collections:提供集合功能,比 java 类中的集合提供更强大的功能。commons-logging.jar:记录日志。jta.jar:分布式事务。当有跨数据库访问的时候,需要这个包。dom4j.jar:用来解析 XML 文件。
(记得导入数据库 jar 包)
2. 配置hibernate.cfg.xml文件(放在“src”下)
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN""http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd
">
<hibernate-configuration><session-factory>
<propertyname="hibernate.dialect">
>
</property
org.hibernate.dialect.MySQLDialect
批注 [z1]: 数据库的方言。
批注 [z2]: 数据库驱动的名称。
批注 [z3]: 连接数据库的 URL 地址。批注 [z4]: 用户名。
批注 [z5]: 密码。
批注 [z6]: 每次加载 hibernate,重新创建数据库表结构,这就是导致数据库表数据丢失的原因。
批注 [z7]: 显示生成的 SQL 语句。批注 [z8]: 指定映射文件的路径。
<property
name="hibernate.connection.driver_class"> </prope
rty>
<property
name="hibernate.connection.url"> </property>
<property name="hibernate.connection.username"><property name="hibernate.connection.password"><property name="hbm2ddl.auto"> </property>
<property name="show_sql"> </property>
<mapping resource="
</session-factory></hibernate-configuration>
com.mysql.jdbc.Driver
jdbc:mysql:///zhaohan
</property>
root
</property>
123
create
true
com/it315/domain/User.hbm.xml
"/>
Validate:加载 hibernate 时,验证创建数据库表结构。
create:每次加载 hibernate,重新创建数据库表结构,这就是导致数据库表数据丢失的原因。
Hbm2ddl.auto 的配置参数:
传智博客—赵涵笔记 1
3. User.java
4. User.hbm.xml
create-drop:加载 hibernate 时创建,退出是删除表结构。update :加载 hibernate 自动更新数据库结构。
package com.it315.domain;import java.util.Date;public class User {
private long id;private String name;private Date birthday;......set,get方法省略
}
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"><hibernate-mapping
package=" ">
<class name="User" table="
<id name="id">
<generator class=" "/>
</id>
<property name="name" column="
">
com.it315.domain
批注 [z9]: User.java 所在的包。
批注 [z10]: 与 User.java 映射的表名。
批注 [z11]: 由 hibernate 根据底层数据库自行判断采用identity,hilo,sequence 其中一种作为主键生成方式。
批注 [z12]: Name 属性对应生成表中的 user_name 字段。
UserTable
native
"></property>
user_name
<property name="birthday" column="user_birthday"></property>
</class></hibernate-mapping>
package com.it315.main;
import java.util.Date;
import org.hibernate.Session;
import org.hibernate.SessionFactory;import org.hibernate.Transaction;import org.hibernate.cfg.Configuration;import com.it315.domain.User;
public class Main {
public static void main(String[] args) {
Configuration = new Configuration();
批注 [z13]: Configuration 实例会根据当前的数据库配置信息,构造SessionFactory 实例并返回。
cfg
5. Main.java
传智博客—赵涵笔记 2
批注 [z14]: 如果配置文件的名称不是默认的“hibernat e.cfg .xml”,那么 configure()方法要指定文件名。
批注 [z15]: Session 是持久层操作的基础,相当于 jdbc 中的 connection.
批注 [z16]: 对数据有更改的操作都要开启事务和提交事务。Hibernate默认的事务是关闭的。
SessionFactory sessionfactory =cfg. .buildSessionFactory();
Session = sessionfactory.openSession();
User user = new User();user.setName("zhaohan");user.setBirthday(new Date());
Transaction tc = session. ;
session.save(user);
tc.commit();}
}
configure()
session
beginTransaction()
package com.it315.util;
import org.hibernate.Session;
import org.hibernate.SessionFactory;import org.hibernate.cfg.Configuration;
public final class HibernateUtils {
private static SessionFactory sessionFactory;static {
Configuration cfg = new Configuration();
sessionFactory = cfg.configure().buildSessionFactory();
}
public static Session getSession(){
return sessionFactory.openSession();}
}
package com.it315.main;
import java.util.Date;
import org.hibernate.Session;
import org.hibernate.Transaction;import com.it315.domain.User;
import com.it315.util.HibernateUtils;
Domain Object的限制:
○1 必须有一个无参数的构造方法。
○2 有无意义的表示符 id.(可选)
○3 非 final 的,对懒加载有影响。(可选)
注意 :初始化的代码是很耗资源的,所以将上面代码修改为:HibernateUtils.java
Main.java
传智博客—赵涵笔记 3
public class Main {
public static void main(String[] args) {
Session session = null;Transaction tc = null;try {
session = HibernateUtils.getSession();
User user = new User();user.setName("zhaohan");user.setBirthday(new Date());
tc = session.beginTransaction();session.save(user);
tc.commit();
} catch (Exception e) {if(tc != null){
tc.rollback();
throw new RuntimeException(e.getMessage(),e);}
} finally {
if(session != null){
session.close();}
}}
}
<id name="id"
<generator class="native"/></id>
unsaved-value="0"
>
批注 [z17]: 设置没有保存的对象的id 值为-1。调用 session 的
saveOr Updat e()方法,如果这个对象的 id 和设置的这个-1 是相等的,那么说明这个对象没有被保存过,就保存。如果不相等,那么就更新。
注意:
Hibernate 中的几个重要方法:
○1 save 和 persist 的区别(在没有开启事务的前提下是有区别的,如果开启了事务,则效果是一样的。)
○1 save 在没有开启事务的前提下,会将数据插入数据库,但是当发现事务没有开启的话,插入的数据就会立刻回滚。
○2 persist 在没有开启事务的前提下,不会产生 insert 语句。○2 saveOrUpdate
○3 get 和 load 的区别
□1 调用 get()方法,会立刻访问数据库。生成 select 语句。
□2 调用 load()方法,返回一个代理对象,只有在取得对象属性的时候才会真正的去访
传智博客—赵涵笔记 4
问数据库。例如:
批注 [z18]: 得到的这个 user 对象,是从 User 类继承来的。
static User getUser(long id){Session session = null;
Transaction tc = null;
try {
session = HibernateUtils.getSession();
User user = (User)session.load(User.class, id);System.out.println( );
System.out.println(user.getName());
return user;
} catch (Exception e) {
if (tc != null) {tc.rollback();
throw new RuntimeException(e.getMessage(), e);}
} finally {
if (session != null) {
session.close();}
}
return null;}
user.getClass()
package com.it315.UserDaoImpl;
Hibernate 中的对象的几种状态:
○1 瞬时:数据库中没有数据与之对应,超过作用域会被 JVM 垃圾回收器回收,一般是 new
出来且与 session 没有关联的对象。
○2 持久:数据库中有数据与之对应,当前与 session 有关联,并且相关联的 session 没
有关闭,事 务没有提 交,持久 对象状态 发生改变 ,在事 务提交时 会影响到 数据库
(hibernate 能检测到)。
○3 托管:数据库中有数据与之相应,但当前没有 session 与之关联。托管对象状态发生改
变,hibernate 不能检测到。
判断对象的状态是根据 session 判断的,看它与 session 是否关联。例如:刚查到的对象,在 session 还没有关闭之前,是持久状态,因为数据库中有数据与它对应,且相关联的 session 没有关闭,这时候修改它的属性,hibernate 能检测到。若在 session关闭之后,虽然数据库中也有数据与它相对应,但没有与 session 关联,所以变为托管状态。
增,删,改,查:UserDaoImpl.java
传智博客—赵涵笔记 5
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;import com.it315.daoI.UserDaoI;import com.it315.domain.User;
import com.it315.util.HibernateUtils;
public class UserDaoImpl implements UserDaoI {Session session = HibernateUtils.getSession();
Transaction tc = null;
@Override
public void saveUser(User user) {
try {
tc = session.beginTransaction();session.save(user);
tc.commit();
} finally {
if (session != null) {
session.close();}
}}
@Override
public User findUserById(long id) {
try {
User user = (User) session.get(User.class, id);return user;
} finally {
if (session != null) {
session.close();}
}}
@Override
public User findUserByName(String name) {
try {
Query q = session
.createQuery("from User as user where user.name =?");q.setString(0, name);
传智博客—赵涵笔记 6
=:
");
/*Query q = session
.createQuery("from User as user where user.name
q.setString("*/
User user = (User) q.uniqueResult();
name
批注 [z19]: name 是随便起的。
批注 [z20]: 和上面批注的 name 保持一致。
name
", name);
return user;} finally {
if (session != null) {session.close();
}}
}
@Override
public void updateUser(User user) {
try {
tc = session.beginTransaction();session.update(user);tc.commit();
} finally {
if (session != null) {
session.close();}
}}
public void saveOrUpdate(User user){try {
tc = session.beginTransaction();session.saveOrUpdate(user);tc.commit();
} finally {
if (session != null) {
session.close();}
}}
@Override
public void remove(User user) {
try {
tc = session.beginTransaction();
传智博客—赵涵笔记 7
session.delete(user);
tc.commit();} finally {
if (session != null) {session.close();
}}
}}
package com.it315.test;import java.util.Date;
import org.junit.BeforeClass;import org.junit.Test;
import com.it315.UserDaoImpl.UserDaoImpl;import com.it315.daoI.UserDaoI;
import com.it315.domain.User;
public class UserDaoImplTest {static UserDaoI dao = null;static User user = null;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
dao = new UserDaoImpl();
user = new User();user.setName("zhangsan");user.setBirthday(new Date());
}
@Test
public void testFindUserById() {
User user = dao.findUserById(19);
System.out.println(user.getName());}
@Test
public void testFindUserByName() {
User user = dao.findUserByName("mali");
System.out.println(user.getBirthday());}
@Test
Test.java
传智博客—赵涵笔记 8
public void testRemove() {user.setId(19);dao.remove(user);
}
@Test
public void testSaveUser() {
dao.saveUser(user);}
@Test
public void testUpdateUser() {
user.setId(19);user.setName("maik");dao.updateUser(user);
}}
package com.it315.domain;public class Department {
private int id;private String name;get(),set()方法省略......
}
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"><hibernate-mapping
package="com.it315.domain">
<class name="Department" table="Department">
<id name="id">
<generator class="native"/>
</id>
<property name="name" column="department_name"></property></class>
</hibernate-mapping>
关联和集合的映射
1. 多对一(Employee-Department)○1 Department.java
○2 Department.hbm.xml
传智博客—赵涵笔记 9
○3 Employee.java
○4 Employee.hbm.xml
批注 [z21]: 员工与部门之间是多对一的关系。在这里将 Employee.java里的 depart 属性映射为 Employee 表中的 depart_id 字段。
默认是与部门的 id 进行关联,如果想指定关联的字段:<many-to-one name="depart"column= "depa rt_id "
propert y-ref ="nam e"/>
package com.it315.domain;public class Employee {
private int id;
private String name;private int age;
private Department depart;get(),set()方法省略......
}
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"><hibernate-mapping
package="com.it315.domain">
<class name="Employee" table="Emlpoyee">
<id name="id">
<generator class="native"/>
</id>
<property name="name" column="employee_name"></property><property name="age" column="employee_age"></property>
< column="depart_id" />
</class></hibernate-mapping>
many-to-one name="depart"
......
<mapping resource="com/it315/domain/Department.hbm.xml"/><mapping resource="com/it315/domain/Employee.hbm.xml"/>......
package com.it315.test;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;import com.it315.domain.Department;import com.it315.domain.Employee;import com.it315.util.HibernateUtils;
○5 hibernate.cfg.xml
○6 Test.java
传智博客—赵涵笔记 10
批注 [z22]: 清除一级缓存。读取数据时默认是先去一级缓存中查询,一级缓存中没有的话才去数据库里查询。
public class Test {
public static void main(String[] args) {
Session session = null;Transaction tc = null;try {
session = HibernateUtils.getSession();tc = session.beginTransaction();
Department depart = new Department();depart.setName("JiShu");
session.save(depart);
Employee emp = new Employee();emp.setAge(20);emp.setName("zhaohan");emp.setDepart(depart);
session.save(emp);
session. ;
System.out.println("----------------------------");
Employee emp1 = (Employee)session.get(Employee.class,emp.getId());
System.out.println(emp1.getName());
tc.commit();
} finally {
if (session != null) {
session.close();}
}}
}
clear()
Hibernate: insert into Department (department_name) values (?)Hibernate: insert into Emlpoyee (employee_name, employee_age, depart_id)values (?, ?, ?)
----------------------------
zhaohan
JiShu
不调用 clear()方法输出的语句是:
传智博客—赵涵笔记 11
掉用 clear()方法输出的语句是:
Hibernate: insert into Department (department_name) values (?)Hibernate: insert into Emlpoyee (employee_name, employee_age, depart_id)values (?, ?, ?)
----------------------------
Hibernate: select employee0_.id as id1_0_, employee0_.employee_name asemployee2_1_0_, employee0_.employee_age as employee3_1_0_,employee0_.depart_id as depart4_1_0_ from Emlpoyee employee0_ whereemployee0_.id=?
zhaohan
Hibernate: select department0_.id as id0_0_,department0_.department_name as department2_0_0_ from Departmentdepartment0_ where department0_.id=?
JiShu
CREATE TABLE `department` (
`id` int(11) NOT NULL auto_increment,`department_name` varchar(255) default NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=gbk;
CREATE TABLE `emlpoyee` (
`id` int(11) NOT NULL auto_increment,
`employee_name` varchar(255) default NULL,
`employee_age` int(11) default NULL,
`depart_id` int(11) default NULL,
PRIMARY KEY (`id`),
KEY `FK44624656163671D3` (`depart_id`),
CONSTRAINT `FK44624656163671D3` FOREIGN KEY (`depart_id`) REFERENCES
`department` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=gbk;
package com.it315.domain;import java.util.Set;
public class Department {
private int id;
private String name;
private Set<Employee> employee;get(),set()方法省略......
表的定义语句为:
2. 一对多(Department-Employee)○1 Department.java
传智博客—赵涵笔记 12
}
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"><hibernate-mapping package="com.it315.domain">
<class name="Department" table="Department"><id name="id">
<generator class="native" /></id>
<property name="name" column="department_name"></property><set name=" ">
<key column="
<one-to-many class="Employee" /></set>
</class></hibernate-mapping>
employee
批注 [z23]: 映射 Department 的employee 属性,它的类型是 set 类型。
批注 [z24]: 生成外键的字段名。(注意这个字段名要和 Employee 表中定义的外键字段名相同。)
depart_id
" />
package com.it315.domain;public class Employee {
private int id;
private String name;private int age;
private Department depart;get(),set()方法省略......
}
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"><hibernate-mapping
package="com.it315.domain">
<class name="Employee" table="Emlpoyee">
<id name="id">
<generator class="native"/>
</id>
<property name="name" column="employee_name" /><property name="age" column="employee_age" /><many-to-one name="depart" column=" "/>
批注 [z25]: 这个字段名要和Department.hbm.xml 里 set 集合映射的 key 的字段名相同。
depart_id
○2 Department.hbm.xml
○3 Employee.java
○4 Employee.hbm.xml
传智博客—赵涵笔记 13
</class></hibernate-mapping>
package com.it315.test;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.Session;
import org.hibernate.Transaction;import com.it315.domain.Department;import com.it315.domain.Employee;import com.it315.util.HibernateUtils;
public class Test {
public static void main(String[] args) {
save();
test();
}
static void save() {
Session session = null;Transaction tc = null;try {
session = HibernateUtils.getSession();tc = session.beginTransaction();
Department depart = new Department();depart.setName("jishu");session.save(depart);
Set<Employee> set = new HashSet<Employee>();
Employee emp = new Employee();emp.setAge(20);emp.setName("lisa");emp.setDepart(depart);session.save(emp);set.add(emp);
Employee emp1 = new Employee();emp1.setAge(21);emp1.setName("zhangsan");emp1.setDepart(depart);session.save(emp1);set.add(emp1);
○5 Test.java
传智博客—赵涵笔记 14
System.out.println("-------------------");depart.setEmployee(set);
tc.commit();
} finally {
if (session != null) {
session.close();}
}}
static void test() {Session session = null;try {
session = HibernateUtils.getSession();
Department depart =(Department)session.get(Department.class, 1);
Set<Employee> set = depart.getEmployee();for(Employee emp : set){
System.out.println(emp.getName());}
} finally {
if(session != null){
session.close();}
}}
}
Hibernate: insert into Department (department_name) values (?)Hibernate: insert into Emlpoyee (employee_name, employee_age, depart_id)values (?, ?, ?)
Hibernate: insert into Emlpoyee (employee_name, employee_age, depart_id)values (?, ?, ?)
-------------------
Hibernate: update Emlpoyee set depart_id=? where id=?
Hibernate: update Emlpoyee set depart_id=? where id=?
Hibernate: select department0_.id as id0_0_,
○6 运行结果:
传智博客—赵涵笔记 15
department0_.department_name as department2_0_0_ from Departmentdepartment0_ where department0_.id=?
Hibernate: select employee0_.depart_id as depart4_1_, employee0_.id asid1_, employee0_.id as id1_0_, employee0_.employee_name asemployee2_1_0_, employee0_.employee_age as employee3_1_0_,employee0_.depart_id as depart4_1_0_ from Emlpoyee employee0_ whereemployee0_.depart_id=?
zhangsanlisa
CREATE TABLE `department` (
`id` int(11) NOT NULL auto_increment,`department_name` varchar(255) default NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `emlpoyee` (
`id` int(11) NOT NULL auto_increment,
`employee_name` varchar(255) default NULL,
`employee_age` int(11) default NULL,
`depart_id` int(11) default NULL,
PRIMARY KEY (`id`),
KEY `FK44624656163671D3` (`depart_id`),
CONSTRAINT `FK44624656163671D3` FOREIGN KEY (`depart_id`) REFERENCES
`department` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
package com.it315.domain;
public class Room {private int id;private int floor;private Door door;get(),set()方法省略......}
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
表的定义语句为:
3. 一对一(Room-Door)○1 Room.java
○2 Room.hbm.xml
传智博客—赵涵笔记 16
批注 [z26]: 通过外键生成主键。
批注 [z27]: 告诉当前表主键上存在一个约束。
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"><hibernate-mapping package="com.it315.domain">
<class name="Room" table="room"><id name="id">
<generator class="native" /></id>
<property name="floor" /><one-to-one name="door" />
</class></hibernate-mapping>
package com.it315.domain;
public class Door {private int id;private int number;private Room room;get(),set()方法省略......}
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"><hibernate-mapping package="com.it315.domain">
<class name="Door" table="door"><id name="id">
<generator class=" ">
<param name="property">room</param></generator>
</id>
<property name="number" />
<one-to-one name="room" />
</class></hibernate-mapping>
foreign
constrained="true"
package com.it315.test;
import org.hibernate.Session;import org.hibernate.Transaction;import com.it315.domain.Door;import com.it315.domain.Room;
○3 Door.java
○4 Door.hbm.xml
○5 Test.java
传智博客—赵涵笔记 17
import com.it315.util.HibernateUtils;
public class Test {
public static void main(String[] args) {
add();
select();
}
public static void add() {Session session = null;Transaction tc = null;try {
session = HibernateUtils.getSession();tc = session.beginTransaction();
Room r = new Room();r.setFloor(1);
Door d = new Door();d.setNumber(2);d.setRoom(r);
session.save(r);session.save(d);
tc.commit();} finally {
if (session != null) {session.close();
}}
}
public static void select() {Session session = null;try {
session = HibernateUtils.getSession();
Room r = (Room) session.get(Room.class, 1);System.out.println(r.getDoor().getNumber());
} finally {
if (session != null) {
session.close();}
}
传智博客—赵涵笔记 18
}}
Hibernate: insert into room (floor) values (?)
Hibernate: insert into door (number, id) values (?, ?)
Hibernate: select room0_.id as id1_1_, room0_.floor as floor1_1_,door1_.id as id0_0_, door1_.number as number0_0_ from room room0_ leftouter join door door1_ on room0_.id=door1_.id where room0_.id=?
2
CREATE TABLE `room` (
`id` int(11) NOT NULL auto_increment,`floor` int(11) default NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `door` (
`id` int(11) NOT NULL,
`number` int(11) default NULL,
PRIMARY KEY (`id`),
KEY `FK2F23AE66854F71` (`id`),
CONSTRAINT `FK2F23AE66854F71` FOREIGN KEY (`id`) REFERENCES `room`
(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"><hibernate-mapping package="com.it315.domain">
<class name="Door" table="door"><id name="id">
<generator class="native" /></id>
<property name="number" />
<many-to-one name="room" column="room_id" unique="true"/></class>
</hibernate-mapping>
结果:
表的定义语句为:
两张表共用一个主键,所以只执行了一条查询语句。一对一的另一种方法:
Door.hbm.xml
传智博客—赵涵笔记 19
表的定义语句为:
CREATE TABLE `room` (
`id` int(11) NOT NULL auto_increment,`floor` int(11) default NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `door` (
`id` int(11) NOT NULL auto_increment,
`number` int(11) default NULL,
`room_id` int(11) default NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `room_id` (`room_id`),
KEY `FK2F23AEB8C4C155` (`room_id`),
CONSTRAINT `FK2F23AEB8C4C155` FOREIGN KEY (`room_id`) REFERENCES `room`
(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
package com.it315.domain;import java.util.Set;
public class Teacher {
private int id;
private String name;
private Set<Student> students;get(),set()方法省略......
public String toString() {
return "id=" + this.id + "name=" + this.name + "students="
+ this.getStudents();
}}
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"><hibernate-mapping package="com.it315.domain">
<class name="Teacher" table="Teacher">
这种写法其实是多对一加了一个限制,外键只允许有一个,所以才有一对一的效果。4. 多对多(teacher-student)
○1 Teacher.java
○2 Teacher.hbm.xml
传智博客—赵涵笔记 20
批注 [z28]: 生成表的名字。生成表中有两个字段,一个是 teacher 的 id,一个是 student 的 id.
<id name="id">
<generator class="native" />
</id>
<property name="name" column="teacher_name" /><set name="students" table=" ">
<key column="teacher_id"/>
<many-to-many class="Student" column="student_id"/></set>
</class></hibernate-mapping>
teacher_student
package com.it315.domain;import java.util.Set;
public class Student {
private int id;
private String name;
private Set<Teacher> teachers;get(),set()方法省略......
public String toString() {
return "id=" + this.id + "name=" + this.name;
}}
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"><hibernate-mapping package="com.it315.domain">
<class name="Student" table="Student"><id name="id">
<generator class="native" /></id>
<property name="name" column="student_name"></property><set name="teachers" table="teacher_student">
<key column="student_id" />
<many-to-many class="Teacher" column="teacher_id" /></set>
</class></hibernate-mapping>
○3 Student.java
○4 Student.hbm.xml
○5 Test.java
传智博客—赵涵笔记 21
package com.it315.test;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.Session;
import org.hibernate.Transaction;import com.it315.domain.Student;import com.it315.domain.Teacher;import com.it315.util.HibernateUtils;
public class Test {
public static void main(String[] args) {
add();
select();}
static void add(){
Session session = null;Transaction ts = null;try{
session = HibernateUtils.getSession();ts = session.beginTransaction();
Set<Teacher> set1 = new HashSet<Teacher>();
Teacher teacher1 = new Teacher();teacher1.setName("t1");set1.add(teacher1);
Teacher teacher2 = new Teacher();teacher2.setName("t2");set1.add(teacher2);
Set<Student> set2 = new HashSet<Student>();
Student student1 = new Student();student1.setName("s1");set2.add(student1);
Student student2 = new Student();student2.setName("s2");set2.add(student2);
teacher1.setStudents(set2);teacher2.setStudents(set2);
传智博客—赵涵笔记 22
session.save(teacher1);session.save(teacher2);session.save(student1);session.save(student2);
ts.commit();
}finally{
if(session != null){
session.close();}
}}
static void select(){Session session = null;Transaction ts = null;try{
session = HibernateUtils.getSession();
ts = session.beginTransaction();
Teacher teacher = (Teacher)session.get(Teacher.class, 1);
System.out.println(teacher.getStudents());
}finally{
if(session != null){
session.close();}
}}
}
Hibernate: insert into Teacher (teacher_name) values (?)
Hibernate: insert into Teacher (teacher_name) values (?)
Hibernate: insert into Student (student_name) values (?)
Hibernate: insert into Student (student_name) values (?)
Hibernate: insert into teacher_student (teacher_id, student_id) values(?, ?)
Hibernate: insert into teacher_student (teacher_id, student_id) values(?, ?)
Hibernate: insert into teacher_student (teacher_id, student_id) values(?, ?)
Hibernate: insert into teacher_student (teacher_id, student_id) values
○6 运行结果:
传智博客—赵涵笔记 23
(?, ?)
Hibernate: select teacher0_.id as id0_0_, teacher0_.teacher_name asteacher2_0_0_ from Teacher teacher0_ where teacher0_.id=?
Hibernate: select students0_.teacher_id as teacher1_1_,students0_.student_id as student2_1_, student1_.id as id2_0_,student1_.student_name as student2_2_0_ from teacher_student students0_left outer join Student student1_ on students0_.student_id=student1_.idwhere students0_.teacher_id=?
[id=1name=s1, id=2name=s2]
CREATE TABLE `teacher` (
`id` int(11) NOT NULL auto_increment,`teacher_name` varchar(255) default NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=gbk;
CREATE TABLE `student` (
`id` int(11) NOT NULL auto_increment,`student_name` varchar(255) default NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=gbk;
CREATE TABLE `teacher_student` (
`teacher_id` int(11) NOT NULL,
`student_id` int(11) NOT NULL,
PRIMARY KEY (`student_id`,`teacher_id`),
KEY `FK2E2EF2DE7E54D71F` (`teacher_id`),
KEY `FK2E2EF2DE6DB98C7F` (`student_id`),
CONSTRAINT `FK2E2EF2DE6DB98C7F` FOREIGN KEY (`student_id`) REFERENCES
`student` (`id`),
CONSTRAINT `FK2E2EF2DE7E54D71F` FOREIGN KEY (`teacher_id`) REFERENCES
`teacher` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=gbk;
package com.it315.domain;
public class Name {
private String firstName;private String lastName;get(),set()方法省略......
}
○7 表的定义语句:
5. 组件映射(user-name)○1 Name.java
传智博客—赵涵笔记 24
○2 User.java
○3 User.hbm.xml
批注 [z29]: 说明 name 属性是由firstName 和 lastName 两个属性组成。
package com.it315.domain;
public class User {private int id;private Name name;private int age;get(),set()方法省略......
}
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"><hibernate-mapping package="com.it315.domain">
<class name="User" table="User"><id name="id">
<generator class="native" /></id>
<component name="name">
</component>
<property name="age" column="user_age"></property></class>
</hibernate-mapping>
<property name="firstName" column="first_name" />
<property name="lastName" column="last_name" />
package com.it315.test;
import org.hibernate.Session;
import org.hibernate.Transaction;import com.it315.domain.Name;
import com.it315.domain.User;
import com.it315.util.HibernateUtils;
public class Test {
public static void main(String[] args) {
User user = new User();user.setAge(20);
Name name = new Name();name.setFirstName("zhang");name.setLastName("san");
○4 Test.java
传智博客—赵涵笔记 25
批注 [z30]: 表中会多加一个字段,用来记录保存数据的顺序,读取的时候就会按照这个字段上的值的顺序读取。
user.setName(name);
Session session = null;Transaction tc = null;try{
session = HibernateUtils.getSession();tc = session.beginTransaction();
session.save(user);
tc.commit();}finally{
if(session != null){session.close();
}}
}}
CREATE TABLE `user` (
`id` int(11) NOT NULL auto_increment,`first_name` varchar(255) default NULL,`last_name` varchar(255) default NULL,`user_age` int(11) default NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=gbk;
......
<list name = “employee”>
<key column = “depart_id” />
<one-to-many class = “Employee” /></list>
......
<list-index column = “order_col” />
○5 表的定义语句
讨论映射集合为什么用 set?......
private list employees;......
如果改用 list 的话,读取的时候就会按照保存的顺序读取,例如:
因为我们读取数据时候对读取的顺序并没有要求,所以不必要耗费系统资源用 list 读取。如果想用 list,但又不需要顺序的话,可以这样:
传智博客—赵涵笔记 26
批注 [z31]: Bag 的底层借助 List 实现,但却屏蔽了 List 的有序性。Bag集合为无序集,且允许出现重复元素,是 Hibernate 自定义的集合类型。
批注 [z32]: 表示放弃维护关系,默认是 false.
......
< name = “employee”>
<key column = “depart_id” />
<one-to-many class = “Employee” /></bag>
......
bag
......
......
teacher1.setStudents(set2);teacher2.setStudents(set2);student1.setTeachers(set1);student2.setTeachers(set1);
......
<set name="teachers" table="teacher_student" >
<key column="student_id" />
<many-to-many class="Teacher" column="teacher_id" /></set>
......
inverse="true"
static void save() {
Session session = null;
Transaction tc = null;try {
session = HibernateUtils.getSession();tc = session.beginTransaction();
Department depart = new Department();depart.setName("jishu");session.save(depart);
Set<Employee> set = new HashSet<Employee>();
Inverse和cascade(Employee-Department)○1 在上面“多对多”的例子中,如果:
这样会报 错,发 生主键 冲突。
这时候可 以放弃 学生的 维护关 系,这 样在保 存学生 的时候 就不 会在中 间表中 产生数 据。
One-to-many 维护关联关系就是更新外键;many-to-many 维护关联关系就是在中间表增减记 录。
注意:配置 one-to-one 的对象不维护关联关系。
如果使用 List 的话,为了保持顺序,不能放弃对关系的维护。
○2 在上面“一对多”的例子中,将 Test.java 中的 save()方法改为:
传智博客—赵涵笔记 27
批注 [z33]: 设置级联保存和更新。当保存部门的时候,连同与它有关的其他对象信息一并保存。
Employee emp = new Employee();emp.setAge(20);emp.setName("lisa");emp.setDepart(depart);//session.save(emp);set.add(emp);
Employee emp1 = new Employee();emp1.setAge(21);emp1.setName("zhangsan");emp1.setDepart(depart);//session.save(emp1);set.add(emp1);
System.out.println("-------------------");depart.setEmployee(set);
tc.commit();
} finally {
if (session != null) {
session.close();}
}
<set name="employee" inverse="true"
<key column="depart_id"></key><one-to-many class="Employee" />
</set>
cascade="save-update"
>
package com.it315.domain;
public class Skiller extends Employee {private String skill;get(),set()方法省略......
}
然后在 Department.hbm.xml 文件中:
一般对 many-to-one 和 many-to-many 不设置级联,在 one-to-one 和one-to-many 中设置级联。
继承的映射○1 Skiller.java
传智博客—赵涵笔记 28
○2 Sales.java
○3 Employee.java
批注 [z34]: 普通员工的值是 0.
批注 [z35]: 定义了一个鉴别器,类型为 int 型,列名是 type.
批注 [z36]: 技术员工的值是 1,也可以都不赋值,如果不赋值的话,默认在表的字段中用包名+类名区分。
package com.it315.domain;
public class Sales extends Employee {private int sell;get(),set()方法省略......
}
package com.it315.domain;
public class Employee {private int id;private String name;private int age;
public String toString() {
return "id=" + this.id + "name=" + this.name;
}}
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"><hibernate-mapping
package="com.it315.domain">
<class name="Employee" table="Emlpoyee"
<id name="id">
<generator class="native"/>
</id>
<property name="name" column="employee_name" /><property name="age" column="employee_age" /><subclass name="Skiller"
<property name="skill" /></subclass>
<subclass name="Sales" discriminator-value="2"><property name="sell" />
</subclass>
</class></hibernate-mapping>
>
>
discriminator-value="0"
<discriminator type="int" column="type"/>
discriminator-value="1"
○4 Employee.hbm.xml
传智博客—赵涵笔记 29
○5 Test.java
package com.it315.test;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;import com.it315.domain.Employee;import com.it315.domain.Sales;
import com.it315.domain.Skiller;import com.it315.util.HibernateUtils;
public class Test {
public static void main(String[] args) {
add();
select();}
static void add() {
Session session = null;Transaction tc = null;try {
session = HibernateUtils.getSession();tc = session.beginTransaction();
Employee em1 = new Employee();em1.setAge(20);em1.setName("el1");
Skiller em2 = new Skiller();em2.setAge(30);em2.setName("e2");em2.setSkill("skill");
Sales em3 = new Sales();em3.setAge(40);em3.setName("e3");em3.setSell(15);
session.save(em1);session.save(em2);session.save(em3);
tc.commit();} finally {
if (session != null) {
传智博客—赵涵笔记 30
session.close();}
}}
static void select() {Session session = null;try {
session = HibernateUtils.getSession();String hql = "from Employee";
Query q = session.createQuery(hql);System.out.println(q.list());
} finally {
if (session != null) {
session.close();}
}}
}
Hibernate: insert into Emlpoyee (employee_name, employee_age, type)values (?, ?, 0)
Hibernate: insert into Emlpoyee (employee_name, employee_age, skill,type) values (?, ?, ?, 1)
Hibernate: insert into Emlpoyee (employee_name, employee_age, sell, type)values (?, ?, ?, 2)
Hibernate: select employee0_.id as id0_, employee0_.employee_name asemployee3_0_, employee0_.employee_age as employee4_0_, employee0_.skillas skill0_, employee0_.sell as sell0_, employee0_.type as type0_ fromEmlpoyee employee0_
[id=1name=el1, id=2name=e2, id=3name=e3]
CREATE TABLE `emlpoyee` (
`id` int(11) NOT NULL auto_increment,`type` int(11) NOT NULL,
`employee_name` varchar(255) default NULL,`employee_age` int(11) default NULL,`skill` varchar(255) default NULL,
`sell` int(11) default NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
○6 运行结果:
○7 生成表的定义语句:
传智博客—赵涵笔记 31
因为在技 术和销 售两个 类里可 能会有 多个字 段,所 以放在 一张表 里会对 系统的 优化造 成影响。所以 要将每 个子类 建立一 张表。
Employee.hbm.xml
批注 [z37]: 生成的表名:skiller.
批注 [z38]: 查询有关 skiller 的信息,会根据这个 emp-id 外键字段去查找。
<class name="Employee" table="Emlpoyee"><id name="id">
<generator class="native"/></id>
<property name="name" column="employee_name" /><property name="age" column="employee_age" /><joined-subclass name="Skiller" table=" ">
<key column="
<property name="skill" /></joined-subclass>
<joined-subclass name="Sales" table="sales">
<key column="emp_id" />
<property name="sell" /></joined-subclass>
</class>
skiller
emp_id
Hibernate: insert into Emlpoyee (employee_name, employee_age) values(?, ?)
Hibernate: insert into Emlpoyee (employee_name, employee_age) values(?, ?)
Hibernate: insert into skiller (skill, emp_id) values (?, ?)Hibernate: insert into Emlpoyee (employee_name, employee_age) values(?, ?)
Hibernate: insert into sales (sell, emp_id) values (?, ?)
Hibernate: select employee0_.id as id0_, employee0_.employee_name asemployee2_0_, employee0_.employee_age as employee3_0_,employee0_1_.skill as skill1_, employee0_2_.sell as sell2_, case whenemployee0_1_.emp_id is not null then 1 when employee0_2_.emp_id is notnull then 2 when employee0_.id is not null then 0 end as clazz_ from Emlpoyeeemployee0_ left outer join skiller employee0_1_ onemployee0_.id=employee0_1_.emp_id left outer join sales employee0_2_ onemployee0_.id=employee0_2_.emp_id
[id=1name=el1, id=2name=e2, id=3name=e3]
CREATE TABLE `emlpoyee` (
`id` int(11) NOT NULL auto_increment,`employee_name` varchar(255) default NULL,`employee_age` int(11) default NULL,
" />
运行结果:
这种映射 在效率 上不是 很理想 。生成表的 定义语 句:
传智博客—赵涵笔记
32
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `sales` (
`emp_id` int(11) NOT NULL,
`sell` int(11) default NULL,
PRIMARY KEY (`emp_id`),
KEY `FK682490C46FD469B` (`emp_id`),
CONSTRAINT `FK682490C46FD469B` FOREIGN KEY (`emp_id`) REFERENCES
`emlpoyee` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `skiller` (
`emp_id` int(11) NOT NULL,
`skill` varchar(255) default NULL,
PRIMARY KEY (`emp_id`),
KEY `FK7FFD86BE46FD469B` (`emp_id`),
CONSTRAINT `FK7FFD86BE46FD469B` FOREIGN KEY (`emp_id`) REFERENCES
`emlpoyee` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
<class name="Employee" table="Employee"><id name="id">
<generator class="native" /></id>
<property name="name" column="employee_name"/><property name="age" column="employee_age" /><discriminator column="type" />
<subclass name="Skiller">
<property name="skill" /></subclass>
<subclass name="Sales"><join table="sales">
<key column="employee_id" />
<property name="sell" /></join>
</subclass>
</class>
如果销售 类中有 很多字 段,而 技术类 中只有 很少字 段,那 么需要 混合使 用“一 个类继 承体系 一张表”和“ 每个子 类一张 表”。
懒加载
利用上面 “组件 映射” 那个例 子:
传智博客—赵涵笔记 33
Lazy.java
批注 [z39]: 这里调用了 user 的getAge()方法,那么会去访问数据库,但此时 session 已经关闭,所以这里会抛出代理没有初始化的异常。
package com.it315.test;
import org.hibernate.Session;
import org.hibernate.Transaction;import com.it315.domain.Name;
import com.it315.domain.User;
import com.it315.util.HibernateUtils;
public class lazy {
public static void main(String[] args) {
add();
User user = select();System.out.println( );
}
static void add() {
User user = new User();user.setAge(20);
Name name = new Name();name.setFirstName("zhang");name.setLastName("san");
user.setName(name);
Session session = null;Transaction tc = null;try {
session = HibernateUtils.getSession();tc = session.beginTransaction();
session.save(user);
tc.commit();} finally {
if (session != null) {session.close();
}}
}
static User select() {Session session = null;User user = null;
try {
session = HibernateUtils.getSession();
user.getAge()
传智博客—赵涵笔记 34
批注 [z40]: 调用 load()方法返回的是一个代理对象,里面没有值,只有第一次去调用它的方法的时候才会去数据库里查询。
批注 [z41]: 初始化代理对象,传的参数必须是代 理对象 。
} finally {
if (session != null) {
session.close();}
}
return user;}
}
user
= (User)session.load(User.class, 1);
static User select() {
Session session = null;
User user = null;try {
session = HibernateUtils.getSession();user = (User)session.load(User.class, 1);
;
} finally {
if (session != null) {
session.close();}
}
return user;}
}
Hibernate.initialize(user)
......
<class name="Door" table="door">
<id name="id">
<generator class="foreign">
<param name="property">room</param>
用 load()实现懒加载必须保证 session 没有关闭。Hibernate 提供了一种代理对象初始化的方法:
这时候再 运行上 面那段 程序, 就不会 出现异 常了。
○1 一对一懒加载
必须同时 满足下 面三个 条件, 才能实 现懒加 载。Lazy != false
Constrained = true
Fetch = select
因为查找 room 的时候一般需要一同把 door 的信息列出来,而查找 door 的时候却不一定要把房间的信息列出来,所以在次我将 room 设为懒加载。
Door.hbm.xml
传智博客—赵涵笔记 35
批注 [z42]: 默认设置。使用代理的方式懒加载。
批注 [z43]: Fetch 是设置通过 door 获取 room 的信息时的抓取方式。Join是一条 select 语句全部查询出来,这样无法实现懒加载,所以用 select。必须通过第二条 select 语句才能查询处 room 的信息。(select 是默认配置)
批注 [z44]: Door 不是代理对象,我们设置的 Room 是代理对象,所以我们将 Room 作为参数传进去。
</generator></id>
<property name="number" />
<one-to-one name="room" constrained="true"
/>
</class>......
lazy="proxy"
fetch="select"
package com.it315.test;
import org.hibernate.Hibernate;import org.hibernate.Session;
import com.it315.domain.Door;
import com.it315.util.HibernateUtils;
public class Lazy {
public static void main(String[] args) {
Test.add();
Door door = select();System.out.println(door.getRoom());
}
static Door select() {Session session = null;Door door = null;
try {
session = HibernateUtils.getSession();door = (Door) session.get(Door.class, 1);
;
} finally {
if (session != null) {
session.close();}
}
return door;}
}
Hibernate.initialize(door.getRoom())
Lazy.java
○2 一对多懒加载必须满足 以下两 个条件 :Lazy != falseFetch = select
因为默认的配置就是 lazy =传智博客—赵涵笔记
proxy,fetch =
select,所以配置文件不需要修改。
36
Lazy.java
批注 [z45]: 当确定只有一个结果的时候用 uniqueResult()方法,否则用list().
package com.it315.test;
import org.hibernate.Hibernate;import org.hibernate.Session;
import com.it315.domain.Department;import com.it315.util.HibernateUtils;
public class Lazy {
public static void main(String[] args) {
Test.save();
Department depart = select();System.out.println(depart.getEmployee());
}
static Department select(){
Session session = null;Department depart = null;try{
session = HibernateUtils.getSession();
depart = (Department)session.get(Department.class, 1);Hibernate.initialize(depart.getEmployee());
}finally{
if(session != null){
session.close();}
}
return depart;}
}
static void select(){
Session session = null;
List list = null;try{
session = HibernateUtils.getSession();
String hql = "from Department as depart where depart.name=:n";Query q = session.createQuery(hql);q.setString("n","jishu");
Department depart = (Department) ;
/*
q.uniqueResult()
若不想使用懒加载,只需要在配置文件中将 lazy 的值改为 false,或者将 fetch 的值改为 join.
HQL和Criteria一.最简单 HQL 的查询:
传智博客—赵涵笔记 37
批注 [z46]: 其实索引。
批注 [z47]: 获取最大记录数。
list = q.list();
for(Iterator iter = list.iterator();iter.hasNext();){
Department depart = (Department)iter.next();
System.out.println(depart.getName());}
*/
}finally{
if(session != null){
session.close();}
}}
Query q = session.createQuery(hql);
List list = q.list();
for(Iterator iter = list.iterator();iter.hasNext();){
Object[] rs = (Object[])iter.next();Department d1 = (Department)rs[0];Employee e1 = (Employee)rs[1];
}
setFirstResult(begin)
setMaxResults(count)
;
Criteria crit = session.createCriteria(Department.class);list = crit.list();
for(Iterator iter = list.iterator();iter.hasNext();){
Department depart = (Department)iter.next();
System.out.println(depart.getName());}
Criteria crit = session.createCriteria(Department.class);
○1 如果查询语句是这样:
String hql = “select depart,emp from Department depart,Employee asemp ”+”where depart.id=emp.depart.id and depart.id=:id”;
应当这样 遍历:
查询几个 对象, 数组长 度就是 几。
○2 如果查询语句是这样:
String hql = “from java.lang.Object”;这样的 hql 语句会将所有的对象都查一遍。
○3 分页
q. ;
q.
二 . Criteria○1
○2
传智博客—赵涵笔记 38
crit.add(
crit.add(
crit.addOrder(
list = crit.list();
Restrictions.eq("name","jishu")
);
批注 [z48]: 属性 name 的值必须是jishu.
批注 [z49]: Id 必须在 1 和 10 之间。批注 [z50]: 按照 id 的倒序排列。
Restrictions.between("id", 1, 10)
);
Order.desc("id")
);
crit.setFirstResult(1);crit.setMaxResults(10);
static void test() {
Session session = null;
try {
session = HibernateUtils.getSession();Department depart =
(Department)session.get(Department.class, 1);System.out.println(depart.getEmployee());
System.out.println("----------");
Department depart1 =(Department)session.get(Department.class, 1);
System.out.println(depart1.getEmployee());} finally {
if(session != null){session.close();
}}
}
相当于这样的 SQl 语句:
Select * from Department where name=”jishu” and id between 1 and 10 orderby id desc
○3 分 页
缓存一.一级缓存
○
1
两次查询的都是 id 为 1 的 Department 对象,因为有一级缓存,所以在第一次查询到了以后,将查询到的对象以及 id 值一同保存在 Map 集合中,在第二次执行相同操作的时候,就不会从数据 库里查 询,直接 去一级 缓存中 读取数 据。这也 就是为 什么只 执行了 一个查 询语句 的原因 。
如果在System.out.println("----------");这一句后面添加:session.clear();
这样就把 一级缓 存清除 了,在第 二次查 询具有 相同id的 对象的 时候,一 级缓存 中没有 数据 ,
所以会去 数据库 中查询 ,这样 就出现 了第二 条查询 语句。
如果一级 缓存中 的对象 特别多 ,只想 清空某 一个对 象的缓 存, 可以调 用evict方法 ,传参
数是对象 类型:
传智博客—赵涵笔记 39
session.evict(depart);
注意:save,update,saveOrUpdate,load,get,list,iterate,lock这些方法都将会对象 放在一 级缓存 中,一 级缓存 不能控 制缓存 的数量 ,所以 要注意 大批量 的操作 数据时 可能造成内存 溢出。
○2
static void test() {
Session session = null;
Transaction tc = null;try {
session = HibernateUtils.getSession();tc = session.beginTransaction();Department depart =
(Department)session.get(Department.class, 1);System.out.println(depart.getName());
depart.setName("sell");
tc.commit();} finally {
if(session != null){session.close();
}}
}
......<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">
org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver
</property>
此时对象为持久状态,所以它的值可以修改,并且生成了updaet语句。
如果在depart.setName(“sell”);这句下面清除一级缓存,那么将不会生成update语句,并且表中的字段也不会修改。因为它的更新是根据一级缓存来和数据库进行同步的。调用setName()方法的时候是不会更新数据的,它的属性值全在一级缓存里,只有在提交事务的时候才会更新数据库。这时候如果将一级缓存清掉,hibernate将无法检测到这种属性变化。
一级缓存的作用范围是很小的,当session关闭,那么一级缓存中的数据也不在了。
二.二级缓存
Update,saveorUpdate,list,iterator,get,load,一级Query,Criteria都会填充二 级缓存 。Save这 个 方法 不适合 native生成 方式的 主键 。
○1
hibernate.cfg.xml
传智博客—赵涵笔记 40
<property name="hibernate.connection.url">jdbc:mysql:///zhaohan
</property>
<property name="hibernate.connection.username">root</property><property name="hibernate.connection.password">123</property><property name="hbm2ddl.auto">create</property>
<property name="show_sql">true</property>
<property name="cache.use_second_level_cache"> </property>
<property name="cache.provider_class">
</property>
<mapping resource="com/it315/domain/Department.hbm.xml" /><mapping resource="com/it315/domain/Employee.hbm.xml" />
<class-cache usage=" "
</session-factory></hibernate-configuration>
true
批注 [z51]: True 为默认值,打开二级缓存。(Hiberna te.pr oper ties文件中第 479 行。)
批注 [z52]: 表示用HashtableCacheProvider 这个缓存实现。(Hibernate.properties 文件中第 494 行提供了多种缓存实现。)
批注 [z53]: 说明缓存中的数据是只读的,不能更改,这样就不需要手工清除缓存;
usage=" read- write "说明放入缓存中的数据是可读可写的。
批注 [z54]: 表示对哪个对象实现二级缓存。
org.hibernate.cache.HashtableCacheProvider
read-write
/>
class="com.it315.domain.Department"
static void cache(){
Session session = HibernateUtils.getSession();
Department depart = (Department)session.get(Department.class,
1);
1);
System.out.println(depart.getName());session.close();
System.out.println("---------------");
Session session1 = HibernateUtils.getSession();
Department depart1 = (Department)session1.get(Department.class,
System.out.println(depart1.getName());
session1.close();}
Hibernate: select department0_.id as id0_0_,department0_.department_name as department2_0_0_ from Departmentdepartment0_ where department0_.id=?
jishu
---------------
jishu
Test.java
运行结果:
传智博客—赵涵笔记 41
两次查询 都是查 询同一id 的同一 对象, 第二次 查询默 认会 去一级 缓存中 查找, 这时候 发现session已经关闭,一级缓存中没有数据,然后会去二级缓存中查找。(二级缓存中没有才会去数据 库中查 找)发 现二级 缓存中 有这个 对象, 所以只 出现 了一条select 语句。
○2HibernateUtils.java
批注 [z55]: 打开统计信息,默认为true.
package com.it315.util;
import org.hibernate.Session;
import org.hibernate.SessionFactory;import org.hibernate.cfg.Configuration;
public final class HibernateUtils {
private static SessionFactory sessionFactory;static {
Configuration cfg = new Configuration();
sessionFactory = cfg.configure().buildSessionFactory();
}
public static SessionFactory getSessionFactory(){
return sessionFactory;}
public static Session getSession(){return sessionFactory.openSession();
}}
......
<property name="cache.use_second_level_cache">true</property><property name="cache.provider_class">
org.hibernate.cache.HashtableCacheProvider</property>
<property name="generate_statistics"> </property>
<class-cache usage="read-write"class="com.it315.domain.Employee" />
<class-cache usage="read-only"class="com.it315.domain.Department" />
......
true
public class Test {
public static void main(String[] args) {
save();cache();
Hibernate.cfg.xml
Test.java
传智博客—赵涵笔记 42
批注 [z56]: 得到统计信息。(只统计二级缓存,不统计一级缓存)
Statistics s =
System.out.println("往二级缓存中放了"+s.getSecondLevelCachePutCount()+"次");
;
System.out.println("命中了"+s.getSecondLevelCacheHitCount()+" 次
");
System.out.println(s.getSecondLevelCacheMissCount()+"次在缓存中没有找到");
}
static void save() {
Session session = null;
Transaction tc = null;try {
session = HibernateUtils.getSession();tc = session.beginTransaction();
Department depart = new Department();depart.setName("jishu");session.save(depart);
Set<Employee> set = new HashSet<Employee>();
Employee emp = new Employee();emp.setAge(20);emp.setName("lisa");emp.setDepart(depart);set.add(emp);
Employee emp1 = new Employee();emp1.setAge(21);emp1.setName("zhangsan");emp1.setDepart(depart);set.add(emp1);
depart.setEmployee(set);
tc.commit();
} finally {
if (session != null) {
session.close();}
}
HibernateUtils.getSessionFactory().getStatistics()
传智博客—赵涵笔记 43
}
static void cache(){
Session session = HibernateUtils.getSession();
Department depart = (Department)session.get(Department.class,
1);
1);
}
System.out.println(depart.getName());
session.close();
System.out.println("---------------");
Session session1 = HibernateUtils.getSession();
Department depart1 = (Department)session1.get(Department.class,
System.out.println(depart1.getName());session1.close();
Hibernate: insert into Department (department_name) values (?)Hibernate: insert into Emlpoyee (employee_name, employee_age, depart_id)values (?, ?, ?)
Hibernate: insert into Emlpoyee (employee_name, employee_age, depart_id)values (?, ?, ?)
Hibernate: select department0_.id as id0_0_,department0_.department_name as department2_0_0_ from Departmentdepartment0_ where department0_.id=?
jishu
---------------
jishu
往二级缓 存中放 了1次
批注 [z57]: 这一次是第一回查询Department 对象的时候,将查询到的department 放进缓存中。
批注 [z58]: 第二次查找 Department对象的时候是从二级缓存中查找到的。
批注 [z59]: 第一次查询 Department对象的时候会去缓存中查找,没有查找到,所以去数据库中查找的。
批注 [z60]: 保存一个部门和两个员工信息的时候放进二级缓存,共放了三次。
批注 [z61]: 两次查找部门信息都是从二级缓存中读取,所以命中两次,没有生成 select 语句。
命中了1次
1次在缓存 中没有 找到
jishu---------------jishu
往二级缓 存中放 了3次
命中了2次
运行结果是:
查看命中次数 有助于 对程序 的优化,如果二 级缓存 中保存 的对象 有很多 ,而命 中率却 很少的话,那 就浪费 了资源 。
执行save操作的时候,是会把保存的对象放进二级缓存中的,因为save方法是否将对象放进二级缓存是和主键生成策略有关的,如果是native的话,就不会把对象放进二级缓存。上例中Department和Employee两个主键生成方式都是native,所以在保存两个对象的时候不会放进二级缓存中。
如果将两 个对象 的主键 生成方 式都改 成hilo,那 么执行 的结 果为:
传智博客—赵涵笔记 44
批注 [z62]: 在缓存中查找数据都查找到了,所以没有 miss.
0次在缓存 中没有 找到
static void cache(){
Session session = HibernateUtils.getSession();
Department depart = (Department)session.get(Department.class,
1);
1);
System.out.println(depart.getName());session.close();
System.out.println("---------------");
Session session1 = HibernateUtils.getSession();
Department depart1 = (Department)session1.get(Department.class,
System.out.println(depart1.getName());session1.close();
Session session2 = HibernateUtils.getSession();
String hql = "from Employee emp where emp.id = 32768";Query q = session2.createQuery(hql);
Employee em = (Employee)q.uniqueResult();System.out.println(em.getAge());
session2.close();}
jishu
---------------
jishu
Hibernate: select employee0_.id as id1_, employee0_.employee_name asemployee2_1_, employee0_.employee_age as employee3_1_,employee0_.depart_id as depart4_1_ from Emlpoyee employee0_ whereemployee0_.id=32768
20
往二级缓 存中放 了3次
命中了2次
有三个方法会从二级缓存中读取数据:iterator,get,load。Iterator方法指的是Query的 iterator方法 。例如:
Iterator iter = query.iterate();Iter.next();
使用下面 例子进 行说明 :
○1 HQL查询
运行结果:
传智博客—赵涵笔记 45
批注 [z63]: 这个信息是从缓存中查询的。
0次在缓存 中没有 找到
static void cache(){
Session session = HibernateUtils.getSession();String hql = "from Employee";
Query q = session.createQuery(hql);
Iterator iter = q.iterate();while(iter.hasNext()){
Employee em = (Employee)iter.next();
System.out.println(em.getName());}
session.close();System.out.println("----------------");
Session session1 = HibernateUtils.getSession();
Employee em = (Employee)session1.get(Employee.class, 32768);System.out.println(em.getAge());
session1.close();}
Hibernate: select employee0_.id as col_0_0_ from Emlpoyee employee0_Hibernate: select employee0_.id as id1_0_, employee0_.employee_name asemployee2_1_0_, employee0_.employee_age as employee3_1_0_,employee0_.depart_id as depart4_1_0_ from Emlpoyee employee0_ whereemployee0_.id=?
lisa
Hibernate: select employee0_.id as id1_0_, employee0_.employee_name asemployee2_1_0_, employee0_.employee_age as employee3_1_0_,employee0_.depart_id as depart4_1_0_ from Emlpoyee employee0_ whereemployee0_.id=?
zhangsan
----------------
往二级缓 存中放 了2次命中了1次
2次在缓存 中没有 找到
20
HQL查询会生成select语句从数据库中查找,不会去二级缓存中去查找。所以建议在根据id查找数 据的时 候要用get 方法。
○2 iterator方法
运行结果:
这样如果 有N个对 象,那 么就会 出现N+1条 查询语 句。如 果缓 存中没 有对象 但遍历 的对象 又很多的情 况下,用iterator 方法的 话并不 是很好,但若是 第二次 查询的 话,会把 系统优 化很多 。
查询缓存:
传智博客—赵涵笔记 46
上面例一中,HQL查询不会去缓存中查找,我们可以通过配置使Query和Criteria都从缓存中查 找,但是因 为命 中率较 低,所以默 认配置 是关闭 的。(hibernate.properties 文件中第484行)
通过Query或Criteria 查找数 据库得 到的结 果集,hibernate 会把 查询语 句当作key,把返回结 果的id做 为value存到 缓存里 去。那 么第二 次执 行相 同 查询语 句 的时候,发现缓 存里有相关信 息,那么 就会拿 到缓存 中所有id 的集合,然后再 根据id去 一级缓 存和二 级缓存 中去找 ,找不到就 去数据 库中查 找。
将cache.use_query_cache配置为true打开查询缓存,并且调用query.setCacheable(true)或criteria.setCacheable(true)。
注意:查 询缓存 不是一 级缓存 ,也不 是二级 缓存。 如果没 有十 足把握 ,不要 打开查 询缓存,这样 会对性 能有严 重影响 。
清除二级缓存:
批注 [z64]: 将缓存中的所有Department 对象全部清除。如果只想清除 id 为 1 的 Department 对象,只需要:
evict(D epart ment. clas s,1)
static void cache(){
Session session = HibernateUtils.getSession();
Department depart = (Department)session.get(Department.class,
1);
1);
}
System.out.println(depart.getName());session.close();
System.out.println("---------------");
Session session1 = HibernateUtils.getSession();
Department depart1 = (Department)session1.get(Department.class,
System.out.println(depart1.getName());session1.close();
HibernateUtils.getSessionFactory().evict(Department.class);
jishu
---------------
Hibernate: select department0_.id as id0_0_,department0_.department_name as department2_0_0_ from Departmentdepartment0_ where department0_.id=?
jishu
往二级缓 存中放 了4次
命中了1次
1次在缓存 中没有 找到
.......
运行结果:
换OSCacheProvider缓存实现:
传智博客—赵涵笔记 47
批注 [z65]: “sql”参数为配置文件中的命名查询名称。
<property name="cache.provider_class">
org.hibernate.cache.OSCacheProvider
</property>
......
......
<class name="Department" table="Department"><id name="id">
<generator class="hilo" /></id>
<property name="name" column="department_name"></property><set name="employee" inverse="true" cascade="save-update">
<key column="depart_id"></key>
<one-to-many class="Employee" /></set>
</class>
<query name="sql">
from Department depart where depart.id = 1
</query>......
static void cache(){
Session session = HibernateUtils.getSession();Query q = session. ;
Department depart = (Department)q.uniqueResult();System.out.println(depart.getName());session.close();
}
getNamedQuery("sql")
for(int i=0;i<1000000;i++){
只需要将原来的缓存实现换成OSCacheProvider,并且把OSCacheProvider包oscache-2.1.jar导入。
使用缓存的条件:
1. 读取大于修改
2. 数据量不能超过内存容量3. 对数据要有独享的控制4. 可以容忍出现无效数据
将查询语句写在配置文件中
注意大批量操作的问题
大批量的 操作数 据时可 能造成 内存溢 出,解 决办法 如下:
1. 清除session中的数据
传智博客—赵涵笔记 48
批注 [z66]: 只能出现在字段上,不能出现在方法上.
批注 [z67]: 默认设置是 CLASS,但设置成 RUNTIME 才能获取到这个Annotation.
Session.save(obj);if(i%50==0){
session.flush();
session.clear();}
}
package com.it315.jpa;
import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;
@Retention(RetentionPolicy.public @interface HelloWorld {}
@Target(ElementType.FIELD)
RUNTIME
)
package com.it315.jpa;public class TestBean {
@HelloWorld
private String name;private int age;get(),set()方法省略......
}
package com.it315.jpa;
import java.lang.annotation.Annotation;import java.lang.reflect.Field;
public class Test {
public static void main(String[] args) throws SecurityException,
每当有50条数据的 时候, 就和数 据库同 步一次 ,然 后清除 一级缓 存中的 对象。
2. 用StatelessSession接口,它没有一级缓存,也不和二级缓存,查询缓存交互,通过该接口的操作会立即发送给数据库,与JDBC的功能一样。
StatelessSession s = sessionFactory.openStatelessSession();该接口的方法和session类似。
Annotation
○1 HolloWorld.java
@Target({ElementType.METHOD,ElementType.FIELD})既可以出现在字段上,也可以出 现在方 法上。 里面是 一个数 组,要 中花括 号括起 来。
○2 Bean.java
○3 Test.java
传智博客—赵涵笔记 49
NoSuchFieldException {
test();}
static void test() throws SecurityException, NoSuchFieldException {Class = TestBean.class;
Field
Boolean b = field.
if (b) {
Annotation annotation = field. ;
System.out.println(annotation.getClass());} else {
System.out.println("没有这个Annota tion");}
}}
clazz
批注 [z68]: 得到的Class就是Bean类在硬盘上的描述信息,连同他上面的注解一同可以得到。
批注 [z69]: Name字段的信息全部封装在Field对象里去了,包括字段上的注解。
批注 [z70]: 看 name 字段上是否有helloWorld 这个 Annotation.
批注 [z71]: 得到 HelloWorld 这个Annotation.
field
= clazz.getDeclaredField("name");
isAnnotationPresent(HelloWorld.class)
;
getAnnotation(HelloWorld.class)
package com.it315.jpa;
import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)public @interface Notnull {
}
package com.it315.jpa;
public class TestBean {@Notnull
private String name;private int age;get(),set()方法省略......
}
package com.it315.jpa;
import java.lang.annotation.Annotation;import java.lang.reflect.Field;
public class Test {
public static void main(String[] args) throws SecurityException,
例:不允许name字段为空○1 Notnull.java
○2 Bean.java
○3 Test.java
传智博客—赵涵笔记 50
NoSuchFieldException {
Bean tb = new Bean();checkNull(tb);
}
static void checkNull(TestBean tb) throws SecurityException,NoSuchFieldException {
Class clazz = TestBean.class;
Field field = clazz.getDeclaredField("name");if(field.isAnnotationPresent(Notnull.class)){
String name = tb.getName();if(name == null){
throw new RuntimeException("名字不得为空!");}
}}
}
Exception in thread "main" java.lang.RuntimeException: 名字不得为空!at com.it315.jpa.Test.checkNull(Test.java:34)
at com.it315.jpa.Test.main(Test.java:13)
○4 运行结果(name并为赋值):
传智博客—赵涵笔记 51