hibernate框架五

hibernate框架五

十.注解及关联关系配置

  1. 引入支持注解的包

    1.png

  2. 使用org.hibernate.cfg.AnnotationConfiguration创建Session工厂

    注:3.x版本有该类,高版本该类不可用

  3. 使用注解标注类

    @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缓存

  1. 一级缓存

    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;
    }
  2. 二级缓存

    二级缓存是进程或集群范围内的缓存,本质是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;
    }
  3. 查询缓存

    通常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;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值