一. JPA与hibernate的关系
JPA规范本质上就是一种ORM规范,注意不是ORM框架——因为JPA并未提供ORM实现,它只是制订了一些规范,提供了一些编程的API接口,但具体实现则由服务厂商来提供实现。
JPA和Hibernate的关系就像JDBC和数据库JDBC驱动的关系,JPA是规范,Hibernate除了作为ORM框架之外,它也是一种JPA实现。JPA怎么取代Hibernate呢?JDBC规范可以驱动底层数据库吗?答案是否定的,也就是说,如果使用JPA规范进行数据库操作,底层需要hibernate作为其实现类完成数据持久化工作。
二 . JPA-hibernate入门案例
1. 需求介绍:实现的功能是保存一个客户到数据库的客户表中。
2. 导入maven依赖:
<properties>
<!--hibernate版本-->
<project.hibernate.version>5.0.7.Final</project.hibernate.version>
</properties>
<dependencies>
<!-- hibernate对jpa的支持包 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${project.hibernate.version}</version>
</dependency>
<!-- c3p0 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-c3p0</artifactId>
<version>${project.hibernate.version}</version>
</dependency>
<!--mysql-JDBC驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--日志-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
</dependencies>
3. 创建客户的实体类、编写实体类和数据库表的映射配置:
/**
* 所有的注解都是使用JPA的规范提供的注解,
* 所以在导入注解包的时候,一定要导入javax.persistence下的注解包
*/
@Entity //声明实体类
@Data //lombok注解
@Table(name = "cst_customer") //建立实体类和表的映射关系
public class Customer implements Serializable {
@Id //声明当前私有属性为主键
@GeneratedValue(strategy = GenerationType.IDENTITY) //配置主键的生成策略
@Column(name = "cust_id",columnDefinition = "bigint(32) NOT NULL COMMENT '客户编号(主键)'")
private Long custId;
@Column(name = "cust_name",columnDefinition = "varchar(32) NOT NULL COMMENT '客户名称(公司名称)'")
private String custName;
@Column(name = "cust_source",columnDefinition = "varchar(32) DEFAULT NULL COMMENT '客户信息来源'")
private String custSource;
@Column(name="cust_industry",columnDefinition = "varchar(32) DEFAULT NULL COMMENT '客户所属行业'")
private String custIndustry;
@Column(name = "cust_level",columnDefinition = "varchar(32) DEFAULT NULL COMMENT '客户级别'")
private String custLevel;
@Column(name = "cust_address",columnDefinition = "varchar(32) DEFAULT NULL COMMENT '客户联系地址'")
private String custAddress;
@Column(name="cust_phone",columnDefinition = "varchar(32) DEFAULT NULL COMMENT '客户联系电话'")
private String custPhone;
}
JPA常用注解的说明:
@Entity
作用:指定当前类是实体类。
@Table
作用:指定实体类和表之间的对应关系。
属性:name:指定数据库表的名称
@Id
作用:指定当前字段是主键。
@GeneratedValue
作用:指定主键的生成方式。
属性:strategy :指定主键生成策略。
JPA提供四种主键生成策略:
①GenerationType.IDENTITY:主键由数据库自动生成,条件是数据库支持自增(mysql)。
②GenerationType.SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列(oracle)。
③GenerationType.AUTO:主键由程序控制(适合两种及以上数据库同时使用)
④GenerationType.TABLE:使用一个特定的数据库表格来保存主键
@Column
作用:指定实体类属性和数据库表之间的对应关系
属性:
name:指定数据库表的列名称。
unique:是否唯一
nullable:是否可以为空
inserttable:是否可以插入
updateable:是否可以更新
columnDefinition: 定义建表时创建此列的DDL(注意:使用这个属性必须设置数据类型)
secondaryTable: 从表名。如果此列不建在主表上(默认建在主表),该属性定义该列所在从表的名字搭建开发环境[重点]
4. 配置JPA核心配置文件-persistence.xml
在java工程的src路径下创建一个名为META-INF的文件夹,在此文件夹下创建一个名为persistence.xml的配置文件。
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<!--配置持久化单元
name:持久化单元名称
transaction-type:事务类型
RESOURCE_LOCAL:本地事务管理,只能针对一种数据库,不支持分布式的数据库
JTA:分布式事务管理,可以支持分布式的数据库
-->
<persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL">
<!--配置JPA规范的服务提供商 -->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<properties>
<!-- 配置数据源 -->
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/jpa"/>
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="admin123"/>
<!--jpa提供者的可选配置:我们的JPA规范的提供者为hibernate,所以jpa的核心配置中兼容hibernate的配置 -->
<!--显示sql语句-->
<property name="hibernate.show_sql" value="true"/>
<!--格式化sql语句-->
<property name="hibernate.format_sql" value="true" />
<!--自动创建|更新|验证数据库表结构
create:每次加载hibernate时都会删除上一次的生成的表,然后再重新来生成新表
update:运行时创建表(若原始有表存在,不会删除表,直接进行数据更新)
-->
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
</persistence>
5. 实现保存操作
@Test
public void testSave() {
EntityManagerFactory entityManagerFactory = null;
EntityManager entityManager = null;
EntityTransaction transaction = null;
try {
//获得实体管理器工厂
entityManagerFactory = Persistence.createEntityManagerFactory("myJpa");
//获得实体管理器
entityManager = entityManagerFactory.createEntityManager();
//获得实体事务
transaction = entityManager.getTransaction();
transaction.begin();
Customer customer = new Customer();
customer.setCustName("阿里巴巴");
customer.setCustIndustry("互联网");
entityManager.persist(customer);
transaction.commit();
}catch (Exception e) {
transaction.rollback();
e.printStackTrace();
}finally {
entityManager.close();
entityManagerFactory.close();
}
}
三.JPA的API介绍
1. Persistence对象
Persistence对象主要作用是用于获取EntityManagerFactory对象的 。通过调用该类的createEntityManagerFactory静态方法,根据配置文件中持久化单元名称创建EntityManagerFactory。
entityManagerFactory = Persistence.createEntityManagerFactory("myJpa");
2. EntityManagerFactory对象
EntityManagerFactory 接口主要用来创建 EntityManager 实例
entityManager = entityManagerFactory.createEntityManager();
由于EntityManagerFactory 是一个线程安全的对象(即多个线程访问同一个EntityManagerFactory 对象不会有线程安全问题),并且EntityManagerFactory 的创建极其浪费资源,所以在使用JPA编程时,我们可以对EntityManagerFactory 的创建进行优化,只需要做到一个工程只存在一个EntityManagerFactory 即可。
3. EntityManager对象
在 JPA 规范中, EntityManager是完成持久化操作的核心对象。实体类作为普通 java对象,只有在调用 EntityManager将其持久化后才会变成持久化对象。EntityManager对象在一组实体类与底层数据源之间进行 O/R 映射的管理。它可以用来管理和更新 Entity Bean, 根椐主键查找 Entity Bean, 还可以通过JPQL语句查询实体。
我们可以通过调用EntityManager的方法完成获取事务,以及持久化数据库的操作。
方法说明:
getTransaction : 获取事务对象
persist : 保存操作
merge : 更新操作
remove : 删除操作
find/getReference : 根据id查询(立即加载/延迟加载)
4. EntityTransaction对象
在 JPA 规范中, EntityTransaction是完成事务操作的核心对象,对于EntityTransaction在我们的java代码中承接的功能比较简单。
方法说明:
begin:开启事务
commit:提交事务
rollback:回滚事务
四.JPAUtils工具类
public class JPAUtils {
private static EntityManagerFactory entityManagerFactory;
static {
entityManagerFactory = Persistence.createEntityManagerFactory("myJpa");
}
public static EntityManager getEntityManager() {
EntityManager entityManager = entityManagerFactory.createEntityManager();
return entityManager;
}
}
五.使用JPA完成增删改查操作
1. 保存
@Test
public void testAdd() {
Customer customer = new Customer();
customer.setCustName("阿里巴巴");
customer.setCustIndustry("互联网");
customer.setCustLevel("VIP客户");
customer.setCustSource("网络");
customer.setCustAddress("杭州");
customer.setCustPhone("010-84389340");
EntityManager entityManager = null;
EntityTransaction transaction = null;
try {
entityManager = JPAUtils.getEntityManager();
transaction = entityManager.getTransaction();
transaction.begin();
entityManager.persist(customer);
transaction.commit();
}catch (Exception e) {
transaction.rollback();
e.printStackTrace();
}finally {
entityManager.close();
}
}
2. 修改
@Test
public void testMerge() {
EntityManager entityManager = null;
EntityTransaction transaction = null;
try {
entityManager = JPAUtils.getEntityManager();
transaction = entityManager.getTransaction();
transaction.begin();
Customer customer = entityManager.find(Customer.class, 1L);
customer.setCustPhone("888888");
entityManager.merge(customer);
transaction.commit();
}catch (Exception e) {
transaction.rollback();
e.printStackTrace();
}finally {
entityManager.close();
}
}
3. 删除
@Test
public void testRemove() {
EntityManager entityManager = null;
EntityTransaction transaction = null;
try {
entityManager = JPAUtils.getEntityManager();
transaction = entityManager.getTransaction();
transaction.begin();
Customer customer = entityManager.find(Customer.class, 1L);
entityManager.remove(customer);
transaction.commit();
}
catch (Exception e) {
e.printStackTrace();
transaction.rollback();
}finally {
entityManager.close();
}
}
4. 根据id查询
@Test
public void testFind() {
EntityManager entityManager = null;
EntityTransaction transaction = null;
try {
entityManager = JPAUtils.getEntityManager();
transaction = entityManager.getTransaction();
transaction.begin();
Customer customer1 = entityManager.find(Customer.class, 1L);
Customer customer2 = entityManager.find(Customer.class, 1L);
//证实了entityManager有缓存
System.out.println(customer1==customer2);
transaction.commit();
}catch (Exception e) {
transaction.rollback();
e.printStackTrace();
}finally {
entityManager.close();
}
六.JPA中的复杂查询
JPQL全称Java Persistence Query Language
基于首次在EJB2.0中引入的EJB查询语言(EJB QL),Java持久化查询语言(JPQL)是一种可移植的查询语言,旨在以面向对象表达式语言的表达式,将SQL语法和简单查询语义绑定在一起·使用这种语言编写的查询是可移植的,可以被编译成所有主流数据库服务器上的SQL。
其特征与原生SQL语句类似,并且完全面向对象,通过类名和属性访问,而不是表名和表的属性。
1. 查询全部
@Test
public void findAll() {
EntityManager entityManager = null;
EntityTransaction transaction = null;
try {
entityManager = JPAUtils.getEntityManager();
transaction = entityManager.getTransaction();
transaction.begin();
//创建query对象
// Query query = entityManager.createQuery("select c from Customer c");
Query query = entityManager.createQuery("from Customer");
//得到集合返回类型
List resultList = query.getResultList();
for (Object o : resultList) {
System.out.println(o);
}
transaction.commit();
}catch (Exception e) {
transaction.rollback();
e.printStackTrace();
}finally {
entityManager.close();
}
}
2. 分页查询
@Test
public void findPage() {
EntityManager entityManager = null;
EntityTransaction transaction = null;
try {
entityManager = JPAUtils.getEntityManager();
transaction = entityManager.getTransaction();
transaction.begin();
Query query = entityManager.createQuery("from Customer");
//当前页
query.setFirstResult(0);
//每页显示条数
query.setMaxResults(2);
List resultList = query.getResultList();
for (Object o : resultList) {
System.out.println(o);
}
transaction.commit();
}catch (Exception e) {
transaction.rollback();
e.printStackTrace();
}finally {
entityManager.close();
}
}
3. 条件查询
@Test
public void testCondition() {
EntityManager entityManager = null;
EntityTransaction transaction = null;
try {
entityManager = JPAUtils.getEntityManager();
transaction = entityManager.getTransaction();
transaction.begin();
Query query = entityManager.createQuery("from Customer where custName = ?");
query.setParameter(1, "阿里巴巴");
//得到唯一的结果集对象
Object object = query.getSingleResult();
System.out.println(object);
transaction.commit();
}catch (Exception e) {
transaction.rollback();
e.printStackTrace();
}finally {
entityManager.close();
}
}
4. 排序查询
@Test
public void testOrder() {
EntityManager entityManager = null;
EntityTransaction transaction = null;
try {
entityManager = JPAUtils.getEntityManager();
transaction = entityManager.getTransaction();
transaction.begin();
Query query = entityManager.createQuery("from Customer order by custId desc");
List resultList = query.getResultList();
for (Object o : resultList) {
System.out.println(o);
}
transaction.commit();
}catch (Exception e) {
transaction.rollback();
e.printStackTrace();
}finally {
entityManager.close();
}
}
5. 统计查询
@Test
public void findCount() {
EntityManager entityManager = null;
EntityTransaction transaction = null;
try {
entityManager = JPAUtils.getEntityManager();
transaction = entityManager.getTransaction();
transaction.begin();
Query query = entityManager.createQuery("select count(custId) from Customer");
Object object = query.getSingleResult();
System.out.println(object);
transaction.commit();
}catch (Exception e) {
transaction.rollback();
e.printStackTrace();
}finally {
entityManager.close();
}
}