Hibernate JPA 快速入门
1、ORM 的介绍
1、什么是 ORM?
ORM(Object-Relational Mapping) 表示对象关系映射。在面向对象的软件开发中,通过ORM,就可以把对象映射到关系型数据库中。只要有一套程序能够做到建立对象与数据库的关联,操作对象就可以直接操作数据库数据,就可以说这套程序实现了ORM对象关系映射。简单的说:ORM 就是建立实体类和数据库表之间的关系,从而达到操作实体类就相当于操作数据库表的目的(就是说ORM会把数据表映射成一个Java对象,使开发人员可以关注Java程序)
2、为什么要使用 ORM?
当实现一个应用程序时(不使用O/R Mapping),我们可能会写特别多数据访问层的代码(各种各样的DAO类),从数据库增删改查等操作,而这些代码都是重复的。但使用ORM框架则会大大减少重复性代码。对象关系映射(Object Relational Mapping,简称ORM),主要实现程序对象到关系数据库数据的映射。
3、ORM 的优缺点
优点:
- 提高开发效率,降低开发成本 (减少了DAO类的操作)
- 使开发更加对象化 (直接在实体类domain中来映射关系)
- 可移植
- 可以很方便地引入数据缓存之类的附加功能
缺点:
- 自动化进行关系数据库的映射需要消耗系统性能(消耗的性能可以忽略不记)
- 在处理多表联查、where条件复杂之类的查询时,ORM的语法会变得复杂(这才是最致命的缺点)
4、常见ORM思想的框架:JDBC、Hibernate、MyBatis、TopLink、JPA(JPA对ORM框架的再一次封装,这里是一套规范)
- JDBC:其实JDBC是最原生的API,支持连接并操作各种关系型数据库,也就是说可以用JDBC完成ORM思想的程序编写,如一些有ORM思想的框架底层都调用JDBC,所有这个JDBC我对其理解为ORM思想
- Hibernate:这个框架就不用多说了,完全使用ORM思想,不过现在直接使用Hibernate的少了,大多都是在JPA的封装上调用此框架
- MyBatis:这个框架可以手动写SQL语句,也可以完成对象关系映射,其实严格上说Mybatis不完全是一个ORM框架(JPA的供应商就不支持Mybatis)
5、总结
- JDBC:快,代码冗余、频繁的开关连接消耗性能、SQL不够灵活
- Mybatis:小巧、方便、高效、简单、直接、半自动
- Hibernate:强大、方便、高效、复杂、绕弯子、全自动
2、Hibernate JPA简介
1、认识 hibernate
Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将 POJO与数据库表建立映射关系,是一个全自动的 orm 框架,hibernate 可以自动生成 SQL 语句,自动执行,使得 Java 程序员可以随心所欲的使用对象编程思维来操纵数据库。
2、认识 JPA
JPA(Java Persistence API)即Java 持久化API,是SUN公司推出的一套基于ORM的规范,内部是由一系列的接口和抽象类构成。JPA 通过JDK 5.0注解描述 对象-关系表 的映射关系,并将运行期的实体对象持久化到数据库中。
3、JPA 的优势:
- 标准化:
JPA 是 JCP 组织发布的 Java EE 标准之一,因此任何声称符合 JPA 标准的框架都遵循同样的架构,提供相同的访问API,这保证了基于JPA开发的企业应用能够经过少量的修改就能够在不同的JPA框架下运行。 - 容器级特性的支持:
JPA框架中支持大数据集、事务、并发等容器级事务,这使得 JPA 超越了简单持久化框架的局限,在企业应用发挥更大的作用。 - 简单方便:
JPA的主要目标之一就是提供更加简单的编程模型:在JPA框架下创建实体和创建Java 类一样简单,没有任何的约束和限制,只需要使用 javax.persistence.Entity 进行注释,JPA的框架和接口也都非常简单,没有太多特别的规则和设计模式的要求,开发者可以很容易的掌握。JPA基于非侵入式原则设计,因此可以很容易的和其它框架或者容器集成 - 查询能力:
JPA的查询语言是面向对象而非面向数据库的,它以面向对象的自然语法构造查询语句,可以看成是Hibernate HQL的等价物。JPA定义了独特的JPQL(Java Persistence Query Language),JPQL是EJB QL的一种扩展,它是针对实体的一种查询语言,操作对象是实体,而不是关系数据库的表,而且能够支持批量更新和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL 才能够提供的高级查询特性,甚至还能够支持子查询。 - 高级特性:
JPA 中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,这样的支持能够让开发者最大限度的使用面向对象的模型设计企业应用,而不需要自行处理这些特性在关系数据库的持久化。
4、JPA 与 Hibernate 的关系
JPA 规范本质上就是一种ORM规范,注意不是ORM框架,因为JPA并未提供ORM实现,它只是定义了一些规范,提供了一些编程的API接口,但具体实现则由服务厂商来提供实现。JPA示意图:
Java代码
⬇
JPA规范(SUM公司定义的,Java持久化规范,内部由一系列接口和抽象类组成)
⬇
Hibernate/TopLink...其他ORM框架(它们都是实现了JPA规范)
⬇
JDBC规范(SUM公司制定)
⬇
MySQL/Oracle驱动
⬇
MySQL/Oracle数据库
JPA 和 Hibernate 的关系就像 JDBC 和 JDBC 驱动的关系,JPA是规范,Hibernate 除了作为ORM框架之外,它也是一种JPA实现。
PS:JPA 能取代 Hibernate 吗?正如同问 JDBC 规范可以驱动底层数据库吗?
答案是否定的,如果使用JPA规范进行数据库操作,底层需要 Hibernate 作为其实现类完成数据持久化工作。
3、搭建开发环境(IDEA)
直接使用IDEA创建JPA项目(使用Maven方式创建):https://blog.csdn.net/jellily12/article/details/89304345
1、使用 IDEA 创建 maven 工程(如果缺少src/test/resources目录,手动创建和设置即可)
├─JPA
│ JPA.iml
│ pom.xml
└─src
├─main
│ ├─java
│ └─resources
└─test
├─java
└─resources
2、导入pom依赖
<properties>
<!--锁定 jdk 版本为 1.8-->
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- hibernate对jpa的支持包 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.4.1.Final</version>
</dependency>
<!-- Mysql驱动 -->
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<!-- junit4单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
<scope>provided</scope>
</dependency>
</dependencies>
3、建立配置文件:/resources/META-INF/persistence.xml
-
路径:配置到类路径(resources)下的 META-INF 的文件夹下,文件名:persistence.xml
-
PS:由于主要在 src/test/ 下测试,所以建议在 src/test/resources/ 也建立 META-INF/persistence.xml
-
IDEA创建 persistence 模板:
setting=》Editor=》file and code Template=》JPA==》Deployment descriptors=》persistenceXX.xml
persistence.xml 文件内容:
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<!-- name:持久化单元名称,transaction-type:持久化单元事务类型(JTA:分布式事务管理,RESOURCE_LOCAL:本地事务管理) -->
<persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL">
<!--jpa的实现方式,配置JPA服务提供商 -->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<!-- 可配可不配,如果配置了顺序不能错,必须在provider之后-->
<!--<class>com.caochenlei.hibernate.jpa.Customer</class>-->
<!--可选配置:配置jpa实现方的配置信息-->
<properties>
<!-- 数据库信息配置:数据库驱动、数据库地址、数据库账户、数据库密码 -->
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
<property name="hibernate.connection.url" value="jdbc:mysql://127.0.0.1:3306/hibernate_jpa"/>
<property name="hibernate.connection.username" value="root"/>
<property name="hibernate.connection.password" value="password"/>
<!-- 配置JPA服务提供商可选参数 -->
<property name="hibernate.show_sql" value="true" /><!-- 自动显示sql -->
<property name="hibernate.format_sql" value="true"/><!-- 格式化sql -->
<!-- 自动创建数据库表:
none :不会创建表
create : 程序运行时创建数据库表(如果有表,先删除表再创建)
update :程序运行时创建表(如果有表,不会创建表)
create-drop : 每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除。
validate : 每次加载hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。
-->
<property name="hibernate.hbm2ddl.auto" value="update" />
</properties>
</persistence-unit>
</persistence>
4、编写实体类和数据库表的映射配置(目的是达到操作实体类,就相当于操作数据库表)
/**
* 客户的实体类
* 配置映射关系
* 1.实体类和表的映射关系
* @Entity:声明实体类
* @Table:配置实体类和表的映射关系
* name : 配置数据库表的名称
* 2.实体类中属性和表中字段的映射关系
*/
@Data
@Entity
@Table(name = "tb_customer")
public class Customer {
/**
* @Id:声明主键的配置
* @GeneratedValue:配置主键的生成策略
* strategy:
* GenerationType.IDENTITY :自增,mysql。底层数据库必须支持自动增长(底层数据库支持的自动增长方式,对id自增)
* GenerationType.SEQUENCE :序列,oracle(底层数据库必须支持序列)
* GenerationType.TABLE :jpa提供的一种机制,通过一张数据库表的形式帮助我们完成主键自增
* GenerationType.AUTO :由程序自动的帮助我们选择主键生成策略
* @Column:配置属性和字段的映射关系
* name:数据库表中字段的名称
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "customer_id")
private Long Id; // 客户的主键
@Column(name = "customer_name")
private String name; // 客户名称
@Column(name="customer_age")
private int age; // 客户年龄
@Column(name="customer_sex")
private boolean sex; // 客户性别
@Column(name="customer_phone")
private String phone; // 客户的联系方式
@Column(name="customer_address")
private String address; // 客户地址
}
5、对应的数据库表信息:
Field | Type | Comment |
---|---|---|
customer_id | bigint not null auto_increment | 客户编号(主键) |
customer_name | varchar(255) | 客户名称(公司名称) |
customer_age | integer | 客户年龄 |
customer_sex | bit | 客户性别 |
customer_phone | varchar(255) | 客户联系电话 |
customer_address | varchar(255) | 客户联系地址 |
6、测试保存操作的执行
/**
* 测试jpa的保存
* 案例:保存一个客户到数据库中
* Jpa的操作步骤
* 1.加载配置文件创建工厂(实体管理器工厂)对象
* 2.通过实体管理器工厂获取实体管理器
* 3.获取事务对象,开启事务
* 4.完成增删改查操作
* 5.提交事务(回滚事务)
* 6.释放资源
*/
@Test
public void testSave() {
// 1.加载配置文件创建工厂(实体管理器工厂)对象
EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa");
// 2.通过实体管理器工厂获取实体管理器
EntityManager em = factory.createEntityManager();
// 3.获取事务对象,开启事务
EntityTransaction tx = em.getTransaction();
tx.begin();
//4.完成增删改查操作:保存一个客户到数据库中
Customer customer = new Customer();
customer.setName("Sam");
customer.setAddress("Guangzhou");
// 保存操作
em.persist(customer);
// 5.提交事务
tx.commit();
// 6.释放资源
em.close();
factory.close();
}
7、查看日志
Hibernate:
create table tb_customer (
customer_id bigint not null auto_increment,
customer_address varchar(255),
customer_age integer,
customer_name varchar(255),
customer_phone varchar(255),
customer_sex bit,
primary key (customer_id)
) engine=InnoDB
Hibernate:
insert
into
tb_customer
(customer_address, customer_age, customer_name, customer_phone, customer_sex)
values
(?, ?, ?, ?, ?)
8、这里可以看到为我们自动生成了SQL语句,因为 xml 里这里设置的是 update,所以有表的前提下,不会再生成表
<!-- 自动创建数据库表:
none :不会创建表
create : 程序运行时创建数据库表(如果有表,先删除表再创建)
update :程序运行时创建表(如果有表,不会创建表)
create-drop : 每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除。
validate : 每次加载hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。
-->
<property name="hibernate.hbm2ddl.auto" value="update" />
9、错误处理(暂无)
4、JPA 完整配置文件
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<!--
持久化单元:
name:持久化单元名称
transaction-type:持久化单元事务类型
- JTA:分布式事务管理
- RESOURCE_LOCAL:本地事务管理
-->
<persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL">
<!--jpa的实现方式,配置JPA服务提供商 -->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<!-- 可配可不配,如果配置了顺序不能错,必须在provider之后-->
<!--<class>com.caochenlei.hibernate.jpa.Customer</class>-->
<!--
配置二级缓存时候使用的模式,可配置值有:
- ALL:所有的实体类都被缓存
- NONE:所有的实体类都不被缓存
- ENABLE_SELECTIVE:标识@Cacheable(true)注解的实体类将被缓存
- DISABLE_SELECTIVE;缓存除标识@Cacheable(false)以外的所有实体类
- UNSPECIFIED:默认值,JPA 产品默认值将被使用
-->
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
<!--可选配置:配置jpa实现方的配置信息-->
<properties>
<!-- 数据库信息配置:数据库驱动、数据库地址、数据库账户、数据库密码 -->
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
<property name="hibernate.connection.url" value="jdbc:mysql://127.0.0.1:3306/hibernate_jpa"/>
<property name="hibernate.connection.username" value="root"/>
<property name="hibernate.connection.password" value="password"/>
<!-- 数据库信息配置:数据库驱动、数据库地址、数据库账户、数据库密码 -->
<!--<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://127.0.0.1:3306/hibernate_jpa"/>
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="password"/>-->
<!-- 配置JPA服务提供商可选参数 -->
<!-- 自动显示sql -->
<property name="hibernate.show_sql" value="true" />
<!-- 格式化sql -->
<property name="hibernate.format_sql" value="true"/>
<!-- 自动创建数据库表:
none :不会创建表
create : 程序运行时创建数据库表(如果有表,先删除表再创建)
update :程序运行时创建表(如果有表,不会创建表)
create-drop : 每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除。
validate : 每次加载hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。
-->
<property name="hibernate.hbm2ddl.auto" value="update" />
<!-- 建表使用MyISAM,默认是InnoDB,下列是MySQL5 和 MySQL8 两种方言引擎设置-->
<!-- InnoDB引擎:org.hibernate.dialect.MySQL5InnoDBDialect、org.hibernate.dialect.MySQL8InnoDBDialect -->
<!-- MyISAM引擎:org.hibernate.dialect.MySQL5Dialect、org.hibernate.dialect.MySQL5Dialect -->
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
<!-- Scan for annotated classes and Hibernate mapping XML files (可不配置)-->
<!--<property name="hibernate.archive.autodetection" value="class, hbm"/>-->
<!-- 二级缓存相关 -->
<!-- 开启二级缓存 -->
<property name="hibernate.cache.use_second_level_cache" value="true"/>
<!-- 配置二级缓存处理类 -->
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory"/>
<!-- 开启查询缓存,entityManager.find查询可以不配置,如果使用JPQL或SQL查询需要开启该配置 -->
<property name="hibernate.cache.use_query_cache" value="true"/>
<!-- 指定缓存配置文件位置,如果默认在resources下可不配置 -->
<property name="hibernate.cache.provider_configuration" value="classpath:ehcache.xml"/>
<!-- *****************************连接池的配置***************************** -->
<!-- Hibernate JPA整合C3P0数据库连接池 -->
<property name="hibernate.connection.provider_class"
value="org.hibernate.connection.C3P0ConnectionProvider" />
<!-- 数据库连接池的最小连接数 -->
<property name="c3p0.min_size" value="5" />
<!-- 数据库连接池的最大连接数 -->
<property name="c3p0.max_size" value="30" />
<!-- 最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
<property name="c3p0.maxIdleTime" value="60" />
<!-- 获得连接的超时时间,如果超过这个时间,会抛出异常,单位毫秒 -->
<property name="c3p0.timeout" value="1800" />
<!-- 最大的PreparedStatement的数量 -->
<property name="c3p0.max_statements" value="50" />
<!-- 每隔120秒检查连接池里的空闲连接,单位是秒 -->
<property name="c3p0.idle_test_period" value="120" />
<!-- 当连接池里面的连接用完的时候,C3P0一下获取的新的连接数 -->
<property name="c3p0.acquire_increment" value="1" />
<!-- 是否每次都验证连接是否可用 -->
<property name="c3p0.validate" value="false" />
<!-- Druid连接池配置 -->
<property name="hibernate.connection.provider_class"
value="com.alibaba.druid.support.hibernate.DruidConnectionProvider" />
<property name="url"
value="jdbc:mysql://192.168.1.200:3306/SSH_Data?useSSL=false&allowPublicKeyRetrieval=true" />
<property name="username" value="root" />
<property name="password" value="123456" />
<property name="driverClassName"
value="com.mysql.cj.jdbc.Driver" />
<property name="filters" value="stat" />
<property name="maxActive" value="20" />
<property name="initialSize" value="1" />
<property name="maxWait" value="60000" />
<property name="minIdle" value="1" />
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<property name="minEvictableIdleTimeMillis" value="300000" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<property name="poolPreparedStatements" value="true" />
<property name="maxOpenPreparedStatements" value="20" />
<property name="asyncInit" value="true" />
</properties>
</persistence-unit>
</persistence>
5、JPA 核心API对象
JPA类层次结构的显示单元:
单元 | 描述 |
---|---|
Persistence | 这个类包含静态方法来获取EntityManagerFactory实例 |
EntityManagerFactory | 一个EntityManager的工厂类,创建并管理多个EntityManager实例 |
EntityManager | 一个接口,管理持久化操作的对象,工厂原理类似工厂的查询实例 |
EntityTransaction | 与EntityManager是一对一的关系,对于每一个EntityManager的操作由EntityTransaction类维护 |
Entity | 实体是持久性对象,是存储在数据库中的记录(实际上就是Java Bean) |
Query | 该接口由每个JPA供应商实现,能够获得符合标准的关系对象 |
JPA四个核心API对象:Persistence、EntityManagerFactory、EntityManager、EntityTransaction
Hibernate JPA 的操作步骤:
- 加载配置文件创建实体管理器工厂
- 根据实体管理器工厂,创建实体管理器
- 创建事务对象,开启事务
- 完成增删改查操作
- 提交事务(回滚事务)
- 释放资源
1、Persistence对象(创建实体管理器工厂)
/**
* Persisitence.createEntityMnagerFactory(持久化单元名称)
* 静态方法(根据持久化单元名称创建实体管理器工厂)
**/
// 1.加载配置文件创建工厂(实体管理器工厂)对象
EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa");
Persistence对象主要作用是用于获取EntityManagerFactory对象的 。通过调用该类的createEntityManagerFactory()
静态方法,根据配置文件中持久化单元名称创建EntityManagerFactory。
2、EntityManagerFactory(EntityManagerFactory 接口主要用来创建 EntityManager 实例)
/**
* entityManagerFactory.createEntityManager():获取EntityManager对象
* 内部维护的很多的内容:
* 1.内部维护了数据库信息
* 2.维护了缓存信息
* 3.维护了所有的实体管理器对象
* 4.在创建EntityManagerFactory的过程中会根据配置创建数据库表
* EntityManagerFactory的创建过程比较浪费资源
* 特点:线程安全的对象,多个线程访问同一个EntityManagerFactory不会有线程安全问题
* 如何解决EntityManagerFactory的创建过程浪费资源(耗时)的问题?
* 思路:创建一个公共的EntityManagerFactory的对象
* 静态代码块的形式创建EntityManagerFactory
**/
// 2.通过实体管理器工厂获取实体管理器
EntityManager em = factory.createEntityManager();
3、EntityManager:实体类管理器
/**
* EntityManager:实体类管理器
* 获取事务对象: getTransaction()
* 保存数据: presist()
* 更新数据: merge()
* 删除数据: remove()
* 根据id查询: find()/getRefrence()
* 清空缓存: clear()
* 强制立即写入数据库: flush()
* 重新加载缓存的对象: refresh()
* ......(还有挺多方法可以自己慢慢琢磨)
**/
// 3.获取事务对象,然后可以开启事务、提交事务、回滚事务
EntityTransaction tx = em.getTransaction();
在 JPA 规范中, EntityManager
是完成持久化操作的核心对象。实体类作为普通 java对象,只有在调用 EntityManager
将其持久化后才会变成持久化对象。EntityManager
对象在一组实体类与底层数据源之间进行 O/R 映射的管理。它可以用来管理和更新 Entity Bean, 根椐主键查找 Entity Bean
, 还可以通过JPQL语句查询实体。我们可以通过调用EntityManager
的方法完成获取事务,以及持久化数据库的操作
4、EntityTransaction:事务对象
/**
* EntityTransaction: 事务对象
* 开启事务: begin()
* 提交事务: commit()
* 回滚事务: rollback()
**/
// 开启事务
tx.begin();
在 JPA 规范中, EntityTransaction
是完成事务操作的核心对象,对于EntityTransaction
在我们的 java 代码中承接的功能比较简单
6、JPA 实现CRUD操作
1、保存操作:persist
/**
* 运行之前,修改hibernate.hbm2ddl.auto=create
* 保存操作
*/
@Test
public void testSave() {
// 获取实体管理器工厂
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("myJpa");
// 获取实体管理器
EntityManager entityManager = entityManagerFactory.createEntityManager();
// 获取事务
EntityTransaction transaction = entityManager.getTransaction();
// 开启事务
transaction.begin();
// 创建实体对象并保存
Customer customer1 = new Customer();
customer1.setName("张三");
customer1.setAge(20);
customer1.setSex(true);
customer1.setPhone("13018882888");
customer1.setAddress("北京");
entityManager.persist(customer1);
Customer customer2 = new Customer();
customer2.setName("李四");
customer2.setAge(18);
customer2.setSex(false);
customer2.setPhone("13533333555");
customer2.setAddress("广州");
entityManager.persist(customer2);
Customer customer3 = new Customer();
customer3.setName("王五");
customer3.setAge(28);
customer3.setSex(true);
customer3.setPhone("13012345678");
customer3.setAddress("深圳");
entityManager.persist(customer3);
// 提交事务
transaction.commit();
// 释放资源
entityManager.close();
entityManagerFactory.close();
}
查看日志:
Hibernate:
insert
into
tb_customer
(customer_address, customer_age, customer_name, customer_phone, customer_sex)
values
(?, ?, ?, ?, ?)
Hibernate:
insert
into
tb_customer
(customer_address, customer_age, customer_name, customer_phone, customer_sex)
values
(?, ?, ?, ?, ?)
Hibernate:
insert
into
tb_customer
(customer_address, customer_age, customer_name, customer_phone, customer_sex)
values
(?, ?, ?, ?, ?)
2、查询操作:find/getReference
根据 ID 查询操作
1、find
方法查询(立即查询)
/**
* 实际发送的SQL语句就是:select * from customer where id = 2
* 运行之前,修改hibernate.hbm2ddl.auto=update
* 立即查询操作
*/
@Test
public void testQuery1() {
// 获取实体管理器工厂
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("myJpa");
// 获取实体管理器
EntityManager entityManager = entityManagerFactory.createEntityManager();
// 获取事务
EntityTransaction transaction = entityManager.getTransaction();
// 开启事务
transaction.begin();
// 查询实体并输出
Customer customer = entityManager.find(Customer.class, 2L);
System.out.println(customer);
// 提交事务
transaction.commit();
// 释放资源
entityManager.close();
entityManagerFactory.close();
}
Hibernate:
select
customer0_.customer_id as customer1_0_0_,
customer0_.customer_address as customer2_0_0_,
customer0_.customer_age as customer3_0_0_,
customer0_.customer_name as customer4_0_0_,
customer0_.customer_phone as customer5_0_0_,
customer0_.customer_sex as customer6_0_0_
from
tb_customer customer0_
where
customer0_.customer_id=?
Customer(Id=2, name=李四, age=18, sex=false, phone=13533333555, address=广州)
2、getReference
方法实现(懒加载查询)(推荐)
/**
* 实际发送的SQL语句就是:select * from customer where id = 2
* 运行之前,修改hibernate.hbm2ddl.auto=update
* 延迟查询操作(查询结果对象的时候,才会发送查询的sql语句)
*/
@Test
public void testQuery2() {
// 获取实体管理器工厂
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("myJpa");
// 获取实体管理器
EntityManager entityManager = entityManagerFactory.createEntityManager();
// 获取事务
EntityTransaction transaction = entityManager.getTransaction();
// 开启事务
transaction.begin();
// 查询实体并输出
Customer customer = entityManager.getReference(Customer.class, 2L);
System.out.println(customer);
// 提交事务
transaction.commit();
// 释放资源
entityManager.close();
entityManagerFactory.close();
}
日志发现与 find 查询没有区别:
Hibernate:
select
customer0_.customer_id as customer1_0_0_,
customer0_.customer_address as customer2_0_0_,
customer0_.customer_age as customer3_0_0_,
customer0_.customer_name as customer4_0_0_,
customer0_.customer_phone as customer5_0_0_,
customer0_.customer_sex as customer6_0_0_
from
tb_customer customer0_
where
customer0_.customer_id=?
Customer(Id=2, name=李四, age=18, sex=false, phone=13533333555, address=广州)
可以注释打印对象在对比试试://System.out.println(customer)
;(可以发现,如果没有使用对象的话,是没有调用查询SQL语句的)
INFO: HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
3、find
与getReference
查询对比:
find
实现的查询时,是会在调用 find 方法时,立即发送SQL语句查询数据库的操作getReference
是一种延迟加载策略的操作,调用getReference方法不会立即发送sql语句查询数据库,当调用查询结果对象的时候,才会发送查询的sql语句(实际就是动态代理)
3、更新操作:merge
/**
* 运行之前,修改hibernate.hbm2ddl.auto=update
* 更新操作
*/
@Test
public void testUpdate() {
// 获取实体管理器工厂
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("myJpa");
// 获取实体管理器
EntityManager entityManager = entityManagerFactory.createEntityManager();
// 获取事务
EntityTransaction transaction = entityManager.getTransaction();
// 开启事务
transaction.begin();
// 查询实体并更新
Customer customer = entityManager.find(Customer.class, 2L);
customer.setAddress("上海");
entityManager.merge(customer);
// 提交事务
transaction.commit();
// 释放资源
entityManager.close();
entityManagerFactory.close();
}
查看日志:
Hibernate:
select
customer0_.customer_id as customer1_0_0_,
customer0_.customer_address as customer2_0_0_,
customer0_.customer_age as customer3_0_0_,
customer0_.customer_name as customer4_0_0_,
customer0_.customer_phone as customer5_0_0_,
customer0_.customer_sex as customer6_0_0_
from
tb_customer customer0_
where
customer0_.customer_id=?
Hibernate:
update
tb_customer
set
customer_address=?,
customer_age=?,
customer_name=?,
customer_phone=?,
customer_sex=?
where
customer_id=?
4、删除操作:remove
/**
* 运行之前,修改hibernate.hbm2ddl.auto=update
* 删除操作
*/
@Test
public void testDelete() {
// 获取实体管理器工厂
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("myJpa");
// 获取实体管理器
EntityManager entityManager = entityManagerFactory.createEntityManager();
// 获取事务
EntityTransaction transaction = entityManager.getTransaction();
// 开启事务
transaction.begin();
// 查询实体并删除
Customer customer = entityManager.find(Customer.class, 1L);
entityManager.remove(customer);
// 提交事务
transaction.commit();
// 释放资源
entityManager.close();
entityManagerFactory.close();
}
查看日志:
Hibernate:
select
customer0_.customer_id as customer1_0_0_,
customer0_.customer_address as customer2_0_0_,
customer0_.customer_age as customer3_0_0_,
customer0_.customer_name as customer4_0_0_,
customer0_.customer_phone as customer5_0_0_,
customer0_.customer_sex as customer6_0_0_
from
tb_customer customer0_
where
customer0_.customer_id=?
Hibernate:
delete
from
tb_customer
where
customer_id=?
7、JPA 连接工厂工具类
为什么要抽取 JpaUtil 工具类?
由于EntityManagerFactory
是一个线程安全的对象(即多个线程访问同一个EntityManagerFactory
对象不会有线程安全问题),并且EntityManagerFactory
的创建极其浪费资源,所以在使用JPA编程时,我们可以对EntityManagerFactory
的创建进行优化,只需要做到一个工程只存在一个EntityManagerFactory
即可。
解决思路是通过静态代码的形式创建 EntityManagerFactory
JpaUtil.java
工具类
/**
* 解决实体管理器工厂的浪费资源和耗时问题
* 通过静态代码块的形式,当程序第一次访问此工具类时,创建一个公共的实体管理器工厂对象
*
* 第一次访问getEntityManager方法:经过静态代码块创建一个factory对象,再调用方法创建一个EntityManager对象
* 第二次方法getEntityManager方法:直接通过一个已经创建好的factory对象,创建EntityManager对象
*/
public class JpaUtils {
private static EntityManagerFactory factory;
static {
//1.加载配置文件,创建entityManagerFactory
factory = Persistence.createEntityManagerFactory("myJpa");
}
/**
* 获取EntityManager对象
*/
public static EntityManager getEntityManager() {
return factory.createEntityManager();
}
}
测试工具类
public class JpaTest {
@Test
public void testSave() {
// 1.通过工具类获取实体类管理器
EntityManager em = JpaUtils.getEntityManager();
// 2.获取事务对象
EntityTransaction tx = em.getTransaction();
// 开启事务
tx.begin();
//3.完成增删改查操作:保存一个客户到数据库中
Customer customer = new Customer();
customer.setName("Yolo");
customer.setAddress("BeiJing");
//保存操作
em.persist(customer);
//4.提交事务
tx.commit();
//5.释放资源
em.close();
//因为工厂是公共的,不能关闭,不然其他线程将无法获取
//factory.close();
}
}