JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体[对象持久化。
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 中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,这样的支持能够让开发者最大限度的使用面向对象的模型设计企业应用,而不需要自行处理这些特性在关系数据库的持久化。
org.springframework.boot
spring-boot-starter-data-jpa
mysql
mysql-connector-java
com.alibaba
druid
1.1.5
org.hibernate
hibernate-core
5.3.7.Final
CREATE DATABASE `jpatest` ;
也许您想说,为啥不写创建表的语句,因为JPA能帮我们根据实体信息生成对应的数据库表,是不是超级方便呢?
# 数据库基本配置
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/jpatest?serverTimezone=Asia/Shanghai
spring.datasource.username=JPA_TEST(Zujuq4uSPX4ciVivGYDFlSYkxXGJjkcl)
spring.datasource.password=JPA_TEST(gNaKlGt35SGsU6HkpmJKVVKUldFFncZx)
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.database=MYSQL
# 显示后台处理的SQL语句,方便在开发阶段调试
spring.jpa.show-sql=true
# 自动检查实体和数据库表是否一致,如果不一致则会进行更新数据库表
spring.jpa.hibernate.ddl-auto=update
#自动将驼峰命名转换为小写和下划线 userId -> user_id
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
#不加这句则默认为myisam引擎
spring.jpa.database-platform= org.hibernate.dialect.MySQL5InnoDBDialect
数据库用户名和密码如果不知道是怎么回事的话,请参考Spring Boot整合Jasypt增强应用安全这篇文章哦。
import lombok.Data;
import javax.persistence.*;
import java.util.Date;
@Entity //声明这个是一个数据库对应的实体
@Table(name = "user") //指定表的名字
@Data //为了节省代码使用了Lombok插件
public class User {
@Id //声明这个是数据库表的主键Id
@GeneratedValue(strategy = GenerationType.IDENTITY) //声明主键的生成方式
/*
JPA提供的四种标准用法为TABLE,SEQUENCE,IDENTITY,AUTO.
TABLE:使用一个特定的数据库表格来保存主键。
SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。
IDENTITY:主键由数据库自动生成(主要是自动增长型)
AUTO:主键由程序控制(也是默认的,在指定主键时,如果不指定主键生成策略,默认为AUTO)
*/
@Column(name = "id", unique = true, nullable = false)
/*
指定这个是一个数据库字段
name 属性表示字段名称
unique 属性表示该字段是否唯一 true : 唯一 false : 不唯一
注意: 非主键的唯一字段会生成一个唯一索引,命名规格为 UK_随机字符串
nullable 属性表示该字段是否可空 true :可以为空
length 属性表示该字段的长度
*/
private Integer id;
@Column(name = "user_name",length = 32)
private String userName;
@Column(name = "name",length = 32, unique = true, nullable = false)
private String name;
@Column(name = "password",length = 32)
private String password;
@Temporal(TemporalType.TIMESTAMP)
/*
该注解表示这个字段是一个时间类型的
DATE: 表示只存日期 如: 2019-07-31
TIME: 表示只存时间 如: 19:52:25
TIMESTAMP: 表示是一个带有时间的日期 如: 2019-07-31 19:52:25
*/
@Column(name = "gmt_create")
private Date gmtCreate;
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"id\":")
.append(id);
sb.append(",\"userName\":\"")
.append(userName).append('\"');
sb.append(",\"name\":\"")
.append(name).append('\"');
sb.append(",\"password\":\"")
.append(password).append('\"');
sb.append(",\"gmtCreate\":\"")
.append(gmtCreate).append('\"');
sb.append("}");
return sb.toString();
}
}
import com.example.demo.jpa.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
import java.io.Serializable;
/**
* @author Jackieonway
* @version 1.0
* @interfaceName UserDao
**/
@Repository
public interface UserDao extends JpaRepository,JpaSpecificationExecutor,Serializable {
}
如果不使用JPA的高级属性的话, 只需要继承JpaRepository
import com.example.demo.jpa.dao.UserDao;
import com.example.demo.jpa.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.List;
/**
* @author Jackieonway
* @version 1.0
* @className UserService
**/
@Service
public class UserService {
@Autowired
private UserDao userDao;
public Page get(){
return userDao.findAll(PageRequest.of(0,10,Sort.Direction.DESC,"name","gmtCreate"));
}
public Page getAll(){
List orders = new ArrayList<>();
orders.add(new Sort.Order(Sort.Direction.DESC,"name"));
orders.add(new Sort.Order(Sort.Direction.ASC,"gmtCreate"));
return userDao.findAll(PageRequest.of(0,10,Sort.by(orders)));
}
public Page getAllPage(){
Specification specification = new Specification() {
@Override
public Predicate toPredicate(Root root, CriteriaQuery> criteriaQuery,
CriteriaBuilder criteriaBuilder) {
Predicate p1 = criteriaBuilder.like(root.get("name").as(String.class), "%12%");
Predicate p2 = criteriaBuilder.like(root.get("password").as(String.class), "12%");
Predicate p3 = criteriaBuilder.like(root.get("userName").as(String.class), "12%");
criteriaQuery.where(criteriaBuilder.and(p1,criteriaBuilder.or(p2,p3))); //where p1 and ( p2 or p3);
criteriaQuery.orderBy(criteriaBuilder.asc(root.get("userName").as(String.class)),
criteriaBuilder.desc(root.get("name").as(String.class))); // order by user_name asc,name desc
return criteriaQuery.getRestriction();
}
};
return userDao.findAll(specification,PageRequest.of(0,10));
}
}
好了,下面就建一个测试类来验证下整合的结果吧
@Autowired
private UserService userService;
@Test
public void contextLoads() {
Page page = userService.get();
System.out.println("========= "+page);
}
@Test
public void testGetAll() {
Page page = userService.getAll();
System.out.println("========= "+page);
}
@Test
public void testGetAllPage() {
Page page = userService.getAllPage();
System.out.println("========= "+page);
}
Hibernate: select user0_.id as id1_0_, user0_.gmt_create as gmt_crea2_0_, user0_.name as name3_0_, user0_.password as password4_0_, user0_.user_name as user_nam5_0_ from user user0_ order by user0_.name desc, user0_.gmt_create desc limit ?
========= Page 1 of 0 containing UNKNOWN instances
由于我们配置了运行时数据执行的语句,控制台就会打印出执行的语句,生产环境中可以将该参数取消,减少日志输出。