1、建立新java项目,名为HelloWorld
2、学习建User-library-hibernate,并加入相应的jar包
3、引入mysql的JDBC驱动包
4、在mysql中建立对应的数据库以及表
5、建立hibernate配置文件hibernate.cfg.xml
6、建立Student类
7、建立Student映射文件Student.hbm.xml,数据库中表Student和类Student对应关系
8、将映射文件加入到hibernate.cfg.xml中
9、写测试类Main,在Main中对Student对象进行直接的存储测试
10、HibernateUtil将获取SessionFactory做成单例
<!--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可以理解为一个Connection工厂或者Connection池 -->
<session-factory>
<!-- Database connection settings -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost/hibernate</property>
<property name="connection.username">root</property>
<property name="connection.password">bjsxt</property>
<!-- 这个是hibernate自带的连接池用的不多,一般使用applicaton Server用JNDI注册的连接池> <property name="connection.pool_size">1</property> <!-- SQL dialect(方言),hibernate会将语句翻译为对应方言的sql,参考文档 -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- Enable Hibernate's automatic session context management,后面讲 -->
<!-- <property name="current_session_context_class">thread</property>-->
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<!-- Drop and re-create the database schema on startup,hibernate自动生成建表语句,后面讲 -->
<!-- <property name="hbm2ddl.auto">update</property>-->
<mapping resource="com/bjsxt/hibernate/Student.hbm.xml"/>
</session-factory>
</hibernate-configuration>
public class Student { //省略set、get
private int id; private String name; private int age; }
<!--Student.hbm.xml,名称固定,按约定俗称写,放在和Student类相同包下 -->
<?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>
<class name="com.bjsxt.hibernate.Student">
<id name="id" />
<property name="name" />//不写column默认和name一样
<property name="age" />
</class>
</hibernate-mapping>
//测试类
public class Test { public static void main(String[] args) { Student s = new Student(); s.setId(1); s.setName("zhangsan"); s.setAge(8); SessionFactory sessionFactory = new AnnotationConfiguration() .configure().buildSessionFactory(); Session session = sessionFactory.getCurrentSession();// 可以把Session理解为Connection
session.beginTransaction();
session.save(s);// 这样就把数据插入到了数据库中
session.getTransaction().commit(); } }
1、hibernate.cfg.xml文件中添加:
<mapping class="com.bjsxt.hibernate.Teacher"/>//是点,xml版本的是/
2、不需要Student.hbm.xml,老方说过注解的目的就是为了简化配置文件
3、Student添加如下:
import javax.persistence.Entity; import javax.persistence.Id; @Entity public class Teacher { private int id; private String name; private String title; @Id 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 String getTitle() { return title; } public void setTitle(String title) { this.title = title; } }
public class TeacherTest { public static void main(String[] args) { Teacher t = new Teacher(); t.setId(1); t.setName("t1"); t.setTitle("middle"); SessionFactory sessionFactory = new AnnotationConfiguration() .configure().buildSessionFactory(); Session session = sessionFactory.getCurrentSession(); session.beginTransaction(); session.save(t); session.getTransaction().commit(); } }
ps:sun公司自己设计ejb,后来发明hibernate,比ejb好用,于是sun把hibernate的开发者挖过去,开发了一套标准也就是jpa,hibernate转而去实现了jpa(Java Persistence API)标准(mybatis不支持jpa),不过也有自己的扩展,类似sun定义的jdbc标准,其它数据库厂商实现,hibernate重点讲注解,其实hibernate在效率要求较高的项目里不会使用,hibernate能调用存储过程,但是一般不会用,打破了hibernate面向对象的编程.
Note:
要建立自己动手查一手文档的信心!
还有建立自己动手查一手文档的习惯!
主动学习,砍弃被动接受灌输的习惯!
1、JDBC操作数据库很繁琐
2、Sql语句编写并不是面向对象的
3、可以在对象和关系表之间建立关联来简化编程
4、0/R Mapping简化编程,跨越数据库平台
5、Hibernate原理模拟,反射很容易略.
1、属性hbm2ddl.auto值create,自动根据实体创建表,update,实体变化时自动跟新表,validate检验是否一致,create-drop,关闭SessionFactory将删除表.
注:hibernate既可以从表导出类,也可以从类导出表,实际当中是先建表,因为要对表进行优化,比如建索引什么的.
2、搭建日志环境并配置显示DDL语句
1>slf4j与log4j的关系:slf4j是一个接口,可以管理许多的日志框架,log4j是其中之一,slf类似jdbc和jpa一样.
2>加入slf4j-log4j.jar,加入log4j的jar包,去掉slf4-nop.jar,这里将slf接口转为log4j,用到了适配器模式.
3>从hibernate/project/etc 目录copy log4j.properties
4>査询hibernate文裆,日志部分,调整日志的输出策略,开启这个就可:
log4j.logger.org.hibernate.tool.hbm2ddl=debug
3、junit搭建
public class StudentTest { private static SessionFactory sessionFactory; // 类似使用HibernateUtil把sessionFactory做成单例一样,和数据库连接池类似
@BeforeClass public static void beforeClass() { sessionFactory = new AnnotationConfiguration().configure() .buildSessionFactory(); } @AfterClass public static void afterClass() { sessionFactory.close(); } @Test public void testStudentSave() { Student s = new Student(); s.setId(1); s.setName("zhangsan"); s.setAge(8); //类似从数据库连接池中取连接,此操作不费力
Session session = sessionFactory.getCurrentSession(); session.beginTransaction(); session.save(s); session.getTransaction().commit(); } }
4、show_sql属性 是否输出SQL语句
5、format_sql属性 格式化SQL语句,美化SQL语句
5、表名和类名不同,对表名进行配置: Annotation: @Table
6、字段名和属性相同: 不用写@column,与默认的@Basic效果一样
7、字段名和属性名不同: Annotation: @Column
8、不需要psersistence的字段(不用列): Annotation:@Transient
9、映射日期与时间类型,指定时间精度
Annotation:@Temporal(参数) 参数有3种
@Temporal(TemporalType.DATE) 只显示日期
@Temporal(TemporalType.TIME) 只显示时间
@Temporal(TemporalType.TIMESTAMP) 显示日期与时间
10、映射枚举类型(比较少用)
@Enumerated @Enumerated(EnumType.ORDINAL) 枚举类型按位置数,如:0,1,2 ...存储 @Enumerated(EnumType.STRING) 枚举类型按设定值存储
11、字段映射的位置(field或者get方法),建议get方法.ps:CLOB,BLOB类型的数据存取,Hibernate自定义数据类型,略.
12、ID生成策略,@Id后加@GeneratedValue即可由hibernate自动根据数据库,自动选择策略生成主键,详细的用到时候查,比如联合主键.
13、注解配置实例如下:
import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.Id; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.persistence.Transient; @Entity @Table(name="_teacher") public class Teacher { private int id; private String name; private String title; private String yourWifeName; private Date birthDate; private Gender gender; @Id
@GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } @Colume(name="userName") public String getName() { return name; } public void setName(String name) { this.name = name; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } @Transient public String getYourWifeName() { return yourWifeName; } public void setYourWifeName(String yourWifeName) { this.yourWifeName = yourWifeName; } @Temporal(TemporalType.TIME) public Date getBirthDate() { return birthDate; } public void setBirthDate(Date birthDate) { this.birthDate = birthDate; } @Enumerated(EnumType.STRING) public Gender getGender() { return gender; } public void setGender(Gender gender) { this.gender = gender; } }
1、Configuration,AnnotationConfiguration,进行配置信息的管理,用来产生SessionFactory,可以在configure方法中指定hibernate配置文件(不自定义hibernate配置文件则没必要),只关注一个方法即:buildSessionFactory.
// junit中
@BeforeClass public static void beforeClass1() { sessionFactory = new AnnotationConfiguration().configure( "自定义hibernate配置文件").buildSessionFactory(); } @BeforeClass public static void beforeClass2() { sessionFactory = new AnnotationConfiguration().configure() .buildSessionFactory(); } @AfterClass public static void afterClass() { sessionFactory.close();//需要关闭 }
2、SessoinFactory,Session
1>SessoinFactory(当成连接池即可),用来产生和管理Session(当成Connection),通常情况下每个应用只需要一个SessionFactory(可以在util类中做成单例),除非要访问多个数据库的情况,关注两个方法即:openSession,getCurrentsession,不能混用.
2>open session每次从数据库获取新的连接(已经不推荐使用),getCurrentsession每次从当前线程范围中找,如果有,用旧的,如果没有,建新的.
3>getCurrentSession获得的Session会在事务提交或者回滚时自动关闭,而openSession获得的Session必须手动关闭.
4>那么使用getCurrentsession有什么用途,用来界定事务边界,根据之前的知识知道,同一个事务必须保证是同一个Connection.
5>如果使用currentSession需要在hibernate.cfg.xml文件中进行配置,主要用以下两种:
*如果是本地事务(jdbc事务)
<propertyname="hibernate.current_session_context_class">thread</property>
*如果是全局事务(jta事务)
<propertyname="hibernate.current_session_context_class">jta</property>
ps:jta (全称java transaction api)-java分布式事务管理(多数据库访问), jta由中间件提供(jboss WebLogic等,tomcat不支持),先了解,具体用到的时候在查.
实例1:
@Test public void testTeacherSave() { Teacher t = new Teacher(); t.setName("t1"); t.setTitle("middle"); t.setBirthDate(new Date()); // Session session = sessionFactory.openSession();
Session session = sessionFactory.getCurrentSession(); session.beginTransaction(); session.save(t);//一个session可以执行多条语句 Session session2 = sessionFactory.getCurrentSession(); System.out.println(session == session2);//true
session.getTransaction().commit(); Session session3 = sessionFactory.getCurrentSession(); System.out.println(session == session3);//false,提交后连接以关闭
}
实例2,日志,是编程中很常见的一个关注点.用户在对数据库进行操作的过程需要将这一系列操作记录,以便跟踪数据库的动态.那么一个用户在向数据库插入一条记录的时候,就要向日志文件中记录一条记录,用户的一系列操作都要在一个Session中进行,否则这就成为了两个线程.不能保证同步.看下面的代码:
public class HibernateUtils { private static SessionFactory factory; static { try { factory = new Configuration().configure().buildSessionFactory(); } catch (Exception e) { throw new RuntimeException(e); } } // 获得开启着的Session
public static Session getSession() { return factory.openSession(); } // 关闭Session
public static void closeSession(Session session) { if (session != null) { if (session.isOpen()) { session.close(); } } } public static SessionFactory getSessionFactory() { return factory; } }
//用户业务逻辑层
public class UserManagerImpl implements UserManager { /** * 添加用户和添加日志都使用了同一个Session,所以 * 当用户添加失败的时候,日志也会添加失败.事务回滚 * 用户添加成功日志也会添加成功 */
public void addUser(User user) { Session session=null; try{ //取得当前线程Session
session=HibernateUtils.getSessionFactory().getCurrentSession(); session.beginTransaction(); //保存用户
session.save(user); Log log=new Log(); log.setType("操作日志"); log.setTime(new Date()); log.setDetail("XXX"); LogManager logManager=new LogManagerImpl(); //保存日志
logManager.addLog(log); session.getTransaction().commit(); }catch(Exception e){ e.printStackTrace(); session.getTransaction().rollback(); } } } //日志实现类:
public class LogManagerImpl implements LogManager { public void addLog(Log log) { HibernateUtils.getSessionFactory().getCurrentSession().save(log); } } //测试类
public class UserManagerImplTest extends TestCase { public void testAddUser() { UserManager userManager=new UserManagerImpl(); User user=new User(); user.setName("张三"); userManager.addUser(user); } }
1、threadlocal中存连接和数据库连接池不是同一概念,连接池是缓存连接,主要是为了提高性能.一般从连接池中拿到连接,放到threadlocal中,这样能解决的事务问题,不管调用哪个方法,同一个线程使用的是同一个连接.
2、在Hibernate中,Session打开的时候,就会自动conn.setAutoCommit(false),不像一般的JDBC,默认都是true,最后不写commit也没有关系,但是由于Hibernate已经把AutoCommit给关掉了,所以用Hibernate的时候,你在程序中必须手动开启事务,执行完后提交,如果不写Transaction的话,数据库根本就没有反应.
3、对象的三种状态
1>三种状态的区分关键在于:
有没有ID
ID在数据库中有没有
在内存中有没有(session缓存,Session中有个map,key是id,value是对象引用)
2>三种状态:
transient(透明):内存中有,没ID,缓存中也没有
persistent(持久):内存中有,缓存中有,数据库有ID
detached(托管):内存有,缓存没有,数据库有,ID
3>对这三种状态需要关注的问题是在该状态下如果进行数据库的操作会发生什么结果,比如改变属性的值会不会发出update语句?
强烈建议动手实验
进行正常人的思考
Hibernate中涉及很多非常非常细节的区别,但在实际应用中用得极少,绝对不要去背这些东西!
4>sava、delete、get、load、update、saveOrUpdate、SchemaExport实例:
@Test public void testSave() { Teacher t = new Teacher(); t.setName("t1"); t.setTitle("middle"); t.setBirthDate(new Date()); Session session = sessionFactory.getCurrentSession(); session.beginTransaction(); session.save(t);// save前是transient,save后persisent
System.out.println(t.getId()); session.getTransaction().commit();// commit后detach
System.out.println(t.getId()); } @Test public void testDelete() { Teacher t = new Teacher(); t.setName("t1"); t.setTitle("middle"); t.setBirthDate(new Date()); Session session = sessionFactory.getCurrentSession(); session.beginTransaction(); session.save(t); System.out.println(t.getId()); session.getTransaction().commit(); Session session2 = sessionFactory.getCurrentSession(); session2.beginTransaction(); session2.delete(t);// delete时,这个对象必须有id,托管态也可
session2.getTransaction().commit(); } @Test public void testDelete2() { Teacher t = new Teacher(); t.setId(2); Session session2 = sessionFactory.getCurrentSession(); session2.beginTransaction(); session2.delete(t);//可以的,这些细节动手做实验
session2.getTransaction().commit(); } @Test public void testLoad() { Session session = sessionFactory.getCurrentSession(); session.beginTransaction(); Teacher t = (Teacher) session.load(Teacher.class, 1);// 1是id
session.getTransaction().commit(); System.out.println(t.getName());//懒加载,在需要拿属性的时候才发sql,这是Session已经关了,报错
System.out.println(t.getClass());//load返回的是代理对象,从这里看看出
} @Test public void testGet() { Session session = sessionFactory.getCurrentSession(); session.beginTransaction(); Teacher t = (Teacher) session.get(Teacher.class, 1); session.getTransaction().commit(); System.out.println(t.getName());//正常打印值
System.out.println(t.getClass());//get返回的是正常的对象
} @Test public void testUpdate1() { Session session = sessionFactory.getCurrentSession(); session.beginTransaction(); Teacher t = (Teacher) session.get(Teacher.class, 1); session.getTransaction().commit(); t.setName("zhanglaoshi"); Session session2 = sessionFactory.getCurrentSession(); session2.beginTransaction(); session2.update(t);//正常更新,将detach跟新为persient
session2.getTransaction().commit(); } @Test public void testUpdate2() { Teacher t = new Teacher(); t.setName("zhanglaoshi"); Session session2 = sessionFactory.getCurrentSession(); session2.beginTransaction(); session2.update(t);//跟新transient对象报错,没有ID
session2.getTransaction().commit(); } @Test public void testUpdate3() { Teacher t = new Teacher(); t.setId(1); t.setName("zhanglaoshi"); Session session2 = sessionFactory.getCurrentSession(); session2.beginTransaction(); session2.update(t);//跟新设置好的id,不会报错
session2.getTransaction().commit(); } @Test public void testUpdate4() { Session session = sessionFactory.getCurrentSession(); session.beginTransaction(); Teacher t = (Teacher) session.get(Teacher.class, 1); t.setName("zhangsan2");//persisent字段只要发生变化,就会在提交时自动跟新
session.getTransaction().commit(); } @Test public void testUpdate5() { Session session = sessionFactory.getCurrentSession(); session.beginTransaction(); Student s = (Student) session.get(Student.class, 1); s.setName("zhangsan5");//使用dynamic-update,只更新变化的
session.getTransaction().commit(); s.setName("z4"); Session session2 = sessionFactory.getCurrentSession(); session2.beginTransaction(); session2.update(s);//托管态,没有地方比较,全发
session2.getTransaction().commit(); } @Test public void testUpdate6() { Session session = sessionFactory.getCurrentSession(); session.beginTransaction(); Student s = (Student) session.get(Student.class, 1); s.setName("zhangsan6"); session.getTransaction().commit(); s.setName("z4"); Session session2 = sessionFactory.getCurrentSession(); session2.beginTransaction(); session2.merge(s);//只更新变化的,推荐使用hql
session2.getTransaction().commit(); } @Test public void testUpdate7() { Session session = sessionFactory.getCurrentSession(); session.beginTransaction(); //Student大写,是对象
Query q = session .createQuery("update Student s set s.name='z5' where s.id = 1"); q.executeUpdate(); session.getTransaction().commit(); } @Test public void testSaveOrUpdate() { Teacher t = new Teacher(); t.setName("t1"); t.setTitle("middle"); t.setBirthDate(new Date()); Session session = sessionFactory.getCurrentSession(); session.beginTransaction(); session.saveOrUpdate(t);//执行save
session.getTransaction().commit(); t.setName("t2"); Session session2 = sessionFactory.getCurrentSession(); session2.beginTransaction(); session2.saveOrUpdate(t);//执行update
session2.getTransaction().commit(); } @Test public void testClear() { Session session = sessionFactory.getCurrentSession(); session.beginTransaction(); Teacher t = (Teacher) session.load(Teacher.class, 1); System.out.println(t.getName()); //get,load都是先从缓存拿
session.clear();//清除缓存,如果没这句,会发一条sql,直接从缓存拿的
Teacher t2 = (Teacher) session.load(Teacher.class, 1); System.out.println(t2.getName()); session.getTransaction().commit(); } @Test public void testFlush() { Session session = sessionFactory.getCurrentSession(); session.beginTransaction(); Teacher t = (Teacher) session.load(Teacher.class, 1); t.setName("tttt"); session.flush();//强制缓存和数据库内容做同步,会发两条sql
t.setName("ttttt"); session.getTransaction().commit();//默认会flush
} @Test public void testSchemaExport() { new SchemaExport(new AnnotationConfiguration().configure()).create( false, true); }
这里的关系映射指的是对象之间的关系,并不是指数据库的关系,本章解决的问题是当对象之间处于下列关系之一时,数据库表该如何映射,编程上该如何对待,具体实例如下:
package com.bjsxt.hibernate; import java.util.HashSet; import java.util.Set; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.OneToOne; //一对一单向
@Entity public class Husband { private int id; private String name; private Wife wife; @Id @GeneratedValue public int getId() { return id; } public String getName() { return name; } @OneToOne @JoinColumn(name="wifeId")//不想用它生成的名字
public Wife getWife() { return wife; } public void setId(int id) { this.id = id; } public void setName(String name) { this.name = name; } public void setWife(Wife wife) { this.wife = wife; } } @Entity public class Wife { private int id; private String name; @Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ps:这样映射,hibernate自动创建的Husband表中有个wifeId字段,是Wife表的外键,wife表和wifi对象一一对应。 //一对一双向
@Entity public class Husband { private int id; private String name; private Wife wife; @Id @GeneratedValue public int getId() { return id; } public String getName() { return name; } @OneToOne @JoinColumn(name="wifeId") public Wife getWife() { return wife; } public void setId(int id) { this.id = id; } public void setName(String name) { this.name = name; } public void setWife(Wife wife) { this.wife = wife; } } @Entity public class Wife { private int id; private String name; private Husband husband; @OneToOne(mappedBy="wife") public Husband getHusband() { return husband; } public void setHusband(Husband husband) { this.husband = husband; } @Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ps:这种一对一双向和单向在数据库中的表现完全一样,只是对象的映射不一样,双向的关系毕设mappedBy. //一对一单双向主键关联,联合主键,主键映射,略,实际工作项目里, 一对一用的很少,因为完全没必要,设计成一张表就可以了 //多对一单向
@Entity @Table(name="t_group") public class Group { private int id; private String name; @Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } @Entity @Table(name="t_user") public class User { private int id; private String name; private Group group; @ManyToOne public Group getGroup() { return group; } public void setGroup(Group group) { this.group = group; } @Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } //一对多单向
@Entity @Table(name="t_group") public class Group { private int id; private String name; private Set<User> users = new HashSet<User>(); @Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @OneToMany @JoinColumn(name="groupId") public Set<User> getUsers() { return users; } public void setUsers(Set<User> users) { this.users = users; } } @Entity @Table(name="t_user") public class User { private int id; private String name; @Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } //多对一,一对多双向
@Entity @Table(name="t_group") public class Group { private int id; private String name; private Set<User> users = new HashSet<User>(); @Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @OneToMany(mappedBy="group") public Set<User> getUsers() { return users; } public void setUsers(Set<User> users) { this.users = users; } } @Entity @Table(name="t_user") public class User { private int id; private String name; private Group group; @ManyToOne public Group getGroup() { return group; } public void setGroup(Group group) { this.group = group; } @Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ps:多对一和一对多一样,并且和一对一生成的数据库表也一样,只是对象映射关系不同。 //多对多单向
@Entity public class Student { private int id; private String name; @Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } @Entity public class Teacher { private int id; private String name; private Set<Student> students = new HashSet<Student>(); @Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @ManyToMany @JoinTable(name="t_s", joinColumns={@JoinColumn(name="teacher_id")}, inverseJoinColumns={@JoinColumn(name="student_id")} ) public Set<Student> getStudents() { return students; } public void setStudents(Set<Student> students) { this.students = students; } } //多对多双向
@Entity public class Student { private int id; private String name; private Set<Teacher> teachers = new HashSet<Teacher>(); @ManyToMany(mappedBy="students") public Set<Teacher> getTeachers() { return teachers; } public void setTeachers(Set<Teacher> teachers) { this.teachers = teachers; } @Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } @Entity public class Teacher { private int id; private String name; private Set<Student> students = new HashSet<Student>(); @Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @ManyToMany @JoinTable(name="t_s", joinColumns={@JoinColumn(name="teacher_id")}, inverseJoinColumns={@JoinColumn(name="student_id")} ) public Set<Student> getStudents() { return students; } public void setStudents(Set<Student> students) { this.students = students; } } ps:多对多生成的表和前面不一样,会多增加一个中间表,@JoinTable配置中间表的表名和字段名。
ps:Hibernate默认将OneToMany理解为ManyToMany的特殊形式,如果不指定生成的外键列@JoinColumn(name="groupId"),则会默认生成多对多的关系,产生一张中间表.
package com.bjsxt.hibernate; import java.util.HashSet; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.ManyToOne; import javax.persistence.Table; @Entity @Table(name="t_user") public class User { private int id; private String name; private Group group; @ManyToOne(cascade={CascadeType.ALL}) public Group getGroup() { return group; } public void setGroup(Group group) { this.group = group; } @Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } @Entity @Table(name="t_group") public class Group { private int id; private String name; private Set<User> users = new HashSet<User>(); @Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @OneToMany(mappedBy="group", cascade={CascadeType.ALL} ) public Set<User> getUsers() { return users; } public void setUsers(Set<User> users) { this.users = users; } } public class HibernateORMappingTest { private static SessionFactory sessionFactory; @BeforeClass public static void beforeClass() { new SchemaExport(new AnnotationConfiguration().configure()).create(false, true); sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory(); } @AfterClass public static void afterClass() { sessionFactory.close(); } @Test public void testSaveUser() { User u = new User(); u.setName("u1"); Group g = new Group(); g.setName("g1"); u.setGroup(g); Session s = sessionFactory.getCurrentSession(); s.beginTransaction(); //s.save(g);
s.save(u); s.getTransaction().commit(); } @Test public void testSaveGroup() { User u1 = new User(); u1.setName("u1"); User u2 = new User(); u2.setName("u2"); Group g = new Group(); g.setName("g1"); g.getUsers().add(u1); g.getUsers().add(u2); u1.setGroup(g); u2.setGroup(g); Session s = sessionFactory.getCurrentSession(); s.beginTransaction(); //s.save(g);
s.save(g); s.getTransaction().commit(); } @Test public void testGetUser() { testSaveGroup(); Session s = sessionFactory.getCurrentSession(); s.beginTransaction(); User u = (User)s.get(User.class, 1); s.getTransaction().commit(); System.out.println(u.getGroup().getName()); } @Test public void testLoadUser() { testSaveGroup(); Session s = sessionFactory.getCurrentSession(); s.beginTransaction(); User u = (User)s.load(User.class, 1); System.out.println(u.getGroup().getName()); s.getTransaction().commit(); } @Test public void testDeleteUser() { testSaveGroup(); Session s = sessionFactory.getCurrentSession(); s.beginTransaction(); //User u = (User)s.load(User.class, 1); //u.setGroup(null); //s.delete(u);
s.createQuery("delete from User u where u.id = 1").executeUpdate(); s.getTransaction().commit(); } @Test public void testDeleteGroup() { testSaveGroup(); Session s = sessionFactory.getCurrentSession(); s.beginTransaction(); //User u = (User)s.load(User.class, 1); //u.setGroup(null); //s.delete(u);
Group g = (Group)s.load(Group.class, 1); s.delete(g); //s.createQuery("delete from User u where u.id = 1").executeUpdate();
s.getTransaction().commit(); } @Test public void testUpdateUser() { testSaveGroup(); Session s = sessionFactory.getCurrentSession(); s.beginTransaction(); User u = (User)s.get(User.class, 1); s.getTransaction().commit(); u.setName("user"); u.getGroup().setName("group"); Session s2 = sessionFactory.getCurrentSession(); s2.beginTransaction(); s2.update(u); s2.getTransaction().commit(); } @Test public void testGetGroup() { testSaveGroup(); Session s = sessionFactory.getCurrentSession(); s.beginTransaction(); Group g = (Group)s.get(Group.class, 1); s.getTransaction().commit(); for(User u : g.getUsers()) { System.out.println(u.getName()); } } @Test public void testSchemaExport() { new SchemaExport(new AnnotationConfiguration().configure()).create(false, true); } public static void main(String[] args) { beforeClass(); } }
package com.bjsxt.hibernate; import java.util.Date; import java.util.List; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entity public class Category { private int id; private String name; @Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } @Entity public class Topic { private int id; private String title; private Category category; //private Category category2;
private Date createDate; public Date getCreateDate() { return createDate; } public void setCreateDate(Date createDate) { this.createDate = createDate; } @ManyToOne(fetch=FetchType.LAZY) public Category getCategory() { return category; } public void setCategory(Category category) { this.category = category; } @Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } } @Entity public class Msg { private int id; private String cont; private Topic topic; @ManyToOne public Topic getTopic() { return topic; } public void setTopic(Topic topic) { this.topic = topic; } @Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } public String getCont() { return cont; } public void setCont(String cont) { this.cont = cont; } } public class HibernateQLTest { private static SessionFactory sf; @BeforeClass public static void beforeClass() { sf = new AnnotationConfiguration().configure().buildSessionFactory(); } @AfterClass public static void afterClass() { sf.close(); } @Test public void testSchemaExport() { new SchemaExport(new AnnotationConfiguration().configure()).create(false, true); } @Test public void testSave() { Session session = sf.openSession(); session.beginTransaction(); for(int i=0; i<10; i++) { Category c = new Category(); c.setName("c" + i); session.save(c); } for(int i=0; i<10; i++) { Category c = new Category(); c.setId(1); Topic t = new Topic(); t.setCategory(c); t.setTitle("t" + i); t.setCreateDate(new Date()); session.save(t); } for(int i=0; i<10; i++) { Topic t = new Topic(); t.setId(1); Msg m = new Msg(); m.setCont("m" + i); m.setTopic(t); session.save(m); } session.getTransaction().commit(); session.close(); } @Test public void testHQL_01() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.createQuery("from Category"); List<Category> categories = (List<Category>)q.list(); for(Category c : categories) { System.out.println(c.getName()); } session.getTransaction().commit(); session.close(); } @Test public void testHQL_02() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.createQuery("from Category c where c.name > 'c5'"); List<Category> categories = (List<Category>)q.list(); for(Category c : categories) { System.out.println(c.getName()); } session.getTransaction().commit(); session.close(); } @Test public void testHQL_03() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.createQuery("from Category c order by c.name desc"); List<Category> categories = (List<Category>)q.list(); for(Category c : categories) { System.out.println(c.getName()); } session.getTransaction().commit(); session.close(); } @Test public void testHQL_04() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.createQuery("select distinct c from Category c order by c.name desc"); List<Category> categories = (List<Category>)q.list(); for(Category c : categories) { System.out.println(c.getName()); } session.getTransaction().commit(); session.close(); } @Test public void testHQL_05() { Session session = sf.openSession(); session.beginTransaction(); /*Query q = session.createQuery("from Category c where c.id > :min and c.id < :max"); //q.setParameter("min", 2); //q.setParameter("max", 8); q.setInteger("min", 2); q.setInteger("max", 8);*/ Query q = session.createQuery("from Category c where c.id > :min and c.id < :max") .setInteger("min", 2) .setInteger("max", 8); List<Category> categories = (List<Category>)q.list(); for(Category c : categories) { System.out.println(c.getId() + "-" + c.getName()); } session.getTransaction().commit(); session.close(); } @Test public void testHQL_06() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.createQuery("from Category c where c.id > ? and c.id < ?"); q.setParameter(0, 2) .setParameter(1, 8); // q.setParameter(1, 8);
List<Category> categories = (List<Category>)q.list(); for(Category c : categories) { System.out.println(c.getId() + "-" + c.getName()); } session.getTransaction().commit(); session.close(); } //分页
@Test public void testHQL_07() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.createQuery("from Category c order by c.name desc"); q.setMaxResults(4); q.setFirstResult(2); List<Category> categories = (List<Category>)q.list(); for(Category c : categories) { System.out.println(c.getId() + "-" + c.getName()); } session.getTransaction().commit(); session.close(); } @Test public void testHQL_08() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.createQuery("select c.id, c.name from Category c order by c.name desc"); List<Object[]> categories = (List<Object[]>)q.list(); for(Object[] o : categories) { System.out.println(o[0] + "-" + o[1]); } session.getTransaction().commit(); session.close(); } //设定fetch type 为lazy后将不会有第二条sql语句
@Test public void testHQL_09() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.createQuery("from Topic t where t.category.id = 1"); List<Topic> topics = (List<Topic>)q.list(); for(Topic t : topics) { System.out.println(t.getTitle()); //System.out.println(t.getCategory().getName());
} session.getTransaction().commit(); session.close(); } //设定fetch type 为lazy后将不会有第二条sql语句
@Test public void testHQL_10() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.createQuery("from Topic t where t.category.id = 1"); List<Topic> topics = (List<Topic>)q.list(); for(Topic t : topics) { System.out.println(t.getTitle()); } session.getTransaction().commit(); session.close(); } @Test public void testHQL_11() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.createQuery("from Msg m where m.topic.category.id = 1"); for(Object o : q.list()) { Msg m = (Msg)o; System.out.println(m.getCont()); } session.getTransaction().commit(); session.close(); } //了解即可 //VO Value Object //DTO data transfer object
@Test public void testHQL_12() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.createQuery("select new com.bjsxt.hibernate.MsgInfo(m.id, m.cont, m.topic.title, m.topic.category.name) from Msg"); for(Object o : q.list()) { MsgInfo m = (MsgInfo)o; System.out.println(m.getCont()); } session.getTransaction().commit(); session.close(); } //动手测试left right join //为什么不能直接写Category名,而必须写t.category //因为有可能存在多个成员变量(同一个类),需要指明用哪一个成员变量的连接条件来做连接
@Test public void testHQL_13() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.createQuery("select t.title, c.name from Topic t join t.category c "); //join Category c
for(Object o : q.list()) { Object[] m = (Object[])o; System.out.println(m[0] + "-" + m[1]); } session.getTransaction().commit(); session.close(); } //学习使用uniqueResult
@Test public void testHQL_14() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.createQuery("from Msg m where m = :MsgToSearch "); //不重要
Msg m = new Msg(); m.setId(1); q.setParameter("MsgToSearch", m); Msg mResult = (Msg)q.uniqueResult(); System.out.println(mResult.getCont()); session.getTransaction().commit(); session.close(); } @Test public void testHQL_15() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.createQuery("select count(*) from Msg m"); long count = (Long)q.uniqueResult(); System.out.println(count); session.getTransaction().commit(); session.close(); } @Test public void testHQL_16() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.createQuery("select max(m.id), min(m.id), avg(m.id), sum(m.id) from Msg m"); Object[] o = (Object[])q.uniqueResult(); System.out.println(o[0] + "-" + o[1] + "-" + o[2] + "-" + o[3]); session.getTransaction().commit(); session.close(); } @Test public void testHQL_17() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.createQuery("from Msg m where m.id between 3 and 5"); for(Object o : q.list()) { Msg m = (Msg)o; System.out.println(m.getId() + "-" + m.getCont()); } session.getTransaction().commit(); session.close(); } @Test public void testHQL_18() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.createQuery("from Msg m where m.id in (3,4, 5)"); for(Object o : q.list()) { Msg m = (Msg)o; System.out.println(m.getId() + "-" + m.getCont()); } session.getTransaction().commit(); session.close(); } //is null 与 is not null
@Test public void testHQL_19() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.createQuery("from Msg m where m.cont is not null"); for(Object o : q.list()) { Msg m = (Msg)o; System.out.println(m.getId() + "-" + m.getCont()); } session.getTransaction().commit(); session.close(); } //is empty and is not empty
@Test public void testHQL_20() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.createQuery("from Topic t where t.msgs is empty"); for(Object o : q.list()) { Topic t = (Topic)o; System.out.println(t.getId() + "-" + t.getTitle()); } session.getTransaction().commit(); session.close(); } @Test public void testHQL_21() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.createQuery("from Topic t where t.title like '%5'"); for(Object o : q.list()) { Topic t = (Topic)o; System.out.println(t.getId() + "-" + t.getTitle()); } session.getTransaction().commit(); session.close(); } @Test public void testHQL_22() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.createQuery("from Topic t where t.title like '_5'"); for(Object o : q.list()) { Topic t = (Topic)o; System.out.println(t.getId() + "-" + t.getTitle()); } session.getTransaction().commit(); session.close(); } //不重要
@Test public void testHQL_23() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.createQuery("select lower(t.title)," +
"upper(t.title)," +
"trim(t.title)," +
"concat(t.title, '***')," +
"length(t.title)" +
" from Topic t "); for(Object o : q.list()) { Object[] arr = (Object[])o; System.out.println(arr[0] + "-" + arr[1] + "-" + arr[2] + "-" + arr[3] + "-" + arr[4] + "-"); } session.getTransaction().commit(); session.close(); } @Test public void testHQL_24() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.createQuery("select abs(t.id)," +
"sqrt(t.id)," +
"mod(t.id, 2)" +
" from Topic t "); for(Object o : q.list()) { Object[] arr = (Object[])o; System.out.println(arr[0] + "-" + arr[1] + "-" + arr[2] ); } session.getTransaction().commit(); session.close(); } @Test public void testHQL_25() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.createQuery("select current_date, current_time, current_timestamp, t.id from Topic t"); for(Object o : q.list()) { Object[] arr = (Object[])o; System.out.println(arr[0] + " | " + arr[1] + " | " + arr[2] + " | " + arr[3]); } session.getTransaction().commit(); session.close(); } @Test public void testHQL_26() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.createQuery("from Topic t where t.createDate < :date"); q.setParameter("date", new Date()); for(Object o : q.list()) { Topic t = (Topic)o; System.out.println(t.getTitle()); } session.getTransaction().commit(); session.close(); } @Test public void testHQL_27() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.createQuery("select t.title, count(*) from Topic t group by t.title") ; for(Object o : q.list()) { Object[] arr = (Object[])o; System.out.println(arr[0] + "|" + arr[1]); } session.getTransaction().commit(); session.close(); } @Test public void testHQL_28() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.createQuery("select t.title, count(*) from Topic t group by t.title having count(*) >= 1") ; for(Object o : q.list()) { Object[] arr = (Object[])o; System.out.println(arr[0] + "|" + arr[1]); } session.getTransaction().commit(); session.close(); } @Test public void testHQL_29() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.createQuery("from Topic t where t.id < (select avg(t.id) from Topic t)") ; for(Object o : q.list()) { Topic t = (Topic)o; System.out.println(t.getTitle()); } session.getTransaction().commit(); session.close(); } @Test public void testHQL_30() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.createQuery("from Topic t where t.id < ALL (select t.id from Topic t where mod(t.id, 2)= 0) ") ; for(Object o : q.list()) { Topic t = (Topic)o; System.out.println(t.getTitle()); } session.getTransaction().commit(); session.close(); } //用in 可以实现exists的功能 //但是exists执行效率高
@Test public void testHQL_31() { Session session = sf.openSession(); session.beginTransaction();// t.id not in (1)
Query q = session.createQuery("from Topic t where not exists (select m.id from Msg m where m.topic.id=t.id)") ; // Query q = session.createQuery("from Topic t where exists (select m.id from Msg m where m.topic.id=t.id)") ;
for(Object o : q.list()) { Topic t = (Topic)o; System.out.println(t.getTitle()); } session.getTransaction().commit(); session.close(); } //update and delete //规范并没有说明是不是要更新persistent object,所以如果要使用,建议在单独的trasaction中执行
@Test public void testHQL_32() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.createQuery("update Topic t set t.title = upper(t.title)") ; q.executeUpdate(); q = session.createQuery("from Topic"); for(Object o : q.list()) { Topic t = (Topic)o; System.out.println(t.getTitle()); } session.createQuery("update Topic t set t.title = lower(t.title)") .executeUpdate(); session.getTransaction().commit(); session.close(); } //不重要
@Test public void testHQL_33() { Session session = sf.openSession(); session.beginTransaction(); Query q = session.getNamedQuery("topic.selectCertainTopic"); q.setParameter("id", 5); Topic t = (Topic)q.uniqueResult(); System.out.println(t.getTitle()); session.getTransaction().commit(); session.close(); } //Native(了解)
@Test public void testHQL_34() { Session session = sf.openSession(); session.beginTransaction(); SQLQuery q = session.createSQLQuery("select * from category limit 2,4").addEntity(Category.class); List<Category> categories = (List<Category>)q.list(); for(Category c : categories) { System.out.println(c.getName()); } session.getTransaction().commit(); session.close(); } }
1、注意session.clear()的运用,尤其在不断分页循环的时候或者导大量数据时,因为取出的对象都是放在session缓存中的.
1>在一个大集合中进行遍历,遍历msg,取出其中的含有敏感字样的对象
2>另外一种形式的内存泄露 ( //面试题:Java有内存泄漏吗?语法级别没有 但是可由java引起,例如:连接池不关闭,或io读取后不关闭)
2、1+N问题 (典型的面试题),在manytoone时,关联对象会再发一条sql取出来,本来一条sql会解决,但是发了1+N条,解决方案如下:
1>@ManyToOne(fetch=FetchType.LAZY) //fetch=FetchType.LAZY 解决N+1问题 说明如下: //当多对一(@ManyToOne)已经设定属性" fetch=FetchType.LAZY "时 //只有当需要时(如:t.getCategory().getName()时)才会去获取关联表中数据 可以解决N+1问题
2>@BatchSize //@BatchSize 解决N+1问题 说明如下: //在与查询表(此例中为Topic类)关联的表类(此例中为Category类)头处加@BatchSize(size=5) //表示每次可查出5条记录 从而减少了select语句的个数
3>join fetch //join fetch 解决N+1问题 说明如下: //修改hql语句为--" from Topic t left join fetch t.category c "
4>QBC //QBC(Query By Criteria) 解决N+1问题 说明如下: //使用QBC的 createCriteria(*.class)执行查询 也可避免N+1问题
3、一级缓存_二级缓存_查询缓存
1、什么是一级缓存,session级别的缓存
2、什么是二级缓存,SessionFactory级别的缓存,可以跨越session存在
3、什么时候适合使用二级缓存:经常被访间,改动不大不会经常改动,数重有限
4、使用二级缓存
1>hibernate.cfg.xml 设定:
<propertyname= "cache.use_second_level_cache">true</property>
<property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
2>@Cache注解(由hibernate扩展提供)到要放到缓存的类上
@Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
3>copy例子工程ehcache.xml 文件到src目录
注:使用EhCache二级缓存 需要导入ehcache-1.2.3.jar及commons-logging-1.0.4.jar包
5、load默认使用二级缓存,iterate默认使用二级缓存
6、list默认往二级缓存加数据,但是查询的时候不使用
7、如果要query用二级缓存,需打开查询缓存
<property name="cache.use_query_cache">true</property>
调用Query的setCachable (true)方法指明使用二级缓存
session.createQuery("from Category").setCacheable(true).list();
实例如下:
// 只发了一条sql,session级一级缓存
@Test public void testCache1() { Session session = sf.openSession(); session.beginTransaction(); Category c = (Category) session.load(Category.class, 1); System.out.println(c.getName()); Category c2 = (Category) session.load(Category.class, 1); System.out.println(c2.getName()); session.getTransaction().commit(); session.close(); } // 发了两条,一个session不能取另一个session的缓存,这种情况其实就模拟了多线程,有性能问题, // 解决方案是二级缓存,所有的session会从这个大大的缓存里取对象,取不到再去数据库中拿, // 设置二级缓存后,跨session的如下这种load也仅会仅发出一条sql,特别考虑性能时才考虑二级缓存
@Test public void testCache2() { Session session = sf.openSession(); session.beginTransaction(); Category c = (Category) session.load(Category.class, 1); System.out.println(c.getName()); session.getTransaction().commit(); session.close(); Session session2 = sf.openSession(); session2.beginTransaction(); Category c2 = (Category) session2.load(Category.class, 1); System.out.println(c2.getName()); session2.getTransaction().commit(); session2.close(); } //查询缓存,查询缓存必须两条语句一样(依赖二级缓存必须打开)
@Test public void testQueryCache() { Session session = sf.openSession(); session.beginTransaction(); List<Category> categories = (List<Category>) session .createQuery("from Category").setCacheable(true).list(); session.getTransaction().commit(); session.close(); Session session2 = sf.openSession(); session2.beginTransaction(); List<Category> categories2 = (List<Category>) session2 .createQuery("from Category").setCacheable(true).list(); session2.getTransaction().commit(); session2.close(); }
ps:缓存是否生效,观察生成的sql即可,一般实际当中先不考虑缓存的问题,先把功能实现,不能满足要求再考虑效率问题.
8、缓存算法:其实就是对象个数满了后把哪个从缓存中拿走
LRU LFU FIFO Least Recently Used –最近很少被使用 Least Frequently Used (命中率高低) First In First Out 按顺序替换 memoryStoreEvictionPolicy = "LRU" (ehcache.xml中配置)
4、事务隔离机制_悲观锁_乐观锁
1>事务的理解参见之前javaEE(11)_事务处理.
2>在并发和效率考虑一个平衡点,因为设置的级别越高,并发效率越低,所以一般设置隔离级别为Read committed,然后去解决不可重复读的问题,虚读不考虑(要解决技术上只能设成Serializable),实际很少使用(如查一次总数,后面又查一次,或查一次平均值又查一次很少这么干),hibernate中解决这个问题,有两种方式悲观锁和乐观锁.
3>悲观锁解决不可重复读,就是直接上个锁,别人不能动,依赖于数据库本身的锁.
@Entity public class Account { private int id; private int balance; //BigDecimal
@Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } public int getBalance() { return balance; } public void setBalance(int balance) { this.balance = balance; } } public class HibernateCacheTest { private static SessionFactory sf; @BeforeClass public static void beforeClass() { sf = new AnnotationConfiguration().configure().buildSessionFactory(); } @AfterClass public static void afterClass() { sf.close(); } @Test public void testSchemaExport() { new SchemaExport(new AnnotationConfiguration().configure()).create(false, true); } @Test public void testSave() { Session session = sf.openSession(); session.beginTransaction(); Account a = new Account(); a.setBalance(100); session.save(a); session.getTransaction().commit(); session.close(); } @Test public void testOperation1() { Session session = sf.openSession(); session.beginTransaction(); Account a = (Account)session.load(Account.class, 1); int balance = a.getBalance(); //do some caculations,如果这个过程,别的事务修改了这个值,就会有读到新的这个值
balance = balance - 10; a.setBalance(balance); session.getTransaction().commit(); session.close(); } @Test public void testPessimisticLock() { Session session = sf.openSession(); session.beginTransaction(); Account a = (Account)session.load(Account.class, 1, LockMode.UPGRADE); int balance = a.getBalance(); //do some caculation,悲观锁解决不可重复读
balance = balance - 10; a.setBalance(balance); session.getTransaction().commit(); session.close(); } public static void main(String[] args) { beforeClass(); } }
testPessimisticLock时生成的sql如下(select ... for update语句将锁住查询结果中的元组,这些元组将不能被其他事务的UPDATE,DELETE和FOR UPDATE操作,直到本事务提交):
4>乐观锁是在程序中解决,与数据库就没关系了,实体类中增加version属性(数据库也会对应生成该字段,初始值为0),并在其get方法前加@Version注解,则在操作过程中每更新一次该行数据则version值加1,即可在事务提交前判断该数据是否被其他事务修改过.(间谍经常用出门前撒粉,回来看有么有动过)
@Entity public class Account { private int id; private int balance; private int version; @Version public int getVersion() { return version; } public void setVersion(int version) { this.version = version; } @Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } public int getBalance() { return balance; } public void setBalance(int balance) { this.balance = balance; } } public class HibernateCacheTest { private static SessionFactory sf; @BeforeClass public static void beforeClass() { sf = new AnnotationConfiguration().configure().buildSessionFactory(); } @AfterClass public static void afterClass() { sf.close(); } @Test public void testSchemaExport() { new SchemaExport(new AnnotationConfiguration().configure()).create( false, true);//生成的表带version字段
} @Test public void testSave() { Session session = sf.openSession(); session.beginTransaction(); Account a = new Account(); a.setBalance(100); session.save(a);//version会自动插入,第一次为0
session.getTransaction().commit(); session.close(); } @Test public void testOptimisticLock() { Session session = sf.openSession(); Session session2 = sf.openSession(); session.beginTransaction(); Account a1 = (Account) session.load(Account.class, 1); session2.beginTransaction(); Account a2 = (Account) session2.load(Account.class, 1); a1.setBalance(900); a2.setBalance(1100); session.getTransaction().commit(); System.out.println(a1.getVersion()); session2.getTransaction().commit();//这行报错,那么实际中怎么处理呢,可以做个记录,待会儿再过来跟新
System.out.println(a2.getVersion()); session.close(); session2.close(); } public static void main(String[] args) { beforeClass(); } }
ps:面试的意义大于实际的意义,除非非常考虑效率问题,否则直接设置为Repeatable read就可解决问题,设定hibernate的事务隔离级别(使用hibernate.connection.isolation配置 取值1、2、4、8).
ps:具体的参考马士兵文档和程序,已经查官方文档。