Hibernate3.3
一、第一个Demo
先将需要的jar包导入
User:
package cn.itcast.hibernate.domain;
import java.util.Date;
public class User {
private int id;
private String name;
private Date birthday;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
User.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 package="cn.itcast.hibernate.domain">
<class name="User" table="user">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native" />
</id>
<property name="name" column="name" type="java.lang.String"/>
<property name="birthday" column="birthday" type="java.util.Date"/>
</class>
</hibernate-mapping>
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="hbm2ddl.auto">update</property> <!-- 在properties中存在4中,即create-drop/create/update/validate -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/liupeng</property>
<property name="connection.username">root</property>
<property name="connection.password">mysqladmin</property>
<property name="myeclipse.connection.profile">Mysql</property>
<property name="show_sql">true</property> <!-- 显示SQL语句 -->
<mapping resource="cn/itcast/hibernate/domain/User.hbm.xml"/>
</session-factory>
</hibernate-configuration>
HIbernateUtil:
package cn.itcast.hibernate.util;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
/**
* 取得Session或者SessionFactory
* @author liupeng
*
*/
public final class HibernateUtil { //此类不可以被继承
private HibernateUtil(){} //此类不可以在其他地方被new
private static SessionFactory sessionFactory;
static{ //static代码块中的代码只被运行一次,全局
Configuration cfg = new Configuration();
cfg.configure(); //如果名字不是hibernate.cfg.xml,需要在方法中说明
sessionFactory = cfg.buildSessionFactory();
}
//获得SessionFactory,类似于DriverManager
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
//获得Session,类似于Connection
public static Session getSession(){
return sessionFactory.openSession();
}
}
Base:
package cn.itcast.hibernate.test;
import java.util.Date;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import cn.itcast.hibernate.domain.User;
public class Base {
public static void main(String[] args) {
User user = new User();
user.setBirthday(new Date());
user.setName("刘鹏");
addUser(user); //增加
user.setId(3);
deleteUserById(user); //根据ID删除
User u = getUserById(3); //根据ID查询
System.out.println("名称为:"+u.getName());
u.setName("小飞");
updateUser(u); //根据更新
}
//插入
static void addUser(User user){
Session s = null;
Transaction tx = null;
try {
s = HibernateUtil.getSession();
tx = s.beginTransaction();
s.save(user);
tx.commit();
} catch (HibernateException e) {
if(tx!=null){
tx.rollback();
throw e; //一定要将异常抛出,否则什么错误也看不见
}
}finally{
if(s!=null){
s.close();
}
}
}
//根据主键ID删除
static void deleteUserById(User user) {
Session s = null;
Transaction tx = null;
try {
s = HibernateUtil.getSession();
tx = s.beginTransaction();
s.delete(user);
tx.commit();
} catch (HibernateException e) {
if(tx!=null){
tx.rollback();
throw e;
}
}finally{
if(s!=null){
s.close();
}
}
}
//更新
static void updateUser(User user){
Session s = null;
Transaction tx = null;
try {
s = HibernateUtil.getSession();
tx = s.beginTransaction();
s.update(user);
tx.commit();
} catch (HibernateException e) {
if(tx!=null){
tx.rollback();
throw e;
}
}finally{
if(s!=null){
s.close();
}
}
}
//根据主键ID查询
static User getUserById(int id) {
Session s = null;
User user = null;
try {
s = HibernateUtil.getSession();
//User.class会反射出包.类名称,对应查找映射文件,找到数据库中的表
user = (User) s.get(User.class, id);
} catch (HibernateException e) {
System.out.println("查询出错");
}finally{
if(s!=null){
s.close();
}
}
return user;
}
}
二、开发流程
1、由Domain object -> mapping->db。(官方推荐)
2、由DB开始,用工具生成mapping和Domain object。(使用较多)
3、由映射文件开始。
Domain Object的限制
1、默认的构造方法(必须的)。
2、有无意义的标示符id(主键)(可选)
3、非final的,对懒加载有影响(可选)
Session的几个主要方法:
1.save,persist保存数据,persist在事务外不会产生insert语句。
2.delete,删除对象
3.update,更新对象,如果数据库中没有记录,会出现异常。
4.get,根据ID查,会立刻访问数据库。
5.Load,根据ID查,(返回的是代理,不会立即访问数据库)。
6.saveOrUpdate,merge(根据ID和version的值来确定是save或update),调用merge你的对象还是托管的。
7.lock(把对象变成持久对象,但不会同步对象的状态)。
对象状态:
瞬时(transient):数据库中没有数据与之对应,超过作用域会被JVM垃圾回收器回收,一般是new出来且与session没有关联的对象。
持久(persistent):数据库中有数据与之对应,当前与session有关联,并且相关联的session没有关闭,事务没有提交;持久对象状态发生改变,在事务提交时会影响到数据库(hibernate能检测到)。
脱管(detached):数据库中有数据与之对应,但当前没有session与之关联;托管对象状态发生改变,hibernate不能检测到。
三、完善HibernateUtil类及hql查询入门
一、完善HibernateUtil类
User/User.hbm.xml/hibernate.cfg.xml同(一)
HibernateUtil:
public final class HibernateUtil { //此类不可以被继承
private HibernateUtil(){} //此类不可以在其他地方被new
private static SessionFactory sessionFactory;
static{ //static代码块中的代码只被运行一次,全局
Configuration cfg = new Configuration();
cfg.configure(); //如果名字不是hibernate.cfg.xml,需要在方法中说明
sessionFactory = cfg.buildSessionFactory();
}
//获得SessionFactory,类似于DriverManager
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
//获得Session,类似于Connection
public static Session getSession(){
return sessionFactory.openSession();
}
//所有的增加操作都可以使用
public static void add(Object entity){
Session s = null;
Transaction tx = null;
try {
s = getSession();
tx = s.beginTransaction();
s.save(entity);
tx.commit();
}finally{
if(s!=null){
s.close();
}
}
}
//所有的删除操作都可以使用(目前发现根据ID删除)
public static void delete(Object entity){
Session s = null;
Transaction tx = null;
try {
s = getSession();
tx = s.beginTransaction();
s.delete(entity);
tx.commit();
}finally{
if(s!=null){
s.close();
}
}
}
//所有的更新操作都可以使用
public static void update(Object entity){
Session s = null;
Transaction tx = null;
try {
s = getSession();
tx = s.beginTransaction();
s.update(entity);
tx.commit();
}finally{
if(s!=null){
s.close();
}
}
}
//所有的根据ID查询都可以使用
public static Object getUserById(Class classz,Serializable id) {
Session s = null;
Object o = null;
try {
s = HibernateUtil.getSession();
o = (Object) s.get(classz, id);
} catch (HibernateException e) {
System.out.println("查询出错");
}finally{
if(s!=null){
s.close();
}
}
return o;
}
}
Base:
User user = new User(); //在初始化的时候User的ID已经自动赋值
user.setBirthday(new Date());
user.setName("刘鹏的夫人");
HibernateUtil.add(user);
二、HQL(Hibernate Query Language)
1、HQL中的对象名是区分大小写的(除了JAVA类和属性其他部分不区分大小写)
2、HQL中查的是对象而不是和表,并且支持多态
3、HQL主要通过Query来操作
User/User.hbm.xml/hibernate.cfg.xml同(一)HibernateUtil同(三)
QueryTest:
public class QueryTest {
public static void main(String[] args) {
query("刘鹏");
}
static void query(String name){
Session s = null;
try {
s = HibernateUtil.getSession();
//User为对象名称
String hql = "from User as user where user.name=?";
Query query = s.createQuery(hql);
query.setString(0, name);
//Object obj = query.uniqueResult(); //确定返回结果只有一个
List<User> list = query.list();
for(User user : list){
System.out.println(user.getName()+" "+user.getBirthday());
}
}finally{
if(s!=null){
s.close();
}
}
}
}
四、hql的命名参数与Query接口的分页查询
Hql命名参数:
String hql = "from User as user where user.name=:n";
Query query = s.createQuery(hql);
query.setString("n", name);
Hql实现分页查询:
String hql = "from User as user where user.name=:n";
Query query = s.createQuery(hql);
query.setString("n", name);
//实现分页
query.setFirstResult(0);//第一条记录从哪里开始
query.setMaxResults(2);//取多少条
List<User> list = query.list();
五、Criteria查询方式
CriteriaTest:
public class CriteriaTest {
public static void main(String[] args) {
criteriaQuery("刘鹏");
}
static void criteriaQuery(String name){
Session s = null;
try {
s = HibernateUtil.getSession();
Criteria cri = s.createCriteria(User.class);
cri.add(Restrictions.eq("name",name)); //名称相等约束
cri.add(Restrictions.lt("birthday",new Date()));//时间小于当前时间约束
cri.setFirstResult(0);
cri.setMaxResults(2);
List<User> list = cri.list();
for(User user : list){
System.out.println(user.getName()+" "+user.getBirthday());
}
}finally{
if(s!=null){
s.close();
}
}
}
}
六、Hibernate完整CRUD
User:
package cn.itcast.domain;
import java.util.Date;
public class User {
private int id;
private String name;
private Date birthday;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
User.hbm.xml:
<hibernate-mapping package="cn.itcast.domain">
<class name="User" table="user">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native" />
</id>
<property name="name" column="name" type="java.lang.String"/>
<property name="birthday" column="birthday" type="java.util.Date"/>
</class>
</hibernate-mapping>
Hibernate.cfg.xml:
<hibernate-configuration>
<session-factory>
<property name="hbm2ddl.auto">update</property> <!-- 在properties中存在4中,即create-drop/create/update/validate -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/liupeng</property>
<property name="connection.username">root</property>
<property name="connection.password">mysqladmin</property>
<property name="myeclipse.connection.profile">Mysql</property>
<property name="show_sql">true</property> <!-- 显示SQL语句 -->
<mapping resource="cn/itcast/domain/User.hbm.xml"/>
</session-factory>
</hibernate-configuration>
HibernateUtil:
public final class HibernateUtil {
private HibernateUtil(){}
private static SessionFactory sessionFactory;
static{
Configuration cfg = new Configuration();
cfg.configure();
sessionFactory = cfg.buildSessionFactory();
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
public static Session getSession(){
return sessionFactory.openSession();
}
public static void add(Object entity){
Session s = null;
Transaction tx = null;
try {
s = getSession();
tx = s.beginTransaction();
s.save(entity);
tx.commit();
}finally{
if(s!=null){
s.close();
}
}
}
public static void delete(Object entity){
Session s = null;
Transaction tx = null;
try {
s = getSession();
tx = s.beginTransaction();
s.delete(entity);
tx.commit();
}finally{
if(s!=null){
s.close();
}
}
}
public static void update(Object entity){
Session s = null;
Transaction tx = null;
try {
s = getSession();
tx = s.beginTransaction();
s.update(entity);
tx.commit();
}finally{
if(s!=null){
s.close();
}
}
}
public static Object getUserById(Class classz,Serializable id) {
Session s = null;
Object o = null;
try {
s = HibernateUtil.getSession();
o = (Object) s.get(classz, id);
} catch (HibernateException e) {
System.out.println("查询出错");
}finally{
if(s!=null){
s.close();
}
}
return o;
}
}
UserDao:
public interface UserDao {
public void saveUser(User user);
public List<User> findUserByName(String name);
public User findUserById(int id);
public void updateUser(User user);
public void remove(User user);
}
UserDaoImpl:
public class UserDaoImpl implements UserDao {
@Override
public void saveUser(User user) {
Session s = null;
Transaction tx = null;
try {
s = HibernateUtil.getSession();
tx = s.beginTransaction();
s.save(user);
tx.commit();
}finally{
if(s!=null){
s.close();
}
}
}
@Override
public List<User> findUserByName(String name) {
Session s = null;
List<User> list = null;
try {
// s = HibernateUtil.getSession();
// String hql = "from User as user where user.name=?";
// Query query = s.createQuery(hql);
// query.setString(0, name);
// List<User> list = query.list();
s= HibernateUtil.getSession();
Criteria cri = s.createCriteria(User.class);
cri.add(Restrictions.eq("name", name));
list = cri.list();
}finally{
if(s!=null){
s.close();
}
}
return list;
}
@Override
public User findUserById(int id) {
Session s = null;
User user = null;
try {
s = HibernateUtil.getSession();
user = (User)s.get(User.class, id);
System.out.println(user.getName());
}finally{
if(s!=null){
s.close();
}
}
return user;
}
@Override
public void updateUser(User user) {
Session s = null;
Transaction tx = null;
try {
s = HibernateUtil.getSession();
tx = s.beginTransaction();
s.update(user);
tx.commit();
}finally{
if(s!=null){
s.close();
}
}
}
@Override
public void remove(User user) {
Session s = null;
Transaction tx = null;
try {
s = HibernateUtil.getSession();
tx = s.beginTransaction();
s.delete(user);
tx.commit();
}finally{
if(s!=null){
s.close();
}
}
}
}
DaoTest:
public class DaoTest {
public static void main(String[] args) {
User user = new User();
user.setName("刘鹏");
user.setBirthday(new Date());
UserDao userDao = (UserDao)new UserDaoImpl();
userDao.saveUser(user);
List<User> list = userDao.findUserByName("刘鹏");
for(User u : list){
System.out.println(u.getName());
}
user = list.iterator().next();
user.setName("小鹏");
userDao.updateUser(user);
user.setId(6);
userDao.remove(user);
}
}
七、关联映射
多对一(Employee - Department)
一对多(Department-Employee)
一对一(Person - idCard)
多对多(teacher - student)
组件映射(User-Name)
集合映射(set, list, map, bag)
inverse和cascade(Employee – Department)
1、 多对一(Employee->Department)
Department:
private int id;(set/get)
private String name;(set/get)
Employee:
private int id; (set/get)
private String name; (set/get)
private Department department; (set/get)
Department.hbm.xml:
<hibernate-mapping package="cn.itcast.hibernate.domain">
<class name="Department" table="department">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native" />
</id>
<property name="name" column="name" type="java.lang.String"/>
</class>
</hibernate-mapping>
Employee.hbm.xml:
<hibernate-mapping package="cn.itcast.hibernate.domain">
<class name="Employee" table="employee">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native" />
</id>
<property name="name" column="name" type="java.lang.String"/>
<!--多对一,property-ref="id",缺省就是与主键ID比较,反射 -->
<many-to-one name="department" column="department_id"></many-to-one>
</class>
</hibernate-mapping>
hibernate.cfg.xml:
<hibernate-configuration>
<session-factory>
<property name="hbm2ddl.auto">update</property> <!-- 在properties中存在4中,即create-drop/create/update/validate -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/liupeng</property>
<property name="connection.username">root</property>
<property name="connection.password">mysqladmin</property>
<property name="myeclipse.connection.profile">Mysql</property>
<property name="show_sql">true</property> <!-- 显示SQL语句 -->
<mapping resource="cn/itcast/hibernate/domain/Department.hbm.xml"/>
<mapping resource="cn/itcast/hibernate/domain/Employee.hbm.xml"/>
</session-factory>
</hibernate-configuration>
HibernateUtil省略
Many2oneTest:
public class Many2one {
public static void main(String[] args) {
System.out.println(add().getName());
System.out.println(queryById(1).getDepartment().getName());
}
static Department add(){
Session s = null;
Transaction tx = null;
Department department = null;
Employee employee = null;
try {
department = new Department();
department.setName("学生会");
//department.setId(2);
employee = new Employee();
employee.setName("刘鹏");
employee.setDepartment(department);
s = HibernateUtil.getSession();
tx = s.beginTransaction();
s.save(department);
//当插入Department信息后,便存在了id,所以Employee中的Department便有了id,故而插入Employee不会出错,主要判断插入成功与否的依据就是Employee中的属性department是否有id
s.save(employee);
tx.commit();
}finally{
if(s!=null){
s.close();
}
}
return department;
}
}
//查询
static Employee queryById(int empId){
Session s = null;
Employee employee = null;
try {
s = HibernateUtil.getSession();
employee = (Employee)s.get(Employee.class, empId);
//在Session未关闭前可以使用System.out.println(employee.getDepartment().getName());关闭后如果要在其他地方访问department中的属性必须加上下面这句话
Hibernate.initialize(employee.getDepartment());
}finally{
if(s!=null){
s.close();
}
}
return employee;
}
2、一对多 (Department ->Employee)
Department:
private int id;
private String name;
private Set<Employee> emps;//一对多
Employee:
private int id;
private String name;
private Department department;//多对一
Department.hbm.xml:
<hibernate-mapping package="cn.itcast.hibernate.domain">
<class name="Department" table="department">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native" />
</id>
<property name="name" column="name" type="java.lang.String"/>
<!—根据department表中的主键ID到employee表中与department_id匹配 -->
<set name="emps">
<key column="department_id"/>
<one-to-many class="Employee"/>
</set>
</class>
</hibernate-mapping>
Employee.hbm.xml:
<hibernate-mapping package="cn.itcast.hibernate.domain">
<class name="Employee" table="employee">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native" />
</id>
<property name="name" column="name" type="java.lang.String"/>
<many-to-one name="department" column="department_id"></many-to-one>
</class>
</hibernate-mapping>
HibernateUtil/hibernate.cfg.xml同上
One2ManyTest:
增加函数的SQL语句:
Hibernate: insert into employee (name, department_id) values (?, ?)
Hibernate: insert into employee (name, department_id) values (?, ?)
Hibernate: insert into department (name) values (?)
Hibernate: update employee set name=?, department_id=? where id=?
Hibernate: update employee set name=?, department_id=? where id=?
public class Many2oneAndOnetoMany {
public static void main(String[] args) {
System.out.println(add().getName());
System.out.println(queryById(1).getDepartment().getName());
queryByIdForDepartment(1);
}
static Department add(){
Session s = null;
Transaction tx = null;
Department department = null;
Employee employee1 = null;
Employee employee2 = null;
try {
department = new Department();
department.setName("学生会");
employee1 = new Employee();
employee1.setName("刘鹏");
employee1.setDepartment(department);
employee2 = new Employee();
employee2.setName("小刘");
employee2.setDepartment(department);
Set<Employee> emps = new HashSet<Employee>();
emps.add(employee1);
emps.add(employee2);
department.setEmps(emps);
s = HibernateUtil.getSession();
tx = s.beginTransaction();
s.save(employee1);
s.save(employee2);
s.save(department);
tx.commit();
}finally{
if(s!=null){
s.close();
}
}
return department;
}
//查询员工
static Employee queryById(int empId){
Session s = null;
Employee employee = null;
try {
s = HibernateUtil.getSession();
employee = (Employee)s.get(Employee.class, empId);
//在Session未关闭前可以使用System.out.println(employee.getDepartment().getName());,关闭后如果要在其他地方访问department中的属性必须加上下面这句话
Hibernate.initialize(employee.getDepartment());
}finally{
if(s!=null){
s.close();
}
}
return employee;
}
//查询部门
static Department queryByIdForDepartment(int depId){
Session s = null;
Department dep = null;
try {
s = HibernateUtil.getSession();
dep = (Department)s.get(Department.class, depId);
System.out.println("emps:"+dep.getEmps());
Hibernate.initialize(dep.getEmps());
}finally{
if(s!=null){
s.close();
}
}
return dep;
}
}
3、一对一 (Person-IdCard)
(一)基于主键的one-to-one
idCard中的主键ID来自于Person的主键ID,故而idCard中的ID也是外键
Person:
private int id;
private String name;
private IdCard idcard;
IdCard:
private int id;
private Date userfulLife;
private Person person;
Person.hbm.xml:
<hibernate-mapping package="cn.itcast.hibernate.domain">
<class name="Person" table="person">
<id name="id" column="id" type="java.lang.Integer">
<!-- id是自增长的 -->
<generator class="native" />
</id>
<property name="name" column="name" type="java.lang.String"/>
<one-to-one name="idCard"/>
</class>
</hibernate-mapping>
IdCard.hbm.xml:
<hibernate-mapping package="cn.itcast.hibernate.domain">
<class name="IdCard" table="idcard">
<id name="id" column="id" type="java.lang.Integer">
<!-- id是外键,来自于person -->
<generator class="foreign">
<param name="property">person</param>
</generator>
</id>
<property name="userfulLife" column="userfulLife"/>
<one-to-one name="person" constrained="true"/>
</class>
</hibernate-mapping>
HibernateUtil同上
OnetoOneTest:
public class One2One {
public static void main(String[] args) {
add();
queryById(4);
}
static Person add(){
Session s = null;
Transaction tx = null;
IdCard idcard = new IdCard();
idcard.setUserfulLife(new Date());
Person person = new Person();
person.setName("one to one");
person.setIdCard(idcard);
idcard.setPerson(person);//必须要写,因为IdCard中的ID来自Person
try {
s = HibernateUtil.getSession();
tx = s.beginTransaction();
s.save(person);
s.save(idcard);
tx.commit();
}finally{
if(s!=null){
s.close();
}
}
return person;
}
static Person queryById(int id){
Session s = null;
Person person = null;
try {
s = HibernateUtil.getSession();
person = (Person)s.get(Person.class, id);
System.out.println(person.getIdCard().getUserfulLife());
}finally{
if(s!=null){
s.close();
}
}
return person;
}
}
(二)基于外健的one-to-one,可以描述为多对一,加unique=“true”约束
idCard表中的存在主键ID和personId外键,idCard表中字段PersonId来自Person表
Person/IdCard/Hibernate.cfg.xml/OnetoOneTest同(一)
Person.hbm.xml:
<hibernate-mapping package="cn.itcast.hibernate.domain">
<class name="Person" table="person">
<id name="id" column="id" type="java.lang.Integer">
<!-- id是自增长的 -->
<generator class="native" />
</id>
<property name="name" column="name" type="java.lang.String"/>
<!-- 默认缺省主键比较,但idCard不是主键,故而使用property-ref -->
<one-to-one name="idCard" property-ref="person"/>
</class>
</hibernate-mapping>
IdCard.hbm.xml:
<hibernate-mapping package="cn.itcast.hibernate.domain">
<class name="IdCard" table="idcard">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native" />
</id>
<property name="userfulLife" column="userfulLife"/>
<many-to-one name="person" column="personId" unique="true"/>
</class>
</hibernate-mapping>
4、多对多 (teacher-student)
在操作和性能方面都不太理想,所以多对多的映射使用较少,实际使用中最好转换成一对多的对象模型;Hibernate会为我们创建中间关联表,转换成两个一对多。
Teacher:
private int id;
private String name;
private Set<Student> students;
Student:
private int id;
private String name;
private Set<Teacher> teachers;
Teacher.hbm.xml:
<hibernate-mapping package="cn.itcast.hibernate.domain">
<class name="Teacher" table="teacher">
<id name="id" column="id">
<generator class="native" />
</id>
<property name="name" column="name"/>
<!-- 先teacher表中的主键ID到中间表中匹配teacher_id,再根据中间表中的student_id到student表中去与student的主键ID匹配 -->
<set name="students" table="teacher_student">
<key column="teacher_id"></key>
<many-to-many class="Student" column="student_id"/>
</set>
</class>
</hibernate-mapping>
Student.hbm.xml:
<hibernate-mapping package="cn.itcast.hibernate.domain">
<class name="Student" table="student">
<id name="id" column="id">
<generator class="native" />
</id>
<property name="name" column="name"/>
<!-- 先student表中的主键ID到中间表中匹配student_id,再根据中间表中的teacher_id到teacher表中去与teacher的主键ID匹配 -->
<set name="teachers" table="teacher_student">
<key column="student_id"></key>
<many-to-many class="Teacher" column="teacher_id"/>
</set>
</class>
</hibernate-mapping>
HibernateUtil/hibernate/cfg.xml省略
Many2ManyTest:
public class Many2Many {
public static void main(String[] args) {
add();
queryById(1);
}
static void add(){
Session s = null;
Transaction tx = null;
try {
Set<Teacher> teacherSet = new HashSet<Teacher>();
Teacher t1 = new Teacher();
t1.setName("t1 name");
Teacher t2 = new Teacher();
t2.setName("t2 name");
teacherSet.add(t1);
teacherSet.add(t2);
Set<Student> studentSet = new HashSet<Student>();
Student s1 = new Student();
s1.setName("s1 name");
Student s2 = new Student();
s2.setName("s2 name");
studentSet.add(s1);
studentSet.add(s2);
t1.setStudents(studentSet);//t1是这两个学生的老师
t2.setStudents(studentSet);//t2是这两个学生的老师
// s1.setTeachers(teacherSet);//s1是这两个老师的学生
// s2.setTeachers(teacherSet);//s2是这两个老师的学生
s = HibernateUtil.getSession();
tx = s.beginTransaction();
s.save(t1);
s.save(t2);
s.save(s1);
s.save(s2);
tx.commit();
}finally{
if(s!=null){
s.close();
}
}
}
}
static void queryById(int id){
Session s = null;
Transaction tx = null;
Teacher teacher = null;
try {
s = HibernateUtil.getSession();
tx = s.beginTransaction();
teacher = (Teacher)s.get(Teacher.class, id);
System.out.println(teacher.getStudents().size());
tx.commit();
}finally{
if(s!=null){
s.close();
}
}
}
5、组件关联(User-Name)
注意:只会产生一张数据库表
Name:
private String firstName;
private String lastName;
User:
private int id;
private Name name;
private Date birthday;
User.hbm.xml:
……
<component name=”name” class=”com.test.hibernate.domain.Name”>
<property name=” firstName”/>
<property name=” lastName”/>
</component>
……
其他不变
5、总结
只有在一对一的情况下,一次查询完成,其他都是分两次查询
八、集合映射
1、Set省略
2、List(使用一对多,多对一来举例)
Department:
private int id;
private String name;
private List<Employee> emps;
Employee:
private int id;
private String name;
private Department department;
Department.hbm.xml:
<hibernate-mapping package="cn.itcast.hibernate.domain">
<class name="Department" table="department">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native" />
</id>
<property name="name" column="name" type="java.lang.String"/>
<!--
<set name="emps">
<key column="department_id"/>
<one-to-many class="Employee"/>
</set>
-->
<list name="emps">
<key column="department_id"/>
<!-- 使用order_col这一列来排序,因为List是有序的 -->
<list-index column="order_col"/>
<one-to-many class="Employee"/>
</list>
<!-- 使用bag便不再保证List的排序,但只能是与List匹配
<bag name="emps">
<key column="department_id"/>
<one-to-many class="Employee"/>
</bag>
-->
</class>
</hibernate-mapping>
Employee.hbm.xml:
<hibernate-mapping package="cn.itcast.hibernate.domain">
<class name="Employee" table="employee">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native" />
</id>
<property name="name" column="name" type="java.lang.String"/>
<many-to-one name="department" column="department_id"></many-to-one>
</class>
</hibernate-mapping>
Test:
public class Many2oneAndOnetoMany {
public static void main(String[] args) {
System.out.println(add().getName());
//System.out.println(queryById(1).getDepartment().getName());
queryByIdForDepartment(1);
}
static Department add(){
Session s = null;
Transaction tx = null;
Department department = null;
Employee employee1 = null;
Employee employee2 = null;
try {
department = new Department();
department.setName("学生会");
employee1 = new Employee();
employee1.setName("刘鹏");
employee1.setDepartment(department);
employee2 = new Employee();
employee2.setName("小刘");
employee2.setDepartment(department);
List<Employee> emps = new ArrayList<Employee>();
emps.add(employee1);
emps.add(employee2);
department.setEmps(emps);
s = HibernateUtil.getSession();
tx = s.beginTransaction();
s.save(employee1);
s.save(employee2);
s.save(department);
tx.commit();
}finally{
if(s!=null){
s.close();
}
}
return department;
}
//查询员工
static Employee queryById(int empId){
Session s = null;
Employee employee = null;
try {
s = HibernateUtil.getSession();
employee = (Employee)s.get(Employee.class, empId);
//在Session未关闭前可以使用System.out.println(employee.getDepartment().getName());,关闭后如果要在其他地方访问department中的属性必须加上下面这句话
Hibernate.initialize(employee.getDepartment());
}finally{
if(s!=null){
s.close();
}
}
return employee;
}
//查询部门
static Department queryByIdForDepartment(int depId){
Session s = null;
Department dep = null;
try {
s = HibernateUtil.getSession();
dep = (Department)s.get(Department.class, depId);
System.out.println("emps:"+dep.getEmps());
Hibernate.initialize(dep.getEmps());
}finally{
if(s!=null){
s.close();
}
}
return dep;
}
}
3、Map(使用一对多,多对一来举例)
Department:
private Map<String, Employee> emps;
Department.hbm.xml:
<!--
<map name="emps">
<key column="department_id"/>
<map-key type="String" column="name"/>
<one-to-many class="Employee"/>
</map>
-->
4、总结:集合的选择
1、使用hibernate推荐使用的是Set,因为配置简单,且Set不排序,不允许重复资料
2、如果习惯用list,那么可以定义时候使用list,配置时候使用bag,去掉顺序
3、hibernate为了实现懒加载的一些功能,底层将集合接口进行了修改,例如Set变成了persistentSet,所以不能在定义时候使用HashSet等来定义,因为 hibernate的persistentSet依然实现了Set接口,但是并没有继承自HashSet
九、关联关系的级联操作
cascade和inverse (Employee – Department)
Casade用来说明当对主对象进行某种操作时是否对其关联的从对象也作类似的操作,常用的cascade:none,all,save-update ,delete, lock,refresh,evict,replicate,persist,
merge,delete-orphan(one-to-many),一般对many-to-one,many-to-many不设置级联,在<one-to-one>和<one-to-many>中设置级联。
inverse表“是否放弃维护关联关系”(在Java里两个对象产生关联时,对数据库表的影响),在one-to-many和many-to-many的集合定义中使用,inverse=”true”表示该对象不维护关联关系;该属性的值一般在使用有序集合时设置成false(注意hibernate的缺省值是false)。
one-to-many维护关联关系就是更新外键。many-to-many维护关联关系就是在中间表增减记录。
注: 配置成one-to-one的对象不维护关联关系
Cascade:
<one-to-one name="idCard" property-ref="person" cascade="all"/>
<set name="emps" cascade="save-update">
<key column="department_id"/>
<one-to-many class="Employee"/>
</set>
Inverse:
<set name="emps" inverse="true">
<key column="department_id"/>
<one-to-many class="Employee"/>
</set>
十、继承映射
1、一个类继承体系一张表(subclass)(表结构)
Department:
private int id;
private String name;
private Set<Employee> emps;//一对多
Employee:
private int id;
private String name;
private Department department;//多对一
Skiller:
public class Skiller extends Employee {
private String skill;
public String getSkill() {
return skill;
}
public void setSkill(String skill) {
this.skill = skill;
}
}
Sales:
public class Sales extends Employee {
private int sell;
public int getSell() {
return sell;
}
public void setSell(int sell) {
this.sell = sell;
}
}
Department.hbm.xml:
<hibernate-mapping package="cn.itcast.hibernate.domain">
<class name="Department" table="department">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native" />
</id>
<property name="name" column="name" type="java.lang.String"/>
<set name="emps">
<key column="department_id"/>
<one-to-many class="Employee"/>
</set>
</class>
</hibernate-mapping>
Employee.hbm.xml:
<hibernate-mapping package="cn.itcast.hibernate.domain">
<class name="Employee" table="employee" discriminator-value="0"><!-- 默认员工为0 -->
<id name="id" column="id" type="java.lang.Integer">
<generator class="native" />
</id>
<discriminator column="type" type="int"/><!-- 鉴别器,位置不能放错 -->
<property name="name" column="name" type="java.lang.String"/>
<many-to-one name="department" column="department_id"></many-to-one>
<subclass name="cn.itcast.hibernate.domain.children.Skiller" discriminator-value="1"><!-- 技术员为1 -->
<property name="skill"/>
</subclass>
<subclass name="cn.itcast.hibernate.domain.children.Sales" discriminator-value="2"><!-- 销售员为2 -->
<property name="sell"/>
</subclass>
</class>
</hibernate-mapping>
Test(hibernate.cfg.xml和hibernateUtil略):
public class Many2oneAndOnetoMany {
public static void main(String[] args) {
System.out.println(add().getName());
queryById(2);
//System.out.println(queryById(1).getDepartment().getName());
//queryByIdForDepartment(1);
}
static Department add(){
Session s = null;
Transaction tx = null;
Department department = null;
Employee employee1 = null;
try {
department = new Department();
department.setName("学生会");
employee1 = new Employee();
employee1.setName("刘鹏");
employee1.setDepartment(department);
Skiller skiller = new Skiller();
skiller.setName("skiller");
skiller.setDepartment(department);
skiller.setSkill("skill");
Sales sales = new Sales();
sales.setName("sell");
sales.setDepartment(department);
sales.setSell(100);
Set<Employee> emps = new HashSet<Employee>();
emps.add(employee1);
emps.add(skiller);
emps.add(sales);
department.setEmps(emps);
s = HibernateUtil.getSession();
tx = s.beginTransaction();
s.save(department);
s.save(employee1);
s.save(skiller);
s.save(sales);
tx.commit();
}finally{
if(s!=null){
s.close();
}
}
return department;
}
//查询员工
static Employee queryById(int empId){
Session s = null;
Employee employee = null;
try {
s = HibernateUtil.getSession();
employee = (Employee)s.get(Employee.class, empId);
System.out.println(employee.getClass());
//Hibernate.initialize(employee.getDepartment());
}finally{
if(s!=null){
s.close();
}
}
return employee;
}
2、每个子类映射到一张表
Employee.hbm.xml(其他不变):
<hibernate-mapping package="cn.itcast.hibernate.domain">
<class name="Employee" table="employee">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native" />
</id>
<property name="name" column="name" type="java.lang.String"/>
<many-to-one name="department" column="department_id"></many-to-one>
<joined-subclass name="cn.itcast.hibernate.domain.children.Skiller" table="skill">
<key column="emp_id"/>
<property name="skill"/>
</joined-subclass>
<joined-subclass name="cn.itcast.hibernate.domain.children.Sales" table="sales">
<key column="emp_id"/>
<property name="sell"/>
</joined-subclass>
</class>
</hibernate-mapping>
3、 鉴别器与内连接相结合
Employee.hbm.xml(其他不变):
<hibernate-mapping package="cn.itcast.hibernate.domain">
<class name="Employee" table="employee" discriminator-value="0">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native" />
</id>
<discriminator column="type" type="int"/>
<property name="name" column="name" type="java.lang.String"/>
<many-to-one name="department" column="department_id"></many-to-one>
<subclass name="cn.itcast.hibernate.domain.children.Skiller" discriminator-value="1">
<property name="skill"/>
</subclass>
<subclass name="cn.itcast.hibernate.domain.children.Sales" discriminator-value="2">
<join table="sales">
<key column="emp_id"/>
<property name="sell"/>
</join>
</subclass>
</class>
</hibernate-mapping>
4、 每个具体类映射一张独立表
Employee.hbm.xml(其他不变):
<hibernate-mapping package="cn.itcast.hibernate.domain">
<class name="Employee" table="employee">
<id name="id" column="id" type="java.lang.Integer">
<generator class="hilo" />
</id>
<property name="name" column="name" type="java.lang.String"/>
<many-to-one name="department" column="department_id"></many-to-one>
<union-subclass name="cn.itcast.hibernate.domain.children.Skiller" table="skill">
<property name="skill"/>
</union-subclass>
<union-subclass name="cn.itcast.hibernate.domain.children.Sales" table="sales">
<property name="sell"/>
</union-subclass>
</class>
</hibernate-mapping>
十一、懒加载及原理分析
总述:懒加载表示只有在真正去访问数据的时候才会去访问数据库
1、 好比在Department和Employee中,当查询Department的信息时候(s.get(Department.class, depId)),只会出现一条查询deaprtment表的SQL语句
2、 当在session范围内进行System.out.println("emps:"+dep.getEmps());会出现两条SQL语句,因为此时用到了employee中的信息,故而查询两张表
3、 当在session范围之外进行System.out.println("emps:"+dep.getEmps());会报不能初始化代理,因为会形成persisentSet代理类,关闭session便消失了
1、Session.load方式的懒加载
User user = (User)s.load(User.class,id); 1、上面的方法获得的是User的子类,hibernate自动加了一些字符组成奇怪的名字
1、懒加载通过asm和cglib二个包实现
2、获得的User是继承自domain对象的,所以domain类不能是final的
3、解决办法:
User user = (User)s.load(User.class,id);
Hibernate.initialize(user); //初始化,使其访问数据库
4、懒加载只有在需要访问数据库的时候才会去访问,提高效率,而且load不可以使用if(user!=null)来判断,因为这句话不可能成立
2、一对一方式的懒加载
1、从表必需同时满足下面三个条件时才能实现懒加载
(主表不能有constrained=true,所以主表没有懒加载)
1、lazy!=false (缺省)
2、constrained=true (从对象)
3、fetch=select (缺省)
<one-to-one name="person" constrained="true"/>
<!--解除懒加载,lazy代表什么时候抓取,默认值为proxy -->
<one-to-one name="person" constrained="true" lazy=”false”/>
<!—通过什么方式抓取,默认为select -->
<one-to-one name="person" constrained="true" fetch=”join”/>连接抓取
2、主表不能实现懒加载,因为他不知道是否存在idCard,所以就一起查出来
3、一对多、多对一和多对多的懒加载
1、lazy!=false (缺省)
2、fetch=select (缺省)
十二、缓存
总述:缓存的作用主要用来提高性能,可以简单的理解成一个Map;使用缓存涉及到三个操作:把数据放入缓存、从缓存中获取数据、删除缓存中的无效数据。
CacheDemo:
/**
* 描述:从Map中寻找,存在则取出,否则访问数据库,再将结果加入到map中。
* 更新数据之后将map中对应记录删除
* @author liupeng
*
*/
public class CacheDemo {
static Map cache = new HashMap();
public static void main(String[] args) {
User u = getUser(1);
User u2 = getUser(1);
}
public static User getUser(int id){
String key = User.class.getName()+id;
User user = (User)cache.get(key);
if(user!=null){ //表示在Map中存在
return user;
}else {
user = getFromDB();
cache.put(key, user);
return user;
}
}
public static void update(User user){
updateDB(user);
String key = User.class.getName()+user.getId();
cache.remove(key);
}
public static User getFromDB() {
return null;
}
public static void updateDB(User user) {
}
}
1、一级缓存-Session级共享
1、将对象放在一级缓存中:save,update,saveOrUpdate,load,get,list,iterate,lock
2、将对象从一级缓存中取出来:get,load,iterator
一级缓存不能控制缓存的数量,所以要注意大批量操作数据时可能造成内存溢出;可以用evict,clear方法清除缓存中的内容(s.evict(user)/s.clear())。
User:
private int id;
private Name name;
private Date birthday;
Name:
private String firstName;
private String lastName;
User.hbm.xml:
<hibernate-mapping package="cn.itcast.domain">
<class name="User" table="user">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native" />
</id>
<component name="name">
<property name="firstName" column="first_name"/>
<property name="lastName" column="last_name" />
</component>
<property name="birthday" column="birthday" type="java.util.Date"/>
</class>
</hibernate-mapping>
HibernateUtil不变
CacheTest:
public class CacheTest {
public static void main(String[] args) {
User user = addUser();
System.out.println("--------------------");
getUser(1);
}
public static User addUser(){
User user = new User();
Name name = new Name();
name.setFirstName("firstName");
name.setLastName("lastName");
user.setName(name);
user.setBirthday(new Date());
HibernateUtil.add(user);
return user;
}
public static User getUser(int id){
Session s = null;
User user = null;
try {
s = HibernateUtil.getSession();
user = (User)s.get(User.class, id);
user = (User)s.get(User.class, id);//只会产生一条select语句,因为这是Session级别的缓存,当缓存关闭之后再重新启动缓存查询的话会出现两条select语句
}finally{
if(s!=null){
s.close();
}
}
return user;
}
}
2、二级缓存-SessionFactory级缓存
步骤:
1、 使用OSCache,需要先导入oscache-2.1.jar和commons-logging.jar
2、 加入oscache.properties
3、在hibernate映射文件中打开二级缓存
4、在hibernate映射文件中使用OsCacheProvider缓存
5、配置待缓存的类(一种在hibernate映射文件中配置,一种在类配置文件中配置)
细节:
1、 将对象放入二级缓存中:Session的save(这个方法不适合native生成方式的主键),update,saveOrUpdate,list,iterator,get,load,以及Query,Criteria都会填充二级缓存
2、 但只有(没打开查询缓存时)Session的iterator,get,load会从二级缓存中取数据(iterator可能存在N+1次查询)。
3、 SessionFactory中提供了evictXXX()方法用来清除缓存中的内容
4、 Query,Criteria(查询缓存)由于命中率较低,所以hibernate缺省是关闭;修改cache.use_query_cache为true打开对查询的缓存,并且调用query.setCacheable(true)或criteria.setCacheable(true)。
User:
private int id;
private Name name;
private Date birthday;
Name:
private String firstName;
private String lastName;
User.hbm.xml:
<hibernate-mapping package="cn.itcast.domain">
<class name="User" table="user">
<cache usage="read-write"/>
<id name="id" column="id" type="java.lang.Integer">
<generator class="native" />
</id>
<component name="name">
<property name="firstName" column="first_name"/>
<property name="lastName" column="last_name" />
</component>
<property name="birthday" column="birthday" type="java.util.Date"/>
</class>
</hibernate-mapping>
oscache.properties:
cache.capacity=10000
Hibernate.cfg.xml:
<hibernate-configuration>
<session-factory>
<property name="hbm2ddl.auto">update</property>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/liupeng</property>
<property name="connection.username">root</property>
<property name="connection.password">mysqladmin</property>
<property name="myeclipse.connection.profile">Mysql</property>
<property name="show_sql">true</property> <!-- 显示SQL语句 -->
<!-- 打开二级缓存,默认为true -->
<property name="cache.use_second_level_cache">true</property>
<!-- 使用何种cache -->
<property name="cache.provider_class">org.hibernate.cache.OSCacheProvider</property>
<!-- 需要缓存的类,read-only表示User不能修改
<class-cache usage="read-write" class="cn.itcast.domain.User"/>
-->
<!-- 产生统计信息 -->
<property name="generate_statistics">true</property>
<mapping resource="cn/itcast/domain/User.hbm.xml"/>
</session-factory>
</hibernate-configuration>
CacheTest:
public class CacheTest {
public static void main(String[] args) {
User user = addUser();
System.out.println("--------------------");
getUser(1);
//获得统计信息
Statistics st = HibernateUtil.getSessionFactory().getStatistics();
//未命中
System.out.println(st.getSecondLevelCacheMissCount());
//放入
System.out.println(st.getSecondLevelCachePutCount());
//命中
System.out.println(st.getSecondLevelCacheHitCount());
}
public static User addUser(){
User user = new User();
Name name = new Name();
name.setFirstName("firstName");
name.setLastName("lastName");
user.setName(name);
user.setBirthday(new Date());
HibernateUtil.add(user);
return user;
}
public static User getUser(int id){
Session s = null;
User user = null;
try {
s = HibernateUtil.getSession();
user = (User)s.get(User.class, id);
user = (User)s.get(User.class, id);//从一级缓存中查找
}finally{
if(s!=null){
s.close();
}
}
try {
s = HibernateUtil.getSession();
user = (User)s.get(User.class, id);//从二级缓存中查找
}finally{
if(s!=null){
s.close();
}
}
return user;
}
}
三、分布式缓存和中央缓存
使用缓存的条件
1.读取大于修改。
2.数据量不能超过内存容量。
3.对数据要有独享的控制。
4.可以容忍出现无效数据。
十三、事务与事务边界的相关知识
1、 JDBCTransaction:
1、单个数据库,一个sessionFactory对应一个数据库,由JDBC实现,这种事务只能在一个数据库上操作,只能处理一个数据库的事务
public static void add(Object entity){
Session s = null;
Transaction tx = null;
try {
s = getSession();
tx = s.beginTransaction();
s.save(entity);
tx.commit();
}finally{
if(s!=null){
s.close();
}
}
}
2、JTATransaction(分布式)
1、可以简单的理解成跨数据库的事物,由应用JTA 容器实现
2、使用JTATransaction需要配置hibernate.transaction.factory_class参数,该参数缺省值是org.hibernate.transaction. JDBCTransactionFactory,当使用JTATransaction时需要将该参数改成org.hibernate.transaction.JTATransactionFactory,
3、并配置jta.UserTransaction参数JNDI名(Hibernate在启动JTATransaction时要用该值到JNDI的上下文Context中去找javax.transaction.UserTransaction)
javax.transaction.UserTransactin tx = context.lookup(“jndiName”);
try{
tx.begin();
//多个数据库的session操作;
//session1….
//session2….
tx.commit();
}catch(Exception e){
tx.rollback(); throw e;
}
3、OpenSessionInView模式的代码分析
session context和事务边界
用current_session_context_class属性来定义context(用sessionFactory.getCurrentSession()来获得session),其值为:
1.thread:ThreadLocal来管理Session实现多个操作共享一个Session,避免反复获取Session,并控制事务边界,此时session不能调用close当commit或rollback的时候session会自动关闭(connection.release_mode:after_transaction)。
Open session in view:在生成(渲染)页面时保持 session打开。
2.jta:由JTA事务管理器来管理事务(connection.release_mode:after_statement)
HibernateUtil:
public final class HibernateUtil {
private HibernateUtil(){}
private static SessionFactory sessionFactory;
//在一个线程范围内有效
private static ThreadLocal session = new ThreadLocal();
public static Session getThreadLocalSession(){
Session s = (Session)session.get();
if(s == null){
s = getSession();
session.set(s);
}
return s;
}
public void closeSession(){
Session s = (Session)session.get();
if(s!=null){
s.close();
session.set(null);
}
}
static{
Configuration cfg = new Configuration();
cfg.configure();
sessionFactory = cfg.buildSessionFactory();
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
public static Session getSession(){
return sessionFactory.openSession();
}
OpenSessionInView(Filter):
public class OpenSessionInView implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,FilterChain filterChain) throws IOException, ServletException {
Session session = null;
Transaction tx = null;
try {
session = HibernateUtil.getThreadLocalSession();
tx = session.beginTransaction();
filterChain.doFilter(request, response);
tx.commit();
} catch (Exception e) {
if(tx!=null){
tx.rollback();
throw new RuntimeException(e.getMessage(),e);
}
}finally{
HibernateUtil.closeThreadLocalSession();
}
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}
UserDao:
public class UserDao {
static void addUser(User user){
//此处取到的Session和过滤器中是同一个Session
HibernateUtil.getThreadLocalSession().save(user);
}
}
4、悲观锁和乐观锁
悲观锁由数据库来实现;乐观锁hibernate用version和timestamp来实现
User:
private int id;
private Name name;
private Date birthday;
private int ver; //版本号
User.hbm.xml:
<hibernate-mapping package="cn.itcast.domain">
<class name="User" table="user">
<cache usage="read-write"/>
<id name="id" column="id" type="java.lang.Integer">
<generator class="native" />
</id>
<!-- 必须放在id下面 -->
<version name="ver"></version>
<component name="name">
<property name="firstName" column="first_name"/>
<property name="lastName" column="last_name" />
</component>
<property name="birthday" column="birthday" type="java.util.Date"/>
</class>
</hibernate-mapping>
VersionTest:
public class VersionTest {
public static void main(String args[]){
User user = new User();
Name name = new Name();
name.setFirstName("firstName");
name.setLastName("lastName");
user.setName(name);
user.setBirthday(new Date());
addUser(user);
System.out.println(user.getId());
updateUser(user.getId());
}
public static void addUser(User user){
Session s = null;
Transaction tx = null;
try {
s = HibernateUtil.getSession();
tx = s.beginTransaction();
s.save(user);
tx.commit();
}finally{
if(s!=null){
s.close();
}
}
}
public static void updateUser(int id){
Session s1 = HibernateUtil.getSession();
Transaction tx1 = s1.beginTransaction();
User user1 = (User)s1.get(User.class, id);
Session s2 = HibernateUtil.getSession();
Transaction tx2 = s2.beginTransaction();
User user2 = (User)s2.get(User.class, id);
user1.getName().setFirstName("new1 firstName");
user2.getName().setFirstName("new2 firstName");
tx2.commit(); //哪个能执行取决与哪个先提交
tx1.commit();
s1.close();
s2.close();
}
}
十四、Hibernate配置文件和映射文件中的配置项
具体参考hibernate3.2 pdf
Session是非线程安全的,生命周期较短,代表一个和数据库的连接,在B/S系统中一般不会超过一个请求;内部维护一级缓存和数据库连接(connection),如果session长时间打开,会长时间占用内存和数据库连接。
主键id类型:
native:需要将数据更新到数据库中后才能存在ID
hilo:不需要将数据更新到数据库就能知道ID
十五、Session的内部缓存管理与批量更新
1、flush时将一级缓存与数据库同步
s.save(department)
s.save(employee1)
s.save(employee2)
System.out.println(“--------”)
s.commit()
打印结果为先保存department,再打印------,最后保存employee。
原因:根据id的增长方式有关,department是native的,只有数据保存到数据库中的时候才会产生ID,而employee是hilo的,不用将数据插入数据库的时候就已经知道了ID
解决办法:在提交前执行s.flush()
2、大量的操作数据可能造成内存溢出,解决办法:
for(int i=0;i<100000;i++){
session.save(obj);
if(i% 50 == 0){
session.flush(); //强制将记录更新到数据库
session.clear(); //清空一级缓存中的所有数据
}
}
3、用StatelessSession接口:它不和一级缓存、二级缓存交互,也不触发任何事件、监听器、拦截器,通过该接口的操作会立刻发送给数据库,与JDBC的功能一样。
StatelessSession s = sessionFactory.openStatelessSession();该接口的方法与Session类似。
4.Query.executeUpdate()执行批量更新,会清除相关联的类二级缓存(sessionFactory.evict(class)),也可能会造成级联,和乐观锁定出现问题
Query q = s.createQuery(“update u set birthday = :bd from User as u”)
q.executeUpdate()
十六、Hql与Criteria查询的补充知识
1、查询多个对象select art, user from Article art, User user where art.author.id=user.id and art.id=:id这种方式返回的是Object[],Object[0]:article,Object[1]:user。(art和user都是对象,查询对象)
Iterator kittensAndMothers = s.createQuery(
"select kitten, mother from Cat kitten join kitten.mother mother")
.list()
.iterator();
while ( kittensAndMothers.hasNext() ) {
//转换为数组,数组里面包含着对应的元组。
Object[] tuple = (Object[]) kittensAndMothers.next();
Cat kitten = (Cat) tuple[0];
Cat mother = (Cat) tuple[1];
....
}
2、离线查询(动态查询)
业务类
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(User.class);
String name = request.getParameter("name");
if(name!=null){
detachedCriteria.add(Restrictions.eq("name", name));
}
int age = request.getParameter("age");
if(age>0){
detachedCriteria.add(Restrictions.eq("age", age));
}
List users = dc(detachedCriteria);
持久层
static List dc(DetachedCriteria dc){
Session session = HibernateUtil.getSession();
Criteria c =dc.getExecutableCriteria(session);
List list = c.list();
session.close();
return list;
}
十七、Iterate和懒加载的 N+1次查询问题
1、Iterator产生的N+1次查询问题
static void iterator() {
Session s = HibernateUtil.getSession();
Query q = s.createQuery("from User ");
Iterator<User> users = q.iterate(); //第一次查询,查询ID
while (users.hasNext()) {
//N次查询
System.out.println(users.next().getName().getFirstName());
}
s.close();
}
说明:如果二级缓存中没有数据的话,那么会产生N+1条查询语句,第一条查询ID,其他的N条与数据库记录条数对应,查询N次
2、懒加载造成的N+1次查询问题
static void ncpp() {
Session s = HibernateUtil.getSession();
Query q = s.createQuery("from IdCard"); //第一次查询,查询IdCard
List<IdCard> ics = q.list();
for (IdCard ic : ics) {
//N次查询,查询Person
System.out.println(ic.getPerson().getName()); }
s.close();
}
3、解决N+1次查询的办法:
1、打开二级缓存
2、抓取方式该为join
十八、Hibernate拦截器与监听器
拦截器与事件都是hibernate的扩展机制,Interceptor接口是老的实现机制,现在改成事件监听机制;他们都是hibernate的回调接口,hibernate在save,delete,update…等会回调这些类。
saveListener:
public class saveListener implements SaveOrUpdateEventListener {
@Override
public void onSaveOrUpdate(SaveOrUpdateEvent event)
throws HibernateException {
if(event.getObject() instanceof User){
User user = (User)event.getObject();
System.out.println("---"+user.getId());
}
}
}
Hibernate.cfg.xml:
<event type="save">
<!-- 不加上下面这句的话保存的时候不会执行自定义的事件监听器,因为hibernate以为你有更好的策略,故而不调用自己的事件监听器了 -->
<listener class="org.hibernate.event.def.DefaultSaveOrUpdateEventListener"/>
<!-- 在save方法执行之后调用自定义的事件监听器,因为放在了默认事件监听器的下面。 -->
<listener class="cn.itcast.listener.saveListener"/>
</event>
总结:
根据不同的操作,save,delete等从对应的类似s.save()方法依次往里面找,知道找到类似DefaultSaveOrUpdateEventListener事件监听器为止
十九、本地sql查询(原生查询)与命名查询
1、原生查询
public class SQLTest {
public static void main(String[] args) {
sql();
}
static List sql(){
Session s = null;
try {
s = HibernateUtil.getSession();
//此时的user为表名称,而非类名称
Query query = s.createSQLQuery("select * from user").addEntity(User.class);
List<User> list = query.list();
for(User user : list){
System.out.println(user.getName().getFirstName());
}
return list;
}finally{
if(s!=null){
s.close();
}
}
}
}
2、命名查询
User.hbm.xml:
<!--
放在<Class>外面和里面的区别:
如果放在里面的话需要写全路径(c.itcast.domain.User.getUserByVer)
如果放在里面的话直接写名称(getUserByVer),但是名称必须唯一
-->
<query name="getUserByVer">
<![CDATA[from User where ver = :ver]]>
</query>
Test:
static void namedQuery(){
Session session = HibernateUtil.getSession();
Query query = session.getNamedQuery("getUserByVer");
query.setString("ver","0");
List<User> list = query.list();
for(User user : list){
System.out.println(user.getVer());
}
}
二十、Hiberante的最佳实践(详见pdf)
1、不适合OLAP(On-Line Analytical Processing联机分析处理),以查询分析数据为主的系统;适合OLTP(on-line transaction processing联机事务处理)
2、 对于些关系模型设计不合理的老系统,也不能发挥hibernate优势
3、 数据量巨大,性能要求苛刻的系统,hibernate也很难达到要求, 批量操作数据的效率也不高。
Hibernate3.3笔记
最新推荐文章于 2016-09-24 21:04:42 发布