Hibernate文档(配置,JPA,关系映射)

一、Hibernate框架

​ Hibernate是开源的全自动的ORM(对象关系映射)框架,它对JDBC进行封装,将实体类与数据库表建立映射关系.Hibernate可以自动生成SQL语句,可以使用对象编程思维来操纵数据库。

​ ORM,即Object-Relational Mapping,它的作用就是在关系型数据库和对象之间做了一个映射。从对象(Object)映射到关系(Relation),再从关系映射到对象。这样,我们在操作数据库的时候,不需要再去和复杂SQL打交道,只要像操作对象一样操作它就可以了(把关系数据库的字段在内存中映射成对象的属性)。

二、搭建开发环境

2.1导入jar包

​ 注:由于使用到mysql数据库,需要导入mysql的驱动jar文件

	<!-- hibernate核心jar包 -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>5.1.10.Final</version>
		</dependency>
		<!-- mysql驱动 -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.39</version>
		</dependency>

2.2、建立对应的实体类

public class Person {
    private Integer id;
    private String name;
    private Integer age;
   	get & set……
}

2.3、创建实体的映射文件

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<!--
    package:表示实体类所在的包
-->
<hibernate-mapping package="com.qfedu.first">
<!--class:设置表和类的映射关系
    name:类名
    table:表名
-->
    <class name="Person" table="t_Person">
        <!--设置主键
            name:实体中的属性
            column:表中的字段
        -->
        <id column="id" name="id">
            <!--代表主键的生成方法
                native : hibernate根据使用的数据库自行选择
            -->
            <generator class="native"></generator>
        </id>
        <!--其他字段-->
        <property name="name" column="name"></property>
        <property name="age" column="age"></property>

    </class>

</hibernate-mapping>

2.4、创建主配置文件

​ 在resource资源目录下创建名为 Person.hbm.xml 的配置文件

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

    <session-factory>

        <!-- 数据库连接配置 -->
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql://localhost:3306/mydb</property>
        <property name="connection.username">root</property>
        <property name="connection.password">root</property>

        <!-- SQL 方言 使用哪种数据库的sql语句 -->
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>

        <!-- 控制台显示sql语句 -->
        <property name="show_sql">true</property>

        <!-- 是否自动创建数据库中表 create 每次运行都重新创建
        	update 没有就创建,有了就修改
          -->
        <property name="hbm2ddl.auto">update</property>
        <!-- 引入映射配置文件 -->
        <mapping resource="com/qfedu/first/Person.hbm.xml"/>

    </session-factory>

</hibernate-configuration>

2.5测试

执行完这段代码之后,数据库的表中会多出一个Hibernate创建的数据库表

public class Demo {
    public static void main(String[] args) {
        Configuration config = new Configuration();

        //读取resource下的hibernate.cfg.xml文件
        config.configure();

        // 创建SessionFactory对象
        SessionFactory sessionFactory = config.buildSessionFactory();

        //获取一个Session对象
        Session session = sessionFactory.openSession();

        Person p = new Person();
        p.setName("haha");
        p.setAge(20);

        //开启事务
        Transaction transaction = session.beginTransaction();

        //添加数据
        session.save(p);

        transaction.commit();//提交事务

        //关闭
        session.close();
        sessionFactory.close();
    }
}

三、配置详解

3.1、配置详解

<!-- package 实体类文件所在的包 
auto-import 表示hql时,是否自动导入包名,默认true,导入
	如果设置为false ,hql:"from com.rr.cfg.User"
-->
<hibernate-mapping package="com.rr.cfg" auto-import="true">
	<!-- class配置类和数据库表的关系 
		name 类名 ;
		table 类对应的表名,可以不写,如果不写,表名和类名相同
	-->
	<!-- 配置中的name属性,都是指的类中的属性名 -->
	
	<class name="User" table="t_user">
		<!-- id表的配置主键  name 表示类中的属性 
			column 数据库表中的字段名,可以不写,如果不写,表中字段名与name的值相同
		-->
		<id name="uid" column="id">
			<!-- 主键的生成方式   
			identity 针对如mysql的自增方式
			sequence 针对如oracle的自增方法
			 native表示自增,根据选用的数据库自动判断,比如,如果是mysql,相当于identity
			 assign 需要自己给主键赋值
			 uuid 自动生成uuid
			  -->
			<generator class="uuid"></generator>
		</id>
		
		<!-- 配置其他字段(非主键字段)
		 name表示类中的属性 
			column 表中的字段名
			type 类型
				java中的类型,必须带上包名,比如java.lang.String
				hibernate中类型,小写的,比如 string
			length 数据所占长度
		
		-->
		<property name="name" column="name" type="string" length="20"></property>
		<property name="age"></property>
		
		
	</class>

</hibernate-mapping>

3.2、生成方式详解

identity采用数据库生成的主键,用于为long、short、int类型生成唯一标识, Oracle 不支持自增字段.
sequenceDB2、Oracle均支持的序列,用于为long、short或int生成唯一标识。需要oracle创建sequence。 seq_name
native根据底层数据库的能力,从identity、sequence、hilo中选择一个,灵活性更强。
increment个是由Hibernate在内存中生成主键,每次增量为1,不依赖于底层的数据库,因此所有的数据库都可以使用
uuid使用一个128-bit的UUID算法生成字符串类型的标识符

3.3、对应的类型详解

在这里插入图片描述

四、基本的crud操作

4.1、添加操作

public class Demo2 {
    public static void main(String[] args) {
        Configuration config = new Configuration();

        //读取resource下的hibernate.cfg.xml文件
        config.configure();

        // 创建SessionFactory对象
        SessionFactory sessionFactory = config.buildSessionFactory();

        //获取一个Session对象
        Session session = sessionFactory.openSession();

        Person2 p = new Person2();
        p.setName("xuanxuan");
        p.setAge(20);

        //开启事务
        Transaction transaction = session.beginTransaction();

        //添加数据
        //session.save(p);
        
        //关闭
        session.close();
        sessionFactory.close();
    }
}

4.2、根据主键删除数据

public class Demo2 {
    public static void main(String[] args) {
        Configuration config = new Configuration();

        //读取resource下的hibernate.cfg.xml文件
        config.configure();

        // 创建SessionFactory对象
        SessionFactory sessionFactory = config.buildSessionFactory();

        //获取一个Session对象
        Session session = sessionFactory.openSession();

        Person2 p = new Person2();
        p.setName("xuanxuan");
        p.setAge(20);

        //开启事务
        Transaction transaction = session.beginTransaction();

       	//p.setId("8a881eb66b77d154016b77d159900000");
        //根据主键删除
        session.delete(p);
        
        //关闭
        session.close();
        sessionFactory.close();
    }
}

4.3、根据id更新数据

public class Demo2 {
    public static void main(String[] args) {
        Configuration config = new Configuration();

        //读取resource下的hibernate.cfg.xml文件
        config.configure();

        // 创建SessionFactory对象
        SessionFactory sessionFactory = config.buildSessionFactory();

        //获取一个Session对象
        Session session = sessionFactory.openSession();

        Person2 p = new Person2();
        p.setName("xuanxuan");
        p.setAge(20);

        //开启事务
        Transaction transaction = session.beginTransaction();

        //根据id更新数据
       	p.setId("8a881eb66b77ea13016b77ea17700000");
       	session.update(p);
        
        //关闭
        session.close();
        sessionFactory.close();
    }
}

4.4、根据主键进行查询映射为实体

public class Demo2 {
    public static void main(String[] args) {
        Configuration config = new Configuration();

        //读取resource下的hibernate.cfg.xml文件
        config.configure();

        // 创建SessionFactory对象
        SessionFactory sessionFactory = config.buildSessionFactory();

        //获取一个Session对象
        Session session = sessionFactory.openSession();

        Person2 p = new Person2();
        p.setName("xuanxuan");
        p.setAge(20);

        //开启事务
        Transaction transaction = session.beginTransaction();

        //根据主键进行查询
        Person2 p2 = session.get(Person2.class, "8a881eb66b77ea13016b77ea17700000");
        System.out.println(p2.getName());
        
        //关闭
        session.close();
        sessionFactory.close();
    }
}

五、关系映射(XML配置)

5.1、一对多和多对一关系映射

​ 部门和员工关系:

​ 一对多:一个部门有多个员工

​ 多对一:多个员工属于一个部门

创建实体类对象:

//部门类
public class Department {
    private Integer did;
    private String dname;
    //private List<Employee>//Hibernate使用list集合数据库中会多出一个序号字段
    private Set<Employee> empSet;
	get & set ……

}
//员工类
public class Employee {

    private Integer eid;
    private String ename;
    //员工属于一个部门
    private Department dept;
	get & set ……

}

一对多映射文件配置

注:加入inverse = true 后,类不维护表关系

​ 以下为部门类的映射配置:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<!--
    package:表示实体类所在的包
-->
<hibernate-mapping package="com.qfedu.one2many">
    <!--class:设置表和类的映射关系
        name:类名
        table:表名
    -->
    <class name="Department" table="t_dept">
        <!--设置主键
            name:实体中的属性
            column:表中的字段
        -->
        <id column="did" name="did">
            <!--代表主键的生成方法
                native : hibernate根据使用的数据库自行选择
            -->
            <generator class="native"></generator>
        </id>
        <!--其他字段-->
        <property name="dname" column="dname"></property>

        <!--
            类上多的关系,使用Set集合,配置中使用set集合
            name:实体中属性名
            table:集合的数据来源表名
            inverse:是否维护表中数据的关系,默认false
            一般针对一的一方,设置不维护
        -->
        <set name="empSet" table="t_emp" inverse="true">
            <!--多的关系 对应的表中的外键-->
            <key column="deptId"></key>
            <!-- 一对多对应的类 -->
            <one-to-many class="Employee"></one-to-many>
        </set>

    </class>

</hibernate-mapping>

多对一映射文件配置

​ 以下为员工类的映射配置:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<!--
    package:表示实体类所在的包
-->
<hibernate-mapping package="com.qfedu.one2many">
    <!--class:设置表和类的映射关系
        name:类名
        table:表名
    -->
    <class name="Employee" table="t_emp">
        <!--设置主键
            name:实体中的属性
            column:表中的字段
        -->
        <id column="eid" name="eid">
            <!--代表主键的生成方法
                native : hibernate根据使用的数据库自行选择
            -->
            <generator class="native"></generator>
        </id>
        <!--其他字段-->
        <property name="ename" column="ename"></property>

        <!--
            多对一的关系
            name:员工类中的属性
            column:表中外键
            class:对应的实体类
        -->
        <many-to-one name="dept" column="deptId" class="Department"></many-to-one>

    </class>

</hibernate-mapping>

5.2多对多关系

例子:

​ 一个学生有多个课程

​ 一个课程有多个学生

学生class

public class Student {
    private Integer sid;
    private String sname;
    private Set<Course> courseSet;
	get & set ……
}

课程class

public class Course {
    private Integer cid;
    private String cname;
    private Set<Student> stuSet;
	get & set ……
}

学生映射文件配置

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<!--
    package:表示实体类所在的包
-->
<hibernate-mapping package="com.qfedu.many2many">
    <!--class:设置表和类的映射关系
        name:类名
        table:表名
    -->
    <class name="Student" table="t_stu">
        <!--设置主键
            name:实体中的属性
            column:表中的字段
        -->
        <id column="sid" name="sid">
            <!--代表主键的生成方法
                native : hibernate根据使用的数据库自行选择
            -->
            <generator class="native"></generator>
        </id>
        <!--其他字段-->
        <property name="sname" column="sname"></property>

        <!--
            类上多的关系,使用Set集合,配置中使用set集合
            name:实体中属性名
            table:集合的数据来源表名
        -->
        <set name="courseSet" table="t_stu_course">
            <!--多的关系 对应的表中的外键-->
            <key column="sid"></key>
            <!-- 多对多对应的类
            column:对应的课程外键字段
            class:课程的实体
             -->
            <many-to-many column="cid" class="Course"></many-to-many>

        </set>

    </class>

</hibernate-mapping>

​ 课程映射文件配置

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<!--
    package:表示实体类所在的包
-->
<hibernate-mapping package="com.qfedu.many2many">
    <!--class:设置表和类的映射关系
        name:类名
        table:表名
    -->
    <class name="Course" table="t_course">
        <!--设置主键
            name:实体中的属性
            column:表中的字段
        -->
        <id column="cid" name="cid">
            <!--代表主键的生成方法
                native : hibernate根据使用的数据库自行选择
            -->
            <generator class="native"></generator>
        </id>
        <!--其他字段-->
        <property name="cname" column="cname"></property>

        <!--
            类上多的关系,使用Set集合,配置中使用set集合
            name:实体中属性名
            table:集合的数据来源表名
        -->
        <set name="stuSet" table="t_stu_course">
            <!--多的关系 对应的课程表中的外键-->
            <key column="cid"></key>
            <!-- 多对多对应的类
            column:对应的外键字段
            class:实体
             -->
            <many-to-many column="sid" class="Student"></many-to-many>

        </set>

    </class>

</hibernate-mapping>

六、懒加载

​ Hibernate中默认是懒加载的,例子如下

        Session session = sessionFactory.openSession();

        Employee employee = session.get(Employee.class, 1);

        //输出员工的名字
        System.out.println(employee.getEname());
        System.out.println("________________________________");
        //输出部门名称
        System.out.println(employee.getDept().getDname());

输出结果和sql语句

Hibernate: select employee0_.eid as eid1_2_0_, employee0_.ename as ename2_2_0_, employee0_.deptId as deptId3_2_0_ from t_emp employee0_ where employee0_.eid=?
张三
________________________________
Hibernate: select department0_.did as did1_1_0_, department0_.dname as dname2_1_0_ from t_dept department0_ where department0_.did=?
开发部门
如果想要取消懒加载,在对应实体的映射文件 **<many-to-one>**  标签中添加 属性    **lazy="false"**   可以取消懒加载取消懒加载后,两句sql语句一起进行查询了
Hibernate: select employee0_.eid as eid1_2_0_, employee0_.ename as ename2_2_0_, employee0_.deptId as deptId3_2_0_ from t_emp employee0_ where employee0_.eid=?
Hibernate: select department0_.did as did1_1_0_, department0_.dname as dname2_1_0_ from t_dept department0_ where department0_.did=?
张三
________________________________
开发部门

七、Hql查询

createQuery语句

7.1、查询表中所有记录

注:在Hibernate中sql语句不可以写 slecte * from 表名,如果只要想表中的一些字段,可以给类起别名。(AS 不能省略

Query query = session.createQuery("select p from Person as p");
public class Demo {
    private static SessionFactory sessionFactory = null;
    public static void main(String[] args) {
        Configuration config = new Configuration();
        // 读取resource下的hibernate.cfg.xml文件
        config.configure();
        // 创建SessionFactory对象
        sessionFactory = config.buildSessionFactory();
        //获取一个Session对象
        Session session = sessionFactory.openSession();
        
        //sql"select * from t_person
        //hql中使用的都是类名和类中的属性名
        Query query = session.createQuery("from Person");
        //查询的记录,放到列表中
        List<Person> list = query.list();
        String name = list.get(1).getName();
        
        System.out.println(name);
        System.out.println(list);
        System.out.println(list.size());

    }
}

7.2、查询表中单个字段

​ 需要取出某个表中的单个列的数据时,可以在**query.list()**使用 list 进行接受返回值。

public class Demo {


    public static void main(String[] args) {
        Configuration config = new Configuration();
        // 读取resource下的hibernate.cfg.xml文件
        config.configure();
        // 创建SessionFactory对象
        SessionFactory sessionFactory = config.buildSessionFactory();

        //获取一个Session对象
        Session session = sessionFactory.openSession();

        //sql"select * from t_person
        //hql中使用的都是类名和类中的属性名
        //Query query = session.createQuery("select p from Person as p");

        Query query = session.createQuery("select p.name from Person as p");
        List<String> list = query.list();

        //查询的记录,放到列表中
        //List<Person> list = query.list();

        System.out.println(list.get(1);
        System.out.println(list);
        System.out.println(list.size());
    }
}

7.3、查询表中多个字段

​ 查询某个表中的多个字段,封装到数组中

public class Demo {
    public static void main(String[] args) {
        Configuration config = new Configuration();
        // 读取resource下的hibernate.cfg.xml文件
        config.configure();
        // 创建SessionFactory对象
        SessionFactory sessionFactory = config.buildSessionFactory();

        //获取一个Session对象
        Session session = sessionFactory.openSession();

        //sql"select * from t_person
        //hql中使用的都是类名和类中的属性名
        //Query query = session.createQuery("select p from Person as p");

        Query query = session.createQuery("select p.name, p.age from Person as p");

        List<Object[]> list = query.list();
        System.out.println(list.get(0)[1]);
        System.out.println(list.get(0)[0]);

    }

}

7.4、查询到的数据直接封装到对象中

​ 注:要在实体类中创建有参构造方法(最好无参也要创建),根据有参构造的参数,在sql语句中 **new 实体(别名.参数)**来映射为实体类

public class Demo {


    public static void main(String[] args) {
        Configuration config = new Configuration();
        // 读取resource下的hibernate.cfg.xml文件
        config.configure();
        // 创建SessionFactory对象
        SessionFactory sessionFactory = config.buildSessionFactory();

        //获取一个Session对象
        Session session = sessionFactory.openSession();

        Query query = session.createQuery("select new Person(p.id ,p.name, p.age)  from Person as p");
        List<Person> list = query.list();
        System.out.println(list);
    }
}

7.5、根据条件进行查询数据

​ 注:语句中的 age 是实体中的属性名, 不是数据库中的字段

public void query2(){

        Configuration config = new Configuration();
        // 读取resource下的hibernate.cfg.xml文件
        config.configure();
        // 创建SessionFactory对象
        SessionFactory sessionFactory = config.buildSessionFactory();
        //获取一个Session对象
        Session session = sessionFactory.openSession();
        Query query = session.createQuery("from Person where age<30 ");

        List<Person> list = query.list();
        System.out.println(list);

    }
占位符使用索引赋值(不常用)
        //? 占位符
        Query query = session.createQuery("from Person where age<? ");
       
        //占位符设置值,第一个参数表示占位符的索引,第二个参数,设置的值
        query.setInteger(0,30);
        
        /**
         * query.setParameter("age",30);可以使用索引来赋值,也可以使用名称来赋值
         */
占位符使用名称赋值
        Query query = session.createQuery("from Person where age <:age");
        //占位符设置值,按照名称来赋值
        query.setInteger("age",30);
        
        /**
         * query.setParameter("age",30);可以使用索引来赋值,也可以使用名称来赋值
         */

7.6、模糊查询

​ 注:模糊查询时候,可以直接写 query.setParameter(0,"%内容%");,如果查询用到是传过来的字符串,则需要使用+号 把String类型的字符串拼接进去

    public void query3(){
        Configuration config = new Configuration();
        // 读取resource下的hibernate.cfg.xml文件
        config.configure();
        // 创建SessionFactory对象
        SessionFactory sessionFactory = config.buildSessionFactory();

        //获取一个Session对象
        Session session = sessionFactory.openSession();

        String name = "轩";

        Query query = session.createQuery("from Person where name like ?");
        //占位符设置值,按照名称来赋值
        query.setParameter(0,"%"+ name +"%");

        List<Person> list = query.list();

        System.out.println(list);
    }

7.7、分组查询


    public void query4(){
        Configuration config = new Configuration();
        // 读取resource下的hibernate.cfg.xml文件
        config.configure();
        // 创建SessionFactory对象
        SessionFactory sessionFactory = config.buildSessionFactory();

        //获取一个Session对象
        Session session = sessionFactory.openSession();

        String name = "轩";

        Query query = session.createQuery("select p.age, count(*) from Person p group by p.age");
        //占位符设置值,按照名称来赋值

        List<Object[]> list = query.list();

        System.out.println(list.get(0)[0]);
    }

7.8、排序

	public void query5(){
        Configuration config = new Configuration();
        // 读取resource下的hibernate.cfg.xml文件
        config.configure();
        // 创建SessionFactory对象
        SessionFactory sessionFactory = config.buildSessionFactory();

        //获取一个Session对象
        Session session = sessionFactory.openSession();

        String name = "轩";

        Query query = session.createQuery("from Person order by age");
        //占位符设置值,按照名称来赋值

        List<Person> list = query.list();

        System.out.println(list);
    }

7.9、分页

 	public void query6(){
        Configuration config = new Configuration();
        // 读取resource下的hibernate.cfg.xml文件
        config.configure();
        // 创建SessionFactory对象
        SessionFactory sessionFactory = config.buildSessionFactory();

        //获取一个Session对象
        Session session = sessionFactory.openSession();

        String name = "轩";

        Query query = session.createQuery("from Person");
        //占位符设置值,按照名称来赋值

        query.setFirstResult(0);//分页数据开始的索引
        query.setMaxResults(2);//每页显示的记录数


        List<Person> list = query.list();

        System.out.println(list);
    }

八、Hql多表查询

注:hibernate的sql语句中:

/**
 * fetch 迫切连接会把查询到的数据直接赋值在实体中,相当于mybatis的嵌套查询,一次查询把所有数据封装到实体中,不用额外再执行一次sql语句。
 */
Query query = session.createQuery(" select d from Department d inner join fetch d.empSet where d.did = 1");
    public static void query(){
        Configuration config = new Configuration();

        //读取resource下的hibernate.cfg.xml文件
        config.configure();

        // 创建SessionFactory对象
        SessionFactory sessionFactory = config.buildSessionFactory();

        //获取一个Session对象
        Session session = sessionFactory.openSession();
	/**
     * 多表查询进行关联时,使用对象中包含的另一个对象进行关联,不用写on
     */
        Query query = session.createQuery(" select d from Department d inner join d.empSet where d.did = 1");

        //查询一条记录使用query.uniqueResult();
        Department d = (Department)query.uniqueResult();

        System.out.println(d);

        //关闭
        session.close();
        sessionFactory.close();
    }

九、锁机制

数据库中借助for update对查询到的数据加锁

​ 注:锁需要先开启事务。在sql中运行 start transaction; 开启事务

mysql> start transaction;
Query OK, 0 rows affected

mysql> select * from user where name='wangwu' for update;

mysql> update user set age=20 where name='wangwu';
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

9.1、悲观锁:

​ 概念:是数据被外界修改保持保守状态,因此,再整个数据处理过程中,将数据牌锁定状态。悲观所的实现,往往依靠数据提供的锁机制(也只有再数据库层的锁机制才能保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。实际开发中悲观锁不经常使用。

   String hqlStr = "from Account where name='zhangsan'";
		Query query = session.createQuery(hqlStr);
		//for update
		query.setLockOptions(LockOptions.UPGRADE);//加锁
		List list = query.list();

9.2、乐观锁

​ 相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制,以操作最大程度的独占性。但随之而来的就是数据库性能的降低,特别是对长事务而言。乐观锁大多是基于数据版本(Version)记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于库表的版本解决方案中,一般是通过为数据库增加 version 字段来实现。

1)、创建实体类

public class Account {
    private Integer id;
    private String name;
    private Integer money;
    private Integer version;//版本号

	get & set ……
}

2)、对应的实体中的映射文件配置

​ 注:使用 来标注版本编号,一定要把这个标签放最前面(主键字段的后面)。

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<!--
    package:表示实体类所在的包
-->
<hibernate-mapping package="com.qfedu.lock">
<!--class:设置表和类的映射关系
    name:类名
    table:表名
-->
    <class name="Account" table="t_account">
        <!--设置主键
            name:实体中的属性
            column:表中的字段
        -->
        <id column="id" name="id">
            <!--代表主键的生成方法
                native : hibernate根据使用的数据库自行选择
            -->
            <generator class="native"></generator>
        </id>
        <!--配置版本号-->
        <version name="version" column="version"></version>
        <!--其他字段-->
        <property name="name" column="name"></property>
        <property name="money" column="money"></property>

    </class>

</hibernate-mapping>

3)、测试

​ session1修改之后,数据库中的版本号(version字段)会自动加1,

​ 会导致sssion2因版本号不一致无法修改,导致报错

public static void demoLock1(){
        Configuration config = new Configuration();
        config.configure();
        SessionFactory sessionFactory = config.buildSessionFactory();

        //创建2个session对象
        Session session1 = sessionFactory.openSession();
        Session session2 = sessionFactory.openSession();

        //开启两个session的事务
        Transaction transaction1 = session1.beginTransaction();
        Transaction transaction2 = session2.beginTransaction();

        //根据id查询映射出2个相同的实体
        Account account1 = session1.get(Account.class, 1);
        Account account2 = session2.get(Account.class, 1);

        //set账户1实体的钱,并进行更新
        account1.setMoney(10000);
        session1.update(account1);
        transaction1.commit();
        session1.close();//关闭

        //set账户1实体的钱,并进行更新
        account2.setMoney(20000);
        session2.update(account1);
        transaction2.commit();
        session2.close();//关闭
    }

十、JPA

10.1、什么是jpa?

​ JPA全称 Java Persistence API,是sun公司针对ORM技术提出的技术规范,用来将POJO按照标准的方式进行持久化,很雷士于JDNC规范。Hibernate最早是以ORM框架形式出来的,用来解决JDBC存在的问题。随着JPA标准的发展和完善,hibernate到后来也开始支持JPA规范,并且能够完全兼容JPA规范。也就说,hibernate是JPA标准的一个实现,还在此基础上增加了一些自己特有的功能。Hibernate 的注解相当对JPA的扩充。简称:Java持久化API

10.2、常用注解

@Entity 注解将一个类声明为实体类,

@Table 为实体类指定对应数据库表

@Id 注解指定主键

@EmbeddedId 指定复合主键

@GenerateValue注解可以定义该标识符的生成策略

​ GenerationType.AUTO 自增,根据数据库,自动选择自增策略

​ GenerationType.IDENTITY 自增,针对mysql/sqlserver等数据库

​ GenerationType.SEQUENCE 针对oracle

例如:

​ @GeneratedValue(strategy=GenerationType.IDENTITY)

@Version 注解用于支持乐观锁版本控制。

@Column 注解将属性映射到列

​ name属性允许将显式指定列的名称。

​ length 属性允许用于映射一个value尤其是对一个字符串值的列的大小。

​ nullable 属性允许该列被标记为NOT NULL生成架构时。

​ unique 属性允许被标记为只包含唯一值的列。

@JoinColumn注解定义关联关系

​ @GenericGenerator hibernate特有的注解,使用hibernate的主键生成策略

10.3例子(单表)

1)、创建实体类
@Entity //实体类
@Table(name = "t_person")  //设置映射的表名
public class Person {
    @Id     //对应表中的主键
    @GeneratedValue(strategy = GenerationType.IDENTITY) //主键值的生成方式 IDENTITY:自增
    @Column(name = "id")    //如果字段名和属性值一致,可以不写
    private Integer id;
    @Column(name = "pname")
    private String name;
    private Integer age;

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public Person() {
    }

    public Person(Integer id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

	get & set ……
}

10.4、一对多,多对一

注:如果想实现xml中invser = true 的功能,在关系注解里,需要配置mapperBy
  mappedBy的值来自Employee(员工)类中的 Department(部门) 对象的属性值
   @OneToMany:一对多
   @ManyToMany:多对多
   @ManyToOne:多对一关系注解中没有 mappedBy 属性。
一对多,一个部门包含多个员工

​ 部门实体类:

@Entity//实体类
@Table(name = "dept")//设置映射的表名
public class Department {

    @Id//主键
    //hibernate 扩展的注解
    // name:生成器的名称
    // strategy:生成器的策略
    @GenericGenerator(name = "mygen" , strategy = "native" )
    @GeneratedValue(generator = "mygen")//根据name值指定一个生成器
    private Integer did;
    private String dname;

    @OneToMany//一对多关系使用次注解
    @JoinColumn(name = "deptId")//指定关联关系中的外键(员工 Employee 实体中的外键)
    /**
     * 如果想实现xml中invser = true 的功能,注解里,需要配置mappedBy
     * mappedBy的值来自Employee(员工)类中的 Department(部门) 对象的属性值
     * 注:使用mappedBy , 就不能使用@JoinColumn
     * @OneToMany(mappedBy = "dept")
     */
    private Set<Employee> empSet;// 一个部门下有多个员工

	get and set ……
}

多对一,多个员工属于一个部门
package com.qfedu.many2many;

import javax.persistence.*;

@Entity//实体
@Table(name = "emp")
public class Employee {

    @Id//主键
    @GeneratedValue(strategy = GenerationType.IDENTITY) //主键值的生成方式 IDENTITY:自增
    private Integer eid;
    private String ename;

    @ManyToOne//多对一使用次注解
    @JoinColumn(name = "deptId")//指定关联关系中的外键
    private Department dept;// 员工属于一个部门

	get & set ……
}

10.5、多对多关系

​ 一个学生有多个课程,一个课程有多个学生

学生实体类
@Entity//实体类
@Table(name = "student")//映射的表名
public class Student {
    @Id//主键
    @GeneratedValue(strategy = GenerationType.IDENTITY)//主键自增
    private Integer sid;
    private String sname;

    @ManyToMany//多对多映射
    /**
     * @JoinTable:配置中间表的表名
     * JoinColumns:和本类中设置的表的关联的外键
     * inverseJoinColumns:和另外一个类中的表关联外键(这里指的是Student类)
     * @JoinColumn:对应的字段
     */
    @JoinTable(name = "stu_course" ,
            joinColumns = {@JoinColumn (name = "sId")},
            inverseJoinColumns = {@JoinColumn(name = "cId")})
    private Set<Course> courseSet;

	get & set ……
}
课程实体类
@Entity//实体类
@Table(name = "course")//映射的表名
public class Course {
    @Id//主键
    @GeneratedValue(strategy = GenerationType.IDENTITY)//主键自增
    private Integer cid;
    private String cname;

    @ManyToMany//多对多映射
    /**
     * @JoinTable:配置中间表的表名
     * JoinColumns:和本类中设置的表的关联的外键
     * inverseJoinColumns:和另外一个类中的表关联外键(这里指的是Student类)
     * @JoinColumn:对应的字段
     */
    @JoinTable(name = "stu_course" ,
            joinColumns = {@JoinColumn (name = "cId")},
            inverseJoinColumns = {@JoinColumn(name = "sId")})

    private Set<Student> stuSet;

	get & set ……
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值