Spring Data ~ JPA。
文章目录
ORM、Hibernate、JPA 概述 + jpa 基本操作。
orm 思想。
对象关系映射(Object Relational Mapping,简称 ORM)是通过使用描述对象和数据库之间映射的元数据,将面向对象语言程序中的对象自动持久化到关系数据库中。本质上就是将数据从一种形式转换到另外一种形式。 这也同时暗示着额外的执行开销;然而,如果ORM作为一种中间件实现,则会有很多机会做优化,而这些在手写的持久层并不存在。 更重要的是用于控制转换的元数据需要提供和管理;但是同样,这些花费要比维护手写的方案要少;而且就算是遵守ODMG规范的对象数据库依然需要类级别的元数据。
操作对象就相当于操作数据库。
自动建立两个映射。
实体类和表的映射关系。
实体类中属性和表中字段的映射关系。
MyBatis 、Hibernate。
jdbc。
// 操作繁琐。
// 占位符赋值麻烦。
CREATE TABLE `new_schema`.`t_user` (
`id` INT NOT NULL AUTO_INCREMENT,
`username` VARCHAR(45) NULL,
`address` VARCHAR(45) NULL,
PRIMARY KEY (`id`));
package com.geek.jdbc;
import com.geek.domain.User;
import com.mysql.cj.jdbc.Driver;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class JdbcDemo {
// 操作繁琐。
// 占位符赋值麻烦。
public static void main(String[] args) throws SQLException, ClassNotFoundException {
String sql = "insert into t_user (username, address) values (?, ?)";
// DriverManager.registerDriver(new Driver());
Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://192.168.142.135/new_schema?characterEncoding=UTF-8", "root", "root");
PreparedStatement preparedStatement = connection.prepareStatement(sql);
User user = new User();
user.setUsername("geek");
user.setAddress("武汉。");
preparedStatement.setString(1, user.getUsername());
preparedStatement.setString(2, user.getAddress());
int i = preparedStatement.executeUpdate();
System.out.println("i = " + i);
preparedStatement.close();
connection.close();
}
}
Hibernate 框架。
Hibernate 是一个开放源代码的对象关系映射框架,它对 JDBC 进行了非常轻量级的对象封装,它将 POJO 与数据库表建立映射关系,是一个全自动的 orm 框架,hibernate 可以自动生成 SQL 语句,自动执行,使得 Java 程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate 可以应用在任何使用 JDBC 的场合,既可以在 Java 的客户端程序使用,也可以在 Servlet/JSP 的 Web 应用中使用,最具革命意义的是,Hibernate 可以在应用 EJB 的 JaveEE 架构中取代 CMP,完成数据持久化的重任。
~ 百度百科。
↓↓↓ 更好。
JPA 规范。
Sun 公司定义的规范(接口和抽象类)。生产厂商实现。
JPA 基本操作。
增删改查。
搭建环境。
- database。
CREATE TABLE `new_schema`.`cst_customer` (
`cust_id` BIGINT(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)。',
`cust_name` VARCHAR(32) CHARACTER SET 'utf8' COLLATE 'utf8_general_ci' NOT NULL COMMENT '客户名称(公司名称)。',
`cust_source` VARCHAR(45) NULL COMMENT '客户信息来源。',
`cust_industry` VARCHAR(45) NULL COMMENT '客户所属行业。',
`cust_lovel` VARCHAR(45) NULL COMMENT '客户级别。',
`cust_address` VARCHAR(128) NULL COMMENT '客户地址。',
`cust_phone` VARCHAR(64) NULL COMMENT '客户联系电话。',
PRIMARY KEY (`cust_id`))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;
- Maven。
<dependencies>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-entitymanager -->
<!-- Hibernate 对 JPA 的支持包。-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.4.11.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-c3p0 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-c3p0</artifactId>
<version>5.4.11.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
</dependencies>
- 配置文件。
配置到类路径下
META-INF
的文件夹下。
命名:persistence.xml
引入约束。
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
</persistence>
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<!-- persistence-unit 节点。
持久化单元。
name——持久化单元名称。
transaction-type——事务管理方式。
JTA——分布式事务管理。
RESOURCE_LOCAL——本地事务管理。
-->
<persistence-unit name="myJPA" transaction-type="RESOURCE_LOCAL">
<!-- jpa 的实现方式。-->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<!-- 可选配置:配置 jpa 实现方的配置信息。-->
<properties>
<!-- 数据库信息。-->
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="root"/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.url"
value="jdbc:mysql://192.168.142.135/new_schema?characterEncoding=UTF-8"/>
<!-- 配置 jpa 实现方(Hibernate)的配置信息。
显示 sql。~ hibernate.show_sql。
自动创建数据库表。~ hibernate.hbm2ddl.auto。
create 程序运行时创建数据表。(如果有表,先删除表再创建)。
update 程序运行时创建表。(如果有表,不会创建表)。
none 不会创建表。
-->
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="create"/>
</properties>
</persistence-unit>
</persistence>
实体类。
package com.geek.domain;
import javax.persistence.*;
/**
* 客户实体类。
*/
@Entity// 声明实体类。
@Table(name = "cst_customer")
public class Customer {
/*
* 配置映射关系。
* 实体类和表的映射关系。
* @Entity// 声明实体类。
* @Table(name = "cst_customer")
* 配置实体类和表的映射关系。
* name:配置数据库表的名称。
* 实体类中属性和表中字段的映射关系。
* @Id// 主键。
* @GeneratedValue。配置主键的生成策略。
* strategy = GenerationType.IDENTITY。自增。
* @Column。配置属性和字段的映射关系。
* (name = "cust_id")。数据库中表字段的名称。
*/
@Id// 主键。
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "cust_id")
private Long custId;
@Column(name = "cust_name")
private String custName;
@Column(name = "cust_source")
private String custSource;
@Column(name = "cust_level")
private String custLevel;
@Column(name = "cust_industry")
private String custIndustry;
@Column(name = "cust_phone")
private String custPhone;
@Column(name = "cust_address")
private String custAddress;
public Long getCustId() {
return custId;
}
public void setCustId(Long custId) {
this.custId = custId;
}
public String getCustName() {
return custName;
}
public void setCustName(String custName) {
this.custName = custName;
}
public String getCustSource() {
return custSource;
}
public void setCustSource(String custSource) {
this.custSource = custSource;
}
public String getCustLevel() {
return custLevel;
}
public void setCustLevel(String custLevel) {
this.custLevel = custLevel;
}
public String getCustIndustry() {
return custIndustry;
}
public void setCustIndustry(String custIndustry) {
this.custIndustry = custIndustry;
}
public String getCustPhone() {
return custPhone;
}
public void setCustPhone(String custPhone) {
this.custPhone = custPhone;
}
public String getCustAddress() {
return custAddress;
}
public void setCustAddress(String custAddress) {
this.custAddress = custAddress;
}
@Override
public String toString() {
return "Customer{" +
"custId=" + custId +
", custName='" + custName + '\'' +
", custSource='" + custSource + '\'' +
", custLevel='" + custLevel + '\'' +
", custIndustry='" + custIndustry + '\'' +
", custPhone='" + custPhone + '\'' +
", custAddress='" + custAddress + '\'' +
'}';
}
}
CRUD。
package com.geek.test;
import com.geek.domain.Customer;
import org.junit.Test;
import javax.persistence.*;
public class JpaTest {
/**
* 测试 jpa 的保存。
* <p>
* 加载配置文件创建工厂(实体管理器工厂)对象。
* 通过实体管理器工厂获取实体管理器。
* 获取事务对象,开启事务。
* 完成增删改查操作。
* 提交事务。
* 释放资源。
*/
@Test
public void testSave() {
// 加载配置文件创建工厂(实体管理器工厂)对象。
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("myJPA");
// 通过实体管理器工厂获取实体管理器。
EntityManager entityManager = entityManagerFactory.createEntityManager();
// 获取事务对象,开启事务。
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
// 完成增删改查操作。
Customer customer = new Customer();
customer.setCustName("李");
customer.setCustAddress("武汉");
// 保存。
entityManager.persist(customer);
// 提交事务。
transaction.commit();
// 释放资源。
entityManager.close();
entityManagerFactory.close();
}
}
日志文件输出 sql 语句。
log4j:WARN No appenders could be found for logger (org.jboss.logging).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
Hibernate: drop table if exists cst_customer
Hibernate: create table cst_customer (cust_id bigint not null auto_increment, cust_address varchar(255), cust_industry varchar(255), cust_level varchar(255), cust_name varchar(255), cust_phone varchar(255), cust_source varchar(255), primary key (cust_id)) engine=InnoDB
Hibernate: insert into cst_customer (cust_address, cust_industry, cust_level, cust_name, cust_phone, cust_source) values (?, ?, ?, ?, ?, ?)
此处有 drop table if exists cst_cunstomer
是因为
<!-- 配置 jpa 实现方(Hibernate)的配置信息。
显示 sql。~ hibernate.show_sql。
自动创建数据库表。~ hibernate.hbm2ddl.auto。
create 程序运行时创建数据表。(如果有表,先删除表再创建)。
update 程序运行时创建表。(如果有表,不会创建表)。
none 不会创建表。
-->
主键生成策略。
* 实体类中属性和表中字段的映射关系。
* @Id// 主键。
* @GeneratedValue。配置主键的生成策略。
* strategy = GenerationType.IDENTITY。自增。
* 底层数据库必须支持自动增长。(MySQL~AutoIncrement)。
* strategy = GenerationType.SEQUENCE。序列。
* 底层数据库必须支持序列。(Oracle)。
* strategy = GenerationType.TABLE。
* jpa 提供的一种机制,通过一张数据库表的形式帮助我们完成主键自增。
* strategy = GenerationType.AUTO。
* 由程序自动的帮助我们选择主键生成策略。
- 如果使用
GenerrationType.Sequence
,jpa 会创建 2 张表。hibernate_sequences
以记录下一个 id 的值。
Hibernate: drop table if exists cst_customer
Hibernate: drop table if exists hibernate_sequences
Hibernate: create table cst_customer (cust_id bigint not null, cust_address varchar(255), cust_industry varchar(255), cust_level varchar(255), cust_name varchar(255), cust_phone varchar(255), cust_source varchar(255), primary key (cust_id)) engine=InnoDB
Hibernate: create table hibernate_sequences (sequence_name varchar(255) not null, next_val bigint, primary key (sequence_name)) engine=InnoDB
Hibernate: insert into hibernate_sequences(sequence_name, next_val) values ('default',0)
Hibernate: select tbl.next_val from hibernate_sequences tbl where tbl.sequence_name=? for update
Hibernate: update hibernate_sequences set next_val=? where next_val=? and sequence_name=?
Hibernate: insert into cst_customer (cust_address, cust_industry, cust_level, cust_name, cust_phone, cust_source, cust_id) values (?, ?, ?, ?, ?, ?, ?)
- strategy = GenerationType.AUTO。
jpa 会选择 SEQUENCE。
Hibernate: drop table if exists cst_customer
Hibernate: drop table if exists hibernate_sequence
Hibernate: create table cst_customer (cust_id bigint not null, cust_address varchar(255), cust_industry varchar(255), cust_level varchar(255), cust_name varchar(255), cust_phone varchar(255), cust_source varchar(255), primary key (cust_id)) engine=InnoDB
Hibernate: create table hibernate_sequence (next_val bigint) engine=InnoDB
Hibernate: insert into hibernate_sequence values ( 1 )
Hibernate: select next_val as id_val from hibernate_sequence for update
Hibernate: update hibernate_sequence set next_val= ? where next_val=?
Hibernate: insert into cst_customer (cust_address, cust_industry, cust_level, cust_name, cust_phone, cust_source, cust_id) values (?, ?, ?, ?, ?, ?, ?)
Process finished with exit code 0
JPA 运行原理 + 基本操作。
api 基本介绍。
package com.geek.test;
import com.geek.domain.Customer;
import org.junit.Test;
import javax.persistence.*;
import javax.xml.crypto.dsig.TransformService;
public class JpaTest {
/**
* 测试 jpa 的保存。
* <p>
* 加载配置文件创建工厂(实体管理器工厂)对象。
* 通过实体管理器工厂获取实体管理器。
* 获取事务对象,开启事务。
* 完成增删改查操作。
* 提交事务。
* 释放资源。
*/
@Test
public void testSave() {
// 加载配置文件创建工厂(实体管理器工厂)对象。
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("myJPA");
// <persistence-unit name="myJPA" transaction-type="RESOURCE_LOCAL">
// 获取 EntityManager 对象。
// 通过实体管理器工厂获取实体管理器。
EntityManager entityManager = entityManagerFactory.createEntityManager();
// 内部维护了数据库信息。
// 内部维护了缓存信息。
// 内部维护了所有的实体管理器对象。
// 创建 EntityManagerFactory 的过程中会根据配置创建数据库表。
// ——>
// EntityManagerFactory 的创建过程比较浪费资源。
// 线程安全的对象。
// 多个线程访问同一个 EntityManagerFactory 不会有线程安全问题。
// ——>
// 创建一个公共的 EntityManagerFactory 对象。
// 静态代码块。
// entityManager.persist();
// entityManager.merge();
// entityManager.remove();
// entityManager.find();
// entityManager.getReference();// 根据 id 查询,
// 获取事务对象,开启事务。
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();// 开启事物。
// transaction.commit();
// transaction.rollback();
// 完成增删改查操作。
Customer customer = new Customer();// 模拟客户。
customer.setCustName("李");
customer.setCustAddress("武汉");
// 保存。
entityManager.persist(customer);
// 提交事务。
transaction.commit();
// 释放资源。
entityManager.close();
entityManagerFactory.close();
}
}
工具类抽取。
package com.geek.utils;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
/**
* 解决实体管理器工厂的浪费资源和耗时的问题。
* 通过静态代码块的形式,当程序第一次访问此工具类时,创建一个公共的实体管理器工厂对象。
*/
public class JpaUtils {
private static EntityManagerFactory entityManagerFactory;
static {
// 加载配置文件。创建 EntityManagerFactory。
entityManagerFactory = Persistence.createEntityManagerFactory("myJPA");
}
public static EntityManager getEntityManager() {
return entityManagerFactory.createEntityManager();
}
}
根据 id 查询。
- entityManager.find();
/**
* 根据 id 查询。
*/
@Test
public void testFindById() {
// 通过工具类获取 EntityManager。
EntityManager entityManager = JpaUtils.getEntityManager();
// 开启事务。
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
// 增删改查。~根据 id 查询。(实体管理器)。
try {
Object o = entityManager.find(Class.forName("com.geek.domain.Customer"), 1L);
System.out.println("o = " + o);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 提交事务。
tx.commit();
// 释放资源。
entityManager.close();
}
Hibernate: select customer0_.cust_id as cust_id1_0_0_, customer0_.cust_address as cust_add2_0_0_, customer0_.cust_industry as cust_ind3_0_0_, customer0_.cust_level as cust_lev4_0_0_, customer0_.cust_name as cust_nam5_0_0_, customer0_.cust_phone as cust_pho6_0_0_, customer0_.cust_source as cust_sou7_0_0_ from cst_customer customer0_ where customer0_.cust_id=?
o = Customer{custId=1, custName='李', custSource='null', custLevel='null', custIndustry='null', custPhone='null', custAddress='武汉'}
根据日志分析 jpa 执行流程。
select
customer0_.cust_id as cust_id1_0_0_,
customer0_.cust_address as cust_add2_0_0_,
customer0_.cust_industry as cust_ind3_0_0_,
customer0_.cust_level as cust_lev4_0_0_,
customer0_.cust_name as cust_nam5_0_0_,
customer0_.cust_phone as cust_pho6_0_0_,
customer0_.cust_source as cust_sou7_0_0_
from
cst_customer customer0_
where
customer0_.cust_id=?
- entityManager.getReference();
其他代码不变。
Object o = entityManager.find(Class.forName("com.geek.domain.Customer"), 1L);
变为
Object o = entityManager.getReference(Class.forName("com.geek.domain.Customer"), 1L);
延迟加载和立即加载。
entityManager.find();~立即加载。
- 查询的对象是对象本身。
- 调用 entityManager.getReference(); 方法立即发送 sql 语句查询数据库。
entityManager.getReference();~延迟加载。
- 获取的对象是一个动态代理对象。
- 调用 entityManager.getReference(); 方法不会立即发送 sql 语句查询数据库。
- 当调用查询结果对象的时候,才会发送查询的 sql 语句。
根据 id 删除。remove。
entityManager.remove(customer);// 参数需要一个 Object。
/**
* 根据 id 删除。
*/
@Test
public void testRemove() {
// 通过工具类获取 EntityManager。
EntityManager entityManager = JpaUtils.getEntityManager();
// 开启事务。
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
// 增删改查。~根据 id 删除。(实体管理器)。
Customer customer = entityManager.find(Customer.class, 1L);
entityManager.remove(customer);// 参数需要一个 Object。
// 提交事务。
tx.commit();
// 释放资源。
entityManager.close();
}
改。merge。
/**
* 改。
*/
@Test
public void testMerge() {
// 通过工具类获取 EntityManager。
EntityManager entityManager = JpaUtils.getEntityManager();
// 开启事务。
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
// 增删改查。~改。(实体管理器)。
Customer customer = entityManager.find(Customer.class, 2L);
// 更新。
customer.setCustIndustry("java 开发。");
entityManager.merge(customer);// 参数需要一个 Object。
// 提交事务。
tx.commit();
// 释放资源。
entityManager.close();
}
JPQL。
JPQL,Java Persistence Query Language。
sql 查询的是表和表中的字段。
jpql 查询的是实体类和类中的属性。
创建 query 查询对象。
对参数进行赋值。
查询。并得到返回结果。
package com.geek.test;
import com.geek.utils.JpaUtils;
import org.junit.Test;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;
import java.util.List;
/**
* 测试 jpql 查询。
*/
public class JPQLTest {
/**
* 查询全部。
* <p>
* jpql:from com.geek.domain.Customer
* sql:SELECT * FROM new_schema.cst_customer;
*/
@Test
public void testFindAll() {
// 获取 EntityManager 对象。
EntityManager entityManager = JpaUtils.getEntityManager();
// 开启事务。
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
// 查询全部。
String jpql = "from com.geek.domain.Customer";
Query query = entityManager.createQuery(jpql);// Query 对象才是执行 jpql 的对象。
// 发送查询。并封装结果集。
List resultList = query.getResultList();
for (Object o : resultList) {
System.out.println("o = " + o);
}
// 提交事务。
tx.commit();
// 释放资源。
entityManager.close();
}
}
Hibernate: select customer0_.cust_id as cust_id1_0_, customer0_.cust_address as cust_add2_0_, customer0_.cust_industry as cust_ind3_0_, customer0_.cust_level as cust_lev4_0_, customer0_.cust_name as cust_nam5_0_, customer0_.cust_phone as cust_pho6_0_, customer0_.cust_source as cust_sou7_0_ from cst_customer customer0_
o = Customer{custId=1, custName='李', custSource='null', custLevel='null', custIndustry='null', custPhone='null', custAddress='武汉'}
o = Customer{custId=2, custName='李', custSource='null', custLevel='null', custIndustry='java 开发。', custPhone='null', custAddress='武汉'}
/**
* 排序查询。(倒序)。
* sql:SELECT * FROM new_schema.cst_customer ORDER BY cust_id DESC;
* jpql:from Customer order by custId desc
*/
@Test
public void testOrders() {
// 获取 EntityManager 对象。
EntityManager entityManager = JpaUtils.getEntityManager();
// 开启事务。
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
// 查询全部。
String jpql = "from Customer order by custId desc";
Query query = entityManager.createQuery(jpql);// Query 对象才是执行 jpql 的对象。
// 发送查询。并封装结果集。
List resultList = query.getResultList();
for (Object o : resultList) {
System.out.println("o = " + o);
}
// 提交事务。
tx.commit();
// 释放资源。
entityManager.close();
}
/**
* 统计总数。
* sql:SELECT COUNT(cust_id) FROM new_schema.cst_customer;
* jpql:select count(custId) from Customer
*/
@Test
public void testCount() {
// 获取 EntityManager 对象。
EntityManager entityManager = JpaUtils.getEntityManager();
// 开启事务。
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
// 查询总数。
// 根据 jpql 语句创建 Query 对象。
String jpql = "select count(custId) from Customer";
Query query = entityManager.createQuery(jpql);// Query 对象才是执行 jpql 的对象。
// 对象参数赋值。
// 发送查询。并封装结果。(唯一)。
Object singleResult = query.getSingleResult();
System.out.println("singleResult = " + singleResult);
// 提交事务。
tx.commit();
// 释放资源。
entityManager.close();
}
/**
* 分页查询。
* sql:SELECT * FROM new_schema.cst_customer limit ?, ?;
* jpql:from Customer
*/
@Test
public void testPage() {
// 获取 EntityManager 对象。
EntityManager entityManager = JpaUtils.getEntityManager();
// 开启事务。
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
// 查询总数。
// 根据 jpql 语句创建 Query 对象。
String jpql = "from Customer";
Query query = entityManager.createQuery(jpql);// Query 对象才是执行 jpql 的对象。
// 对象参数赋值。分页参数。
// 起始索引。
query.setFirstResult(0);// limit ?
// query.setFirstResult(1);// limit ?, ?
// 每页条数。
query.setMaxResults(2);
// 发送查询。并封装结果。(唯一)。
List resultList = query.getResultList();
for (Object o : resultList) {
System.out.println("o = " + o);
}
// 提交事务。
tx.commit();
// 释放资源。
entityManager.close();
}
/**
* 条件查询。
* sql:SELECT * FROM new_schema.cst_customer where cust_name LIKE ?;
* jpql:from Customer where custName like ?
*/
@Test
public void testCondition() {
// 获取 EntityManager 对象。
EntityManager entityManager = JpaUtils.getEntityManager();
// 开启事务。
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
// 查询总数。
// 根据 jpql 语句创建 Query 对象。
String jpql = "from Customer where custName like ?";
Query query = entityManager.createQuery(jpql);// Query 对象才是执行 jpql 的对象。
// 对象参数赋值。占位符参数。
// 占位符位置。从 1 开始。
query.setParameter(1, "李%" );
// 发送查询。并封装结果。(唯一)。
List resultList = query.getResultList();
for (Object o : resultList) {
System.out.println("o = " + o);
}
// 提交事务。
tx.commit();
// 释放资源。
entityManager.close();
}
Spring Data Jpa。
https://spring.io/projects/spring-data-jpa
Spring Data JPA, part of the larger Spring Data family, makes it easy to easily implement JPA based repositories. This module deals with enhanced support for JPA based data access layers. It makes it easier to build Spring-powered applications that use data access technologies.
Implementing a data access layer of an application has been cumbersome for quite a while. Too much boilerplate code has to be written to execute simple queries as well as perform pagination, and auditing. Spring Data JPA aims to significantly improve the implementation of data access layers by reducing the effort to the amount that’s actually needed. As a developer you write your repository interfaces, including custom finder methods, and Spring will provide the implementation automatically.
Features
- Sophisticated support to build repositories based on Spring and JPA
- Support for Querydsl predicates and thus type-safe JPA queries
- Transparent auditing of domain class
- Pagination support, dynamic query execution, ability to integrate custom data access code
- Validation of @Query annotated queries at bootstrap time
- Support for XML based entity mapping
- JavaConfig based repository configuration by introducing @EnableJpaRepositories.
JPA 是一套规范,内部由接口的抽象类组成。
Hibernate 是一套成熟的 ORM 框架,而且 Hibernate 实现了 JPA 规范,所以也可以说 Hibernate 是 JPA 的一种实现方式。我们使用 JPA 的 API 编程,意味着站在更高的角度上看待问题(面向接口编程)。
Spring Data JPA 是 Spring 提供的一套对 JPA 操作更加高级的封装,是 在 JPA 规范下的专门用来进行数据持久化的解决方案。
使用。
创建工程导入坐标。
配置 Spring。
编写实体类(Customer),使用 jpa 注解配置映射关系。
编写一个符合 SpringDataJpa 的 dao 接口层。
- Maven pom。
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- Spring 对 orm 框架的支持。-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.4.11.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.4.11.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.19.Final</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.29</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.29</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-jpa -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.1.16.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
<!--<scope>test</scope>-->
</dependency>
经测试,2.1.16.RELEASE 的 spring-data-jpa 添加此依赖反而报错。
Caused by: java.lang.ClassNotFoundException: javax.el.ELManager
去掉这两个依赖即可。
<!-- el。使用 Spring Data Jpa 必须引入。-->
<!-- https://mvnrepository.com/artifact/javax.el/javax.el-api -->
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>2.2.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.web/javax.el -->
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.4</version>
</dependency>
</dependencies>
- 数据库。
CREATE SCHEMA `jpa_geek` DEFAULT CHARACTER SET utf8 ;
CREATE TABLE `jpa_geek`.`cst_customer` (
`cust_id` BIGINT(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)。',
`cust_name` VARCHAR(32) CHARACTER SET 'utf8' COLLATE 'utf8_general_ci' NOT NULL COMMENT '客户名称(公司名称)。',
`cust_source` VARCHAR(45) NULL COMMENT '客户信息来源。',
`cust_industry` VARCHAR(45) NULL COMMENT '客户所属行业。',
`cust_level` VARCHAR(45) NULL COMMENT '客户级别。',
`cust_address` VARCHAR(128) NULL COMMENT '客户地址。',
`cust_phone` VARCHAR(64) NULL COMMENT '客户联系电话。',
PRIMARY KEY (`cust_id`))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;
- 配置文件。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/data/jpa https://www.springframework.org/schema/data/jpa/spring-jpa.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 创建 EntityManagerFactory 对象交给 Spring 容器管理。-->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan" value="com.geek.domain"/>
<!-- jpa 实现厂家。-->
<property name="persistenceProvider">
<bean class="org.hibernate.jpa.HibernatePersistenceProvider"/>
</property>
<!-- jpa 的供应商适配器。-->
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!-- 自动创建数据库表。-->
<property name="generateDdl" value="false"/>
<!-- 指定数据库类型。-->
<property name="database" value="MYSQL"/>
<!--数据库方言。支持特有语法。-->
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/>
<!-- 是否显示 sql 语句。-->
<property name="showSql" value="true"/>
</bean>
</property>
<!-- jpa 方言。-->
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
</property>
</bean>
<!-- 数据库连接池。-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value=""/>
<property name="jdbcUrl" value="jdbc:mysql://192.168.142.135/new_schema?characterEncoding=UTF-8"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
</bean>
<!-- 整合 Spring Data Jpa。-->
<jpa:repositories base-package="com.geek.dao" transaction-manager-ref="transactionManager"
entity-manager-factory-ref="entityManagerFactory"/>
<!-- 配置事务管理器。-->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<!-- 声明式事务。-->
<!-- 注解扫描。-->
<context:component-scan base-package="com.geek"/>
</beans>
- 实体类。
package com.geek.domain;
import javax.persistence.*;
/**
* 实体类和表的映射关系。
* + @Entity
* + @Table
* <p>
* 类中属性和表中字段的映射关系。
* + @Id
* + @GeneratedValue
* + @Column
*/
@Entity
@Table(name = "cst_customer")
public class Customer {
@Id// 主键。
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "cust_id")
private Long custId;
@Column(name = "cust_name")
private String custName;
@Column(name = "cust_source")
private String custSource;
@Column(name = "cust_level")
private String custLevel;
@Column(name = "cust_industry")
private String custIndustry;
@Column(name = "cust_phone")
private String custPhone;
@Column(name = "cust_address")
private String custAddress;
public Long getCustId() {
return custId;
}
public void setCustId(Long custId) {
this.custId = custId;
}
public String getCustName() {
return custName;
}
public void setCustName(String custName) {
this.custName = custName;
}
public String getCustSource() {
return custSource;
}
public void setCustSource(String custSource) {
this.custSource = custSource;
}
public String getCustLevel() {
return custLevel;
}
public void setCustLevel(String custLevel) {
this.custLevel = custLevel;
}
public String getCustIndustry() {
return custIndustry;
}
public void setCustIndustry(String custIndustry) {
this.custIndustry = custIndustry;
}
public String getCustPhone() {
return custPhone;
}
public void setCustPhone(String custPhone) {
this.custPhone = custPhone;
}
public String getCustAddress() {
return custAddress;
}
public void setCustAddress(String custAddress) {
this.custAddress = custAddress;
}
@Override
public String toString() {
return "Customer{" +
"custId=" + custId +
", custName='" + custName + '\'' +
", custSource='" + custSource + '\'' +
", custLevel='" + custLevel + '\'' +
", custIndustry='" + custIndustry + '\'' +
", custPhone='" + custPhone + '\'' +
", custAddress='" + custAddress + '\'' +
'}';
}
}
- dao。
package com.geek.dao;
import com.geek.domain.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
/**
* 只需要编写 dao 层接口,不需要编写 dao 层实现类。
* <p>
* dao 规范。
* + 需要继承两个接口:Jpa
* + 需要提供相应的泛型。
* JpaRepository<操作的实体类类型,实体类中主键属性的类型>。
* ~ 封装了基本 CRUD 操作。
* JpaSpecificationExecutor<操作的实体类类型>
* ~ 封装了复杂查询(分页...)。
*/
public interface CustomerDao extends JpaRepository<Customer, Long>, JpaSpecificationExecutor<Customer> {
}
- 测试。
package com.geek.test;
import com.geek.dao.CustomerDao;
import com.geek.domain.Customer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.Optional;
@RunWith(SpringJUnit4ClassRunner.class)// 声明 Spring 提供的单元测试环境。
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class CustomerDaoTest {
@Autowired
private CustomerDao customerDao;
@Test
public void testFindOne() {
// Customer customer = customerDao.findOne(3L);
Optional<Customer> customer = customerDao.findById(1L);
System.out.println("customer = " + customer);
}
}
log4j:WARN No appenders could be found for logger (org.springframework.test.context.junit4.SpringJUnit4ClassRunner).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
Hibernate: select customer0_.cust_id as cust_id1_0_0_, customer0_.cust_address as cust_add2_0_0_, customer0_.cust_industry as cust_ind3_0_0_, customer0_.cust_level as cust_lev4_0_0_, customer0_.cust_name as cust_nam5_0_0_, customer0_.cust_phone as cust_pho6_0_0_, customer0_.cust_source as cust_sou7_0_0_ from cst_customer customer0_ where customer0_.cust_id=?
customer = Optional[Customer{custId=1, custName='李', custSource='null', custLevel='null', custIndustry='null', custPhone='null', custAddress='武汉'}]
Process finished with exit code 0
package com.geek.test;
import com.geek.dao.CustomerDao;
import com.geek.domain.Customer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
import java.util.Optional;
@RunWith(SpringJUnit4ClassRunner.class)// 声明 Spring 提供的单元测试环境。
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class CustomerDaoTest {
@Autowired
private CustomerDao customerDao;
/**
* 根据 id 查询。
*/
@Test
public void testFindOne() {
// Customer customer = customerDao.findOne(3L);
Optional<Customer> customer = customerDao.findById(1L);
System.out.println("customer = " + customer);
}
/**
* 保存或更新。
* 根据传递的对象查询是否存在主键。
* 如果没有 id 主键属性,保存。
* 如果有 id 主键属性,根据 id 查询数据,更新数据。
*/
@Test
public void testSave() {
Customer customer = new Customer();
customer.setCustName("Geek 李");
customer.setCustLevel("vip");
customerDao.save(customer);
// Hibernate: insert into cst_customer (cust_address, cust_industry, cust_level, cust_name, cust_phone, cust_source) values (?, ?, ?, ?, ?, ?)
}
@Test
public void testUpdate() {
Customer customer = new Customer();
customer.setCustId(3L);
customer.setCustName("Geek");
customer.setCustAddress("武汉");
customer.setCustLevel("vip");
customerDao.save(customer);
// Hibernate: update cst_customer set cust_address=?, cust_industry=?, cust_level=?, cust_name=?, cust_phone=?, cust_source=? where cust_id=?
}
/**
* 删除。
*/
@Test
public void testDelete() {
customerDao.deleteById(6L);
// Hibernate: select customer0_.cust_id as cust_id1_0_0_, customer0_.cust_address as cust_add2_0_0_, customer0_.cust_industry as cust_ind3_0_0_, customer0_.cust_level as cust_lev4_0_0_, customer0_.cust_name as cust_nam5_0_0_, customer0_.cust_phone as cust_pho6_0_0_, customer0_.cust_source as cust_sou7_0_0_ from cst_customer customer0_ where customer0_.cust_id=?
//Hibernate: delete from cst_customer where cust_id=?
}
/**
* 查询所有。
*/
@Test
public void testFindAll() {
List<Customer> all = customerDao.findAll();
all.forEach(System.out::println);
}
}