Hibernate

 


概念

    Hibernate是数据访问层框架,对JDBC进行了封装,是针对数据库访问提出的面向对象的解决方案。使用Hibernate可以直接访问对象,Hibernate自动将此访问转换为SQL执行,从而达到间接访问数据库的目的。

Hibernate与Mybatis区别

1.共同点

1.都对JDBC进行了封装,都属于轻量级数据库持久层框架。
2.都采用ORM(Object Relational Mapping)思想解决了Entity和数据库的映射问题。

2.不同点

1.MyBatis采用SQL与Entity映射,对JDBC封装较轻;Hibernate采用数据库与Entity映射,对JDBC封装较重。
2.MyBatis需要自己写SQL,灵活性高;Hibernate自动生成SQL,开发效率高。

3.Hibernate的使用步骤

1.导入Hibernate相关JAR文件和数据库驱动JAR文件
2.创建Hibernate配置文件:hibernate.cfg.xml
3.创建实体类并使用Hibernate注解进行映射
4.使用Hibernate常用API执行增删改查等操作

Hibernate环境搭建

     环境搭建步骤如下:  

  • 导入Hibernate相关JAR包和MySQL数据库驱动JAR包文件。

Hibernate_第1张图片

  • 在src类路径下创建Hibernate核心配置文件     

          Hibernate的配置文件是一个XML文件,通常命名为hibernate.cfg.xml。该文件中可以配置数据库连接参数、Hibernate框架参数以及映射关系文件或映射实体。




    
        
        
            
            
            jdbc:mysql://localhost:3306/hibernate?serverTimezone=Aisa/Shanghai&useUnicode=true&characterEncoding=utf-8
        
        
        
            com.mysql.cj.jdbc.Driver
        
        root
        root
         
        20
        
        1
        
        5000
        
        100
        3000
        2
        true
        
        org.hibernate.dialect.MySQL57Dialect
        
        create
        
        true
        
        true
        
        
    
  • 创建测试类

       创建实体类:

/**
 * 学生实体类
 * @author luckyliuqs
 */
@Entity
@Table(name = "stu")
public class Student implements Serializable{
    private static final long serialVersionUID = 1L;
	
    @Id
    @Column(name = "stu_id")
    private Integer id;          //Id
    @Column(name = "stu_name",length = 20,nullable = false)
    private String name;         //姓名
    @Column(name = "stu_sex")
    private Character sex;       //性别
    @Column(name = "stu_age")
    private Integer age;         //年龄
    @Column(name = "stu_addr")
    private String address;      //地址    
	
    public Student() {}

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Character getSex() {
        return sex;
    }
    public void setSex(Character sex) {
        this.sex = sex;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public Address getAddress() {
        return address;
    }
    public void setAddress(Address address) {
        this.address = address;
    }
}

       创建嵌入类:

/**
 * 地址嵌入类
 * @author luckyliuqs
 */
@Embeddable
public class Address {
    @Column(name = "detail",unique = true)
    private String detail;       //地址详情
    @Column(name = "postal_code")
    private String postalCode;   //邮编 
    public String getDetail() {
        return detail;    
    }
    public void setDetail(String detail) {
        this.detail = detail;
    }
    public String getPostalCode() {
		return postalCode;
    }
    public void setPostalCode(String postalCode) {
        this.postalCode = postalCode;
    }
}

       通过SessionFactory获取数据库连接的session

public class HibernateTest {
    public static void main(String[] args) {
        //1.创建Hibernate配置对象,用于加载Hibernate核心配置文件
        Configuration cfg = new Configuration();
        /**
         * 2.加载Hibernate核心配置文件
         *   注意:默认加载src类路径下名为hibernate.cfg.xml文件
         */
        cfg.configure();
        //3.创建SessionFactory对象
        SessionFactory factory = cfg.buildSessionFactory();
        //4.获取Session对象
        Session session = factory.openSession();
        System.out.println(session);
    }
}

运行结果为:

Hibernate_第2张图片

     可以看见,通过配置,Hibernate执行了创建表,获取到了Session。

Hibernate注解

如下:

@Entity 该注解在实体类上使用,用于表示该类为一个实体类,拥有该注解的类Hibernate框架会自动进行ORM映射
@Id

该注解在实体类属性定义上或属性对应的get访问方法上使用,用于表示该属性为主键属性

1.Hibernate要求一个实体类必须至少拥有一个主键属性

2.如果有两个和两个以上的主键属性,则要求实体类必须实现Serializable接口

@Table

该注解在实体类上使用,用于指定映射的数据库表的表名。默认表名为实体类的类名

@Table(name = "表名")

@Column

该注解在实体类属性上或属性对应的get访问方法上使用,用于指定表的字段。

name:定义字段名,默认字段名是属性的名称

length:定义字段长度,默认长度255

nullable:定义字段是否为空,true表示为空,false表示不为空。默认true。

unique:定义字段是否唯一性,true表示唯一,false表示不唯一。默认false。

insertable:定义是否允许字段可插入数据,true表示允许,false表示不允许,默认true。

updatable:定义字段是否允许字段可更新数据,true表示允许,false表示不允许,默认true。

@Column(name = "字段名", length=20, nullable=false, unique=true, insertable=true,updatable=true)

@Embeddable

该注解在嵌入类上使用,用于实体类引用嵌入类使用。

适用情况:很多实体类拥有共同的属性时,定义嵌入类,让实体类引用。

@Embedded

该注解效果等价于@Embeddable,在实体类的属性上使用。
@EmbeddedId

该注解在属性上使用,用于指定联合主键类。

注意:①联合主键类必须实现Serializable接口。②要有无参构造方法。③重写hashCode和equals方法

@GeneratedValue

该注解在属性上使用,用于指定主键生成策略。

strategy属性:属于JPA的注解

GenerationType.IDENTITY:根据底层数据库实现自增长

GenerationType.AUTO:跟GenerationType.TABLE策略一样

GenerationType.SEQUENCE:采用序列来实现自增长

GenerationType.TABLE:默认值,使用指定表来决定主键的取值,结合@TableGenerator注解使用

如:@GeneratedValue(strategy = GenerationType.IDENTITY)

generator属性:指定注解生成器的名字,通常跟Hibernate的@GenericGenerator注解一起使用。其值跟             

                          @GenericGenerator注解的name属性值一致

    @Id

    @GeneratedValue(generator="id_gen")

    @GenericGenerator(name="id_gen", strategy="xxx")

    private Long id;

@GenericGenerator

该注解在属性上使用,用于指定Hibernate主键生成策略。

name属性:指定生成器的名称,需要跟@GeneratedValue注解的generator属性值一致

strategy属性:指定Hibernate主键生成策略,取值如下:

       ①identity:采用数据库自增长机制生成主键,适用于Oracle之外的其他数据库

       ②sequence:采用序列的方式生成主键,适用于Oracle数据库

       ③native:根据当前配置的数据库方言,自动选择sequence或identity

       ④assigned:不负责生成主键,将来由程序员编程处理主键生成

       ⑤uuid:采用uuid算法生成的一个主键,生成的主键值为一个不规则的长数字。

                     注意:uuid算法可以保证主键不重复,但是没有规律,因此将来不能按照主键进行排序,只能应用在字符串类型的主键属性上。

       ⑥increment:不是采用数据库自身的机制生成主键,而是Hibernate提供的一种生成主键的方式,它会通过获取当前表中主键的最大值,然后+1作为新的主键值

                     注意:在并发量高的时候,此方式可能会出现线程并发安全问题,从而生成重复的主键,因此不推荐使用。

@Transient 该注解在属性上使用,用于指定属性不会被映射成数据库表里面的字段,只是在程序中使用。
@OnToOne 关联注解,该注解在属性上或对应的get方法上使用,用于指定一对一关联关系
@OnToMany 关联注解,该注解在属性上或对应的get方法上使用,用于指定一对多关联关系
@ManyToOne 关联注解,该注解在属性上或对应的get方法上使用,用于指定多对一关联关系
@ManyToMany 关联注解,该注解在属性上或对应的get方法上使用,用于指定多对多关联关系
@Inheritance

该注解在实体类上使用,用于指定继承策略

strategy属性:取值如下:

    InheritanceType.SINGLE_TABLE:表示所有子类一张表,使用单表继承策略。(建议)

    InheritanceType.TABLE_PER_CLASS:表示每个子类一张表,主键不能使用自增方式,

    InheritanceType.JOINED:表示父类一张表,子类一张表。父表中定义公共的字段,每个子表定义自己特有字段,字表通过主键(也是外键)引用父表的主键。

@DiscriminatorColumn

该注解在实体类上使用,用于指定单表继承鉴别列元信息,该注解常与@Inheritance注解一起使用

name属性:指定鉴别列的列名

discriminatorType:用于指定鉴别列的类型,取值如下:

      DiscriminatorType.CHAR:字符型

      DiscriminatorType.INTEGER:整数类型

      DiscriminatorType.STRING:字符串类型(常用)

@DiscriminatorValue

该注解在实体类上使用,用于指定单表继承鉴别列的值。

注:单表继承策略时使用该注解

 

缓存

一级缓存

  • 概念

         Hibernate创建每个Session对象时,都会给该Session分配一块独立的缓存区,用于存放该Session查询出来的对象,这个分配给Session的缓存区称之为一级缓存,也叫Session级别的缓存

  • 使用一级缓存原因

         Session取数据时,会优先向缓存区取数据,如果存在数据则直接返回,不存在则去数据库查询。这样降低了数据库的访问次数,提高了代码的运行效率。

  • 特点
1.一级缓存是Session独享的, Session不共享缓存,每个Session不能访问其他Session的缓存区。
2.Session的save、update、delete操作会触发缓存更新
3.缓存不能手动关闭,只能被清理
注意:Hibernate默认开启一级缓存,使用Hibernate框架必须使用其一级缓存特性。
  • 清理缓存方法

void evict(Object obj)

将给定对象从一级缓存中驱逐

void clear()

将一级缓存中所有对象进行驱逐

void close() 关闭Session

二级缓存

  • 概念

    二级缓存类似于一级缓存,可以缓存对象,但它是SessionFactory级别的缓存,由SessionFactory负责管理啊。因此,二级缓存的数据是Session间共享的,不同的Session对象都可以共享二级缓存中的数据。

  • 使用

      ①导入ehcache相关jar包:

Hibernate_第3张图片

      ②在Hibernate配置文件中开启二级缓存:


true


    org.hibernate.cache.ehache.internal.EhcacheRegionFactory

       ③在映射实体类上使用@Cache注解

         该注解可以在类上、属性上和方法上使用,用于指定二级缓存策略。

          usage属性指定二级缓存策略,取值如下:

read-only(只读型)常用 缓存不会更新,适用于不会发生改变的数据,效率最高,事务隔离级别最低。
read-write(读写型) 缓存会不定期更新,适用于变化频率低的数据。
nostrict-read-write(不严格读写型)常用 缓存会在数据变化时更新,适用于变化的数据。
transcational 缓存会在数据变化时更新,并支持事务,效率最低,事务隔离级别最高。

查询缓存

     查询缓存又叫三级缓存,依赖于二级缓存,可以理解为特殊的二级缓存,也是SessionFactory级别的,也是SessionFactory负责维护。特点如下:

  • 特点

          查询缓存可以缓存任何查询到的结果

  • 原理

         查询缓存已hql为key、缓存该hql查询到的整个结果。即如果查询2次同样的hql,那么第二次执行时,此次查询可以从插叙你换成中取到第一次查询缓存的内容。

  • 使用场景

         频繁使用同样的hql做查询。

  • 使用

        在Hibernate配置文件中,配置使用查询缓存的配置如下:

//开启查询缓存
true

     然后在执行HQL查询时,调用query.setCacheable(true)方法,开启此次查询使用查询缓存。

 

 

Session的方法

1.添加

        save()

2.删除

       delete()

3.更新

       update()

4.查询

4.1单个对象查询

get() 非延迟加载获取指定对象
load() 延迟加载获取指定对象

4.2多个对象查询

        list()和iterate()。iterate()具有延迟加载。

延迟加载

概念 

    在使用某些Hibernate方法查询数据时,Hibernate返回的只是一个空对象,并没有真正的查询数据库,而是在使用该对象时才会触发查询数据库,并将查询到的数据注入到该对象中,这种查询时机推迟到对象访问的机制称之为延迟加载。

     比如前面提到的Session的get()和load()方法:

  • 非延迟加载的get()方法
/**
 * 测试Session的get和load方法
 */
@Test
public void test() {
    Session session = HibernateUtil.getSession();
    Student stu = session.get(Student.class, 1);
    session.close();
}

       运行效果如下:

Hibernate_第4张图片

  • 延迟加载的load()方法
/**
 * 测试Session的get和load方法
 */
@Test
public void test() {
    Session session = HibernateUtil.getSession();
    Student stu = session.load(Student.class, 1);
    session.close();
}

运行效果如下:

Hibernate_第5张图片

    可以看出,load的延迟加载,在对象没有使用的时候没有去数据库进行查询操作。

 

Hibernate对象的3种状态

          在Hibernate中,可以把实体对象看成有3种转态,分别为临时态、持久态和游离态。如下所示:

Hibernate_第6张图片

      上述调用的都是Session的方法。garbage表示垃圾回收器回收对象。描述如下:

临时态

通过new创建的对象处于临时态。

持久态

持久态的对象与Session进行了关联。

调用Session的get()、load()、list()和iterate()方法获取的对象直接是持久态。

垃圾回收器不能回收持久态的对象

游离态 调用Session的evict()、clear()和close()方法,将对象从持久态变为游离态。
特点:临时态和游离态的对象未与Session进行关联,可以被垃圾回收。

       对象与Session关联后,具有持久化的能力(修改会修改数据库对应的值)。如持久态:

/**
 * 测试对象从临时态到持久态的转换
 */
@Test
public void test1() {
    Session session = HibernateUtil.getSession();
    session.beginTransaction();
    Student stu = new Student();
    stu.setName("Yoyo");
    stu.setAge(17);
    stu.setSex('女');
    stu.setAddress("xxx市");
    //对象从临时态变为持久态
    session.save(stu);
		
    //由于stu对象为持久态具有持久化的能力,因此修改对象属性的值也等于修改数据库列的值
    stu.setAge(10);
		
    session.getTransaction().commit();
}

        运行后,数据库中储存该学生的年龄信息为10,而不是17.

       

关联映射

1.关联注解

@OneToOne

该注解在类属性上或者get方法上使用,用于指定一对一的关联关系。

cascade属性:指定级联操作,其值如下:

      CascadeType.ALL:表示拥有所有的级联操作。

fetch属性:指定抓取策略,只针对查询操作。其值如下:fetch属性默认为EAGER

      FetchType.EAGER:EAGER(渴望的),表示没有使用的时候都会查出来

      FetchType.LAZY:懒加载,使用的时候才会查询。

如:@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)

@OneToMany

该注解在类属性上或者get方法上使用,用于指定一对多的关联关系

其属性和@OneToOne一样。fetch属性默认为LAZY

@ManyToOne

该注解在类属性上或者get方法上使用,用于指定多对一的关联关系

其属性和@OneToOne一样。fetch属性默认为EAGER

@ManyToMany

该注解在类属性上或者get方法上使用,用于指定多对多的关联关系

其属性和@OneToOne一样。fetch属性默认为LAZY

@JoinColumn

该注解在类属性上或get方法上使用,用于指定关联外键列的元信息。

name属性:指定外键列的列名,默认的外键列名为主控方中的关联被控方的属性名+"_"+被控方的主键属性名。

如:@JoinColumn(name = "wife_id")

@JoinTable

该注解在类属性上或get方法上使用,用于指定多对多关系关系表元信息。

name属性:指定多对多关系表的表名。

joinColumns属性:指定关系表关联自己的外键列的元信息

inverseJoinColumn属性:指定关系表关联对方的外键列的元信息

如:

@JoinTable(name="teacher_student",joinColumns {@JoinColumn(name="studentId")},inverseJoinColumns = {@JoinColumn(name="teacherId")})

private Set stus;

2.单向关联

 

3.双向关联

      一对一和多对多双向关联,其中一方交出关系维护权;多对一和一对多双向关联,"一方"交出关系维护权(不是多方交)。

  • mappedBy属性     

      mappedBy的意思就是“被映射”,用于交出关系的维护权,即mappedBy这方不用管关联关系,关联关系交给另一方处理。只有OnToOne、OneToMany和ManyToMany上才有mappedBy属性,ManyToOne不存在该属性;

      mappedBy的值为对方关联己方的属性名。

 

HQL

概念

       HQL(Hibernate Query Language)属于Hibernate的查询语言。

与SQL区别

       SQL面向表,HQL面向实体类。HQL中的?从0开始,JDBC的?从1开始。HQL中可以追加查询条件,条件中写的是实体类属性名,之后在执行查询前用query对象为条件参数赋值,例如:

Query query = session.createQuery(“from Emp where name sex = ?”,Emp.class);
query.setString(0,1);
List emps = query.list();

使用

  • 查询所有字段

      查询所有字段可以省略前面的select语句,query.list()方法返回的集合中封装的是实体对象。如下所示:

@Test
public void get() {
    Session session = HibernateUtil.getSession();
    String hql = "from Emp where sex = ?0 and ename like ?1";
    Query query = session.createQuery(hql, Emp.class);
    query.setParameter(0, 2);
    query.setParameter(1, "%O%");
    List empList = query.list();
    for (Emp emp : empList) {
        System.out.println(emp);
    }
    session.close();
}
  • 查询部分字段

        使用HQL查询表中部分字段时,需要在from前面追加select语句,并指定需要查询列对应的属性名,例如: 

String hql = “select name,age from Emp”;

        注意:当显示的查询部分字段时,query.list()方法返回的集合中封装的不再是实体对象,而是Object[]数组对象,数组中的值的顺序与select语句后查询字段的顺序一致。如下所示:

@Test
public void get2() {
    Session session = HibernateUtil.getSession();
    String hql = "select eno,ename from Emp ";
    Query query = session.createQuery(hql, Object[].class);
    List emps = query.list();
    for (Object[] o : emps) {
        System.out.println(o[0]+" " + o[1]);
    }
}

       测试打印结果如下:可以看出,前面的是eno,后面的是ename,与查询字段的顺序一致

7369 SMITH
7499 ALLEN
7521 WARD
7566 JONES
7654 MARTIN

 

分页查询

       Hibernate的分页查询不是通过HQL条件实现,而是通过提供的方法实现,可以通过调用方法来设置分页的起点和每页显示的行数,例如:

@Test
public void test() {
    Session session = HibernateUtil.getSession();
    String hql = "from Emp where sex = :sex order by age asc";
    Query query = session.createQuery(hql, Emp.class);
    query.setParameter("sex", 2);
    //页码
    int page = 1;
    //每页数据量
    int pageSize = 2;
    //记录开始下标
    int first = (page - 1) * pageSize;
    //设置分页的起始位置,从0开始计数
    query.setFirstResult(first);
    //设置每页数据量
    query.setMaxResults(pageSize);
    List emps = query.list();
    for (Emp emp : emps) {
        System.out.println(emp);
    }
}

     注意:Hibernate中行数的起点从0开始计算,不同于JDBC中的从1开始计算

多表联合查询

定义

         Hibernate支持使用HQL进行多表联合查询,不过HQL中写的是关联对象及属性名

注意事项

1.使用HQL进行关联查询时,query.list()方法返回的集合中封装的不再是实体对象,而是Object[]数组对象。
2.使用HQL进行关联查询时,其抓取策略不受利用@Fetch注解指定的抓取策略的影响,默认为两条select语句进行查询。若要进行join方式关联查询,需要通过HQL实现多表联合查询。

实现多表联合查询方式

       Emp代表员工实体类,Dept代表部门实体类

  • 对象方式关联

         如下所示:

String hql = “select e.id, e.name, e.age, e.sex, e.salary, d.id, d.name from Emp e,Dept d where 
            e.dept.id = d.id”
  • join方式关联

         如下所示:Emp实体类中有dept(部门)属性

String hql = “select e.id, e.name, e.age, e.sex, e.salary, d.id, d.name from Emp e inner join e.dept d”

         注意: join时,不能直接join对象,需要join自身关联的属性

  • select子句关联

          如下所示:

String hql = “select id, name, age, sex, salary, dept.id, dept.name from Emp”
  • 直接使用SQL查询

         若业务太复杂,无法通过HQL实现查询功能,Hibernate也支持使用SQL进行查询,参考代码如下:

SQLQuery sqlQuery = session.createSQLQuery("select e.id,e.name,e.dept_id from emp e,dept d where 
                    e.dept_id = d.id");
//将结果类型封装成指定的类型,默认结果类型为Object[]数组类型
sqlQuery.addEntity(Emp.class);

 

Hibernate Validator校验框架

注意事项

     1.需要在Controller控制器类的控制方法的实体Bean前添加@Valid或者@Validated注解,表示该实体Bean参数需要被校验。若想要使用分组校验,则只能使用@Validated注解来进行特定组校验。

     2.添加Errors或者BindingResult参数,用来接收校验的错误信息,必须紧跟在校验的实体Bean参数后面。

     3.所有的校验注解都必须配合@NotNull校验注解一起使用,因为若元素为空值(NULL),则除了@NotNull以外的校验注解都会失效。

      4.被@Valid或者@Validated注解的实体Bean参数,Spring默认会将该实体Bean参数放入到模型中,默认的模型名为实体Bean类名首字母小写,将来视图组件(如jsp)可以通过该模型名获取对应的模型数据。

常用注解

       这些注解位于javax.validation.constraints包中。

  • @Null

        用于校验被验证的属性值为空值(NULL),该注解在属性定义前或对应的get方法上使用,

  • @NotNull

       用于校验被验证的属性值不为空值(NULL),该注解在属性定义前或对应的get方法上使用,

  • @NotEmpty

        用于校验被验证的字符串或集合不为空值(NULL)或者空字符串(""),该注解在属性定义前或对应的get方法上使用,

  • @NotBlank

        用于校验被验证的字符串不能为空值或者空字符串(去掉首尾空白),该注解在属性定义前或对应的get方法上使用,

@NotBlank(message = "用户名不能为空")
private String username;
  • @Size

        用于校验被验证的字符串长度符合指定的规则,校验集合中元素的个数符合指定的规则。会去掉首尾空白

        有两个属性:min指定最小长度,max指定最大长度。

@Size(min = 6,max = 10,message = "密码长度必须介于{min}-{max}之间")
private String password;
  • @Pattern

       被校验的元素必须符合指定的正则表达式规则。如下:

@Pattern(regexp = "[A-Z][A-Za-z0-9]{5,14]")
private String username;
  • @Min

         被校验的元素必须是数字(整数),其值必须大于等于指定的最小值。

  • @Max

         被校验的元素必须是数字(整数),其值必须小于等于指定的最大值。

  • @DecimalMin

         被校验的元素必须是数字(double或BigDecimal),其值必须大于等于指定的最小值。

  • @DecimalMax

         被校验的元素必须是数字(double或BigDecimal),其值必须小于等于指定的最大值。

  • @Digts(integer, fraction)

           被校验的元素必须是数字,其值必须在可接受的范围。integer指定整数位位数,fraction指定小数位位数。

  • @Past

 

  • @Future

 

  • @Email

 

自定义校验注解

 

分组校验

       当多个操作共用同一个实体类时,使用@Validated注解来进行特定组校验。

 

 

 

 

 

 

 

 

你可能感兴趣的:(后端框架)