hibernate框架五
十.注解及关联关系配置
引入支持注解的包
使用org.hibernate.cfg.AnnotationConfiguration创建Session工厂
注:3.x版本有该类,高版本该类不可用
使用注解标注类
@Enetity:标注可持久化类
@Table:标注关联表
@Column:标注映射列
@Id:标注OID
@GeneratedValue:主键生成策略
@Transient:在insert和update时,可以忽略的属性值
@OneToOne:一对一
@ManyToOne:多对一
@OneToMany:一对多
@ManyToMany:多对多
@JoinColum:标注关联键
@JoinTable:标注关联表信息
@PrimaryKeyJoinColumn :标注主键关联
等相关注解
4.在hibernate.cfg.xml文件中引入该注解标注的实体类
<mapping class="model.XXX"/>
5.详细开发代码如下
简单标注
注意:如果类名和表名一致,则可省略@Table,如果属性名和字段名一致,则可以省略@Column
注解可以标注在属性上,也可以标注在getter方法上,但不能混用
@Entity @Table(name="STUDENT") public class Student implements Serializable{ @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Integer id; @Column(name="NAME") private String name; @Column(name="PASSWORD") private String password; @Column(name="AGE") private Integer age; @Column(name="SEX") private Integer sex; //省略getter和setter方法 }
一对多标注(Class~Student)
@Entity @Table(name="CLASS") public class Clazz { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Integer id; private String name; //mappedBy相当于inverse,如果不显式配置该属性,则必须使用@JoinColumn指定关联键 @OneToMany(fetch=FetchType.EAGER,mappedBy="clazz") private Set<Student> stus; }
多对一标注(Student~Class)
@Entity @Table(name="STUDENT") public class Student implements Serializable{ @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Integer id; private String name; private String password; private Integer age; private Integer sex; @ManyToOne(fetch=FetchType.EAGER) @JoinColumn(name="CID")//指定关联键 private Clazz clazz; }
多对多(Student~Teacher)
Entity @Table(name="STUDENT") public class Student implements Serializable{ @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Integer id; private String name; private String password; private Integer age; private Integer sex; @ManyToMany(fetch=FetchType.EAGER) @JoinTable( name="STU_TEACH",//中间表名称 joinColumns=@JoinColumn(name="SID"),//主表到中间表的关联键 inverseJoinColumns=@JoinColumn(name="TID"))//中间表到被关联表的关联键 private Set<Teacher> teaches; }
主键一对一关联(Student~IDCard)
@Entity @Table(name="STUDENT") public class Student implements Serializable{ @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Integer id; private String name; private String password; private Integer age; private Integer sex; //主键一对一 @OneToOne(fetch=FetchType.EAGER) @PrimaryKeyJoinColumn//该注解标注使用主键关联 private IDCard card; }
另一方主键一对一关联配置(IDCard~Student)
@Entity @Table(name="IDCARD") public class IDCard implements Serializable{ @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Integer id; private String cardname; //主键一对一 //注意:如果不显式使用mappedBy,则必须使用注解@PrimaryKeyJoinColumn来标注主键关联关系 @OneToOne(fetch=FetchType.EAGER,mappedBy="card") private Student stu; }
外键一对一关联(Student~IDCard)
@Entity @Table(name="STUDENT") public class Student implements Serializable{ @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Integer id; private String name; private String password; private Integer age; private Integer sex; //外键一对一 //注意:这里要显式使用mappedBy @OneToOne(fetch=FetchType.EAGER,mappedBy="stu") private IDCard card; }
另一方外键关联一对一(IDCard~Student)
@Entity @Table(name="IDCARD") public class IDCard implements Serializable{ @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Integer id; private String cardname; //外键一对一 //注意:这里可以看成多的一方,要手动指定外键关系@JoinColumn //当然,这里的@ManyToOne注解,也可以用@OneToOne注解代替 @ManyToOne(fetch=FetchType.EAGER) @JoinColumn(name="SID") private Student stu; }
十一.hiberante缓存
一级缓存
hibernate一级缓存是Session缓存,是事务性缓存,这级别的缓存由hibernate直接管理,一般情况下无需干预.
每个事务都有单独的第一级缓存,不会出现并发问题,无需提供并发访问策略.
一级缓存的物理介质是内存,处于一级缓存中的对像永不会过期,除非应用程序显式表空缓存或清除特定对像
测试代码如下:结果只发送一条Sql语句,说明一级缓存默认启用
/* * 一级缓存是ID缓存 */ public Student selectById(int id){ Session session = DBUtil.findSession(); Transaction tx = session.beginTransaction(); Student stu = null; try { //第一次从Session中取值 stu = (Student) session.get(Student.class,id); //第二次从Session中取值 Student stu2 = (Student) session.get(Student.class,id); System.out.println(stu+","+stu2); tx.commit(); } catch (Exception e) { e.printStackTrace(); tx.rollback(); } return stu; }
二级缓存
二级缓存是进程或集群范围内的缓存,本质是SessionFactory的外置缓存.可以被多个Session共享
二级缓存的查询步骤一般为下:
- 条件查询时,发送一条Sql语句查询数据库,获取所要的数据
- 把获得的数据对像的OID放入二级缓存中.
- 当hibernate根据OID访问数据时,首先从一级缓存Session中找,如果找不到,发现二级缓存开启,则从二级缓存中查找,如果还查找不到,则会发送Sql语句,查询数据库.
- 查询数据库后,会把查找的数据对像的OID放入缓存,以备下次查询时使用
- 删除,更新,添加数据时,会刷新二级缓存
二级缓存的并发访问策略:
当多个事务同时访问二级缓存中的数据时,会出现并发问题,要使用hibernate提供的事务并发访问策略,来保证特定事务的隔离级别.
并发访问策略如下:
- read-only:只读型,缓存不会更新,适用于不发生改的数据,效率高,事务隔离级别最低.
- nonstrict-read-write:非严格读写型,缓存会不定期更新,适用于变化频率低的数据,对于极少被修改,允许偶尔脏读的数据,可以使用这种并发访问策略
- read-write:严格读写型,缓存在数据变化时,会触发更新操作,适用于经常被读,但很少修改的情况,可以防止脏读
- transactional:支持事务,效率最低,事务隔离级别最高,可以防止脏读和不可重复读
二级缓存的使用步骤:
- 需要在hibernate.cfg.xml文件中开启二级缓存,及指定二级缓存的供应商,这里以Ehcache缓存为例
<!-- 二级缓存开关 --> <property name="hibernate.cache.use_second_level_cache">true</property> <!-- 指定二缓存的供应商 --> <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
- 把ehcache.xml放在src下,并配置,设置缓存管理策略
<ehcache> <diskStore path="java.io.tmpdir"/> <!--设置二级缓存管理策略 --> <!--maxElementsInMemory 缓存空间大小 eternal缓存会不会过期 timeToIdleSeconds允许空闲对像在缓存中生存的最长时间 timeToLiveSeconds缓存中对像允许生存的最长时间 overflowToDisk:把溢出的数据写入磁盘 --> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" /> </ehcache>
- 使用注解方式时,要使用@Cache注解来定义缓存的并发访问策略
@Entity @Table(name="STUDENT") //并发访问策略 @Cache(usage=CacheConcurrencyStrategy.READ_ONLY) public class Student implements Serializable{ }
- 测试代码
可以发现两个session只发送一条Sql语句,说明Session共享二级缓存空间的数据
public Student selectByCache2(int id){ //创建第一个Session Session session = DBUtil.findSession(); Transaction tx = session.beginTransaction(); Student stu = (Student) session.get(Student.class,id); tx.commit(); //创建第二个Session Session session2 = DBUtil.findSession(); Transaction tx2 = session2.beginTransaction(); Student stu2 = (Student) session2.get(Student.class,id); tx2.commit(); System.out.println(stu+","+stu2); return null; }
查询缓存
通常hibernate会对对像的OID进行缓存操作,如果想对其他查询条件进行缓存,则需要使用查询缓存
查询缓存依赖于二级缓存的开启
查询缓存的生命周期是不确定的,当关联的表发生改变时,查询缓存的生命周期即结束
查询缓存的使用步骤如下:
- hibernate.cfg.xml文件配置查询缓存
<!-- 使用查询缓存 --> <property name="hibernate.cache.use_query_cache">true</property>
- 在代码中使用setCachable(true)方法
测试代码如下
public Student selectByCache3(String name){ Session session = DBUtil.findSession(); Transaction tx = session.beginTransaction(); Student stu = null; try { //按普通属性name进行缓存 Query query = session.createQuery("from Student where name = ?"); query.setString(0, name); //使用查询缓存 query.setCacheable(true); stu = (Student) query.uniqueResult(); //再一次根据name去查询 Query query2 = session.createQuery("from Student where name = ?"); query2.setString(0, name); //使用查询缓存 query2.setCacheable(true); Student stu2 = (Student) query2.uniqueResult(); System.out.println(stu+","+stu2); tx.commit(); } catch (Exception e) { e.printStackTrace(); tx.rollback(); } return stu; }