一、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 不支持自增字段. | |
---|---|---|
sequence | DB2、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 ……
}