1JPA 是什么
JPA(Java Persistence API)是 Sun 官方提出的 Java 持久化规范。它为 Java 开发人员提供了一种对象 / 关联映射工具来管理 Java 应用中的关系数据。它的出现主要是为了简化现有的持久化开发工作和整合 ORM 技术,结束现在 Hibernate、TopLink、JDO 等 ORM 框架各自为营的局面。
值得注意的是,JPA 是在充分吸收了现有的 Hibernate、TopLink、JDO 等 ORM 框架的基础上发展而来的,具有易于使用、伸缩性强等优点。从目前的开发社区的反应上看,JPA 受到了极大的支持和赞扬,其中就包括了 Spring 与 EJB 3.0 的开发团队。
JPA 是一套规范,不是一套产品,那么像 Hibernate、TopLink、JDO 它们是一套产品,如果说这些产品实现了这个 JPA 规范,那么我们就可以称他们为 JPA 的实现产品。
2 Spring Data JPA
Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套 JPA 应用框架,可以让开发者用极简的代码即可实现对数据的访问和操作。它提供了包括增、删、改、查等在内的常用功能,且易于扩展,学习并使用 Spring Data JPA 可以极大提高开发效率。Spring Data JPA 其实就是 Spring 基于 Hibernate 之上构建的 JPA 使用解决方案,方便在 Spring Boot 项目中使用 JPA 技术。
Spring Data JPA 让我们解脱了 DAO 层的操作,基本上所有 CRUD 都可以依赖于它实现。
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
配置文件
spring.datasource.url=jdbc:mysql://localhost:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.properties.hibernate.hbm2ddl.auto=create
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
#SQL 输出
spring.jpa.show-sql=true
#format 一下 SQL 进行输出
spring.jpa.properties.hibernate.format_sql=true
hibernate.hbm2ddl.auto 参数的作用主要用于:自动创建、更新、验证数据库表结构,有四个值。
create:
每次加载 Hibernate 时都会删除上一次生成的表,然后根据 model
类再重新来生成新表,哪怕两次没有任何改变也要这样执行,这就是导致数据库表数据丢失的一个重要原因。
create-drop:
每次加载 Hibernate 时根据 model 类生成表,但是 sessionFactory 一关闭,表就自动删除。
update:
最常用的属性,第一次加载 Hibernate 时根据 model 类会自动建立起表的结构(前提是先建立好数据库),以后加载
Hibernate 时根据 model
类自动更新表结构,即使表结构改变了,但表中的行仍然存在,不会删除以前的行。要注意的是当部署到服务器后,表结构是不会被马上建立起来的,是要等应用第一次运行起来后才会。
validate :
每次加载 Hibernate 时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。
其中:
dialect 主要是指定生成表名的存储引擎为 InnoDB show-sql 是否在日志中打印出自动生成的 SQL,方便调试的时候查看
实体类
import javax.persistence.*;
@Api("使用jpa 创建数据库 映射实体")
@Entity // 数据库表名默认与类名相同
@Data
@EqualsAndHashCode
@ToString
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false,unique = true, length=32)
private String userName;
@Column(nullable = false)
private String passWord;
@Column(nullable = false, unique = false)
private String email;
@Column(nullable = true,unique = false)
private String nickName;
@Column(nullable = false)
private String regTime;
@Transient //并非一个到数据库表的字段的映射,ORM 框架将忽略该属性。
private String username;
public Student(String userName, String passWord, String email, String nickName, String regTime) {
this.userName = userName;
this.passWord = passWord;
this.email = email;
this.nickName = nickName;
this.regTime = regTime;
}
}
实体类中的注解说明
@Entity(name=“EntityName”) 必须,用来标注一个数据库对应的实体,数据库中创建的表名默认和类名一致。其中,name 为可选,对应数据库中一个表,使用此注解标记 Pojo 是一个 JPA 实体。
@Table(name="",catalog="",schema="") 可选,用来标注一个数据库对应的实体,数据库中创建的表名默认和类名一致。通常和 @Entity 配合使用,只能标注在实体的 class 定义处,表示实体对应的数据库表的信息。
@Id 必须,@Id 定义了映射到数据库表的主键的属性,一个实体只能有一个属性被映射为主键。
@GeneratedValue(strategy=GenerationType,generator="") 可选,strategy: 表示主键生成策略,有 AUTO、INDENTITY、SEQUENCE 和 TABLE 4 种,分别表示让 ORM 框架自动选择,generator: 表示主键生成器的名称。
@Column(name = “user_code”, nullable = false, length=32) 可选,@Column 描述了数据库表中该字段的详细定义,这对于根据 JPA 注解生成数据库表结构的工具。name: 表示数据库表中该字段的名称,默认情形属性名称一致;nullable: 表示该字段是否允许为 null,默认为 true;unique: 表示该字段是否是唯一标识,默认为 false;length: 表示该字段的大小,仅对 String 类型的字段有效。
@Transient可选,@Transient 表示该属性并非一个到数据库表的字段的映射,ORM 框架将忽略该属性。
@Enumerated 可选,使用枚举的时候,我们希望数据库中存储的是枚举对应的 String 类型,而不是枚举的索引值,需要在属性上面添加 @Enumerated(EnumType.STRING) 注解。
dao层
创建的 Repository 只要继承 JpaRepository 就可以了。
JpaRepository 类中
@Repository
public interface StudentRepository extends JpaRepository<Student,Long> {
public Student findByUserName(String userName);
//自定义查询
//根据名字查询,并且 emaile模糊查询
List<Student> findByNickNameLikeOrEmailLike(String nickName, String email);
List<Student> findByuserNameLike(String userName);
}
JpaRepository 方法中为我们默认提供了很多crud方法,以及查询方法
@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
List<T> findAll();
List<T> findAll(Sort var1);
List<T> findAllById(Iterable<ID> var1);
<S extends T> List<S> saveAll(Iterable<S> var1);
void flush();
<S extends T> S saveAndFlush(S var1);
void deleteInBatch(Iterable<T> var1);
void deleteAllInBatch();
T getOne(ID var1);
<S extends T> List<S> findAll(Example<S> var1);
<S extends T> List<S> findAll(Example<S> var1, Sort var2);
}
PagingAndSortingRepository 集成了CrudRepository接口
CrudRepository 接口为我们提供了很多的查询 删除 以及新增修改方法
public interface CrudRepository<T, ID> extends Repository<T, ID> {
<S extends T> S save(S var1);
<S extends T> Iterable<S> saveAll(Iterable<S> var1);
Optional<T> findById(ID var1);
boolean existsById(ID var1);
Iterable<T> findAll();
Iterable<T> findAllById(Iterable<ID> var1);
long count();
void deleteById(ID var1);
void delete(T var1);
void deleteAll(Iterable<? extends T> var1);
void deleteAll();
}
以及PagingAndSortingRepository , QueryByExampleExecutor 查询接口
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
Iterable<T> findAll(Sort var1);
Page<T> findAll(Pageable var1);
}
public interface QueryByExampleExecutor<T> {
<S extends T> Optional<S> findOne(Example<S> var1);
<S extends T> Iterable<S> findAll(Example<S> var1);
<S extends T> Iterable<S> findAll(Example<S> var1, Sort var2);
<S extends T> Page<S> findAll(Example<S> var1, Pageable var2);
<S extends T> long count(Example<S> var1);
<S extends T> boolean exists(Example<S> var1);
}
当然这些查询方法在无法满足我的业务查询情况下,jpa为我们提供了自定义查询规则,我们只需要在接口中自定义查询即可。
但必须遵守一个自定义查询规则。
Spring Data JPA 可以根据接口方法名来实现数据库操作,主要的语法是
findXXBy、readAXXBy、queryXXBy、countXXBy、getXXBy 后面跟属性名称,利用这个功能仅需要在定义的
Repository 中添加对应的方法名即可,使用时 Spring Boot 会自动帮我们实现,示例如下。
如:
根据名字模糊查询或者 email模糊查询
List<Student> findByNickNameLikeOrEmailLike(String nickName, String email);
根据名字模糊查询
List<Student> findByuserNameLike(String userName);
当然删除也是一样,只要遵循Jpa命名规则即可,Jpa会在底层帮我们自动生成sql语句
修改、删除、统计也是类似语法:
Long deleteById(Long id);
Long countByUserName(String userName)
可以根据查询的条件不断地添加和拼接,Spring Boot 都可以正确解析和执行,其他使用示例可以参考下表。
具体的关键字,使用方法和生产成 SQL 如下表所示
Keyword | Sample | JPQL snippet |
---|---|---|
And | findByLastnameAndFirstname | … where x.lastname = ?1 and x.firstname = ?2 |
Or | findByLastnameOrFirstname | … where x.lastname = ?1 or x.firstname = ?2 |
Is,Equals | findByFirstnameIs,findByFirstnameEquals | … where x.firstname = ?1 |
Between | findByStartDateBetween | … where x.startDate between ?1 and ?2 |
LessThan | findByAgeLessThan | … where x.age < ?1 |
LessThanEqual | findByAgeLessThanEqual | … where x.age ⇐ ?1 |
GreaterThan | findByAgeGreaterThan | … where x.age > ?1 |
GreaterThanEqual | findByAgeGreaterThanEqual | … where x.age >= ?1 |
After | findByStartDateAfter | … where x.startDate > ?1 |
Before | findByStartDateBefore | … where x.startDate < ?1 |
IsNull | findByAgeIsNull | … where x.age is null |
IsNotNull,NotNull | findByAge(Is)NotNull | … where x.age not null |
Like | findByFirstnameLike | … where x.firstname like ?1 |
NotLike | findByFirstnameNotLike | … where x.firstname not like ?1 |
StartingWith | findByFirstnameStartingWith | … where x.firstname like ?1 (parameter bound with appended %) |
EndingWith | findByFirstnameEndingWith | … where x.firstname like ?1 (parameter bound with prepended %) |
Containing | findByFirstnameContaining | … where x.firstname like ?1 (parameter bound wrapped in %) |
OrderBy | findByAgeOrderByLastnameDesc | … where x.age = ?1 order by x.lastname desc |
Not | findByLastnameNot | … where x.lastname <> ?1 |
In | findByAgeIn(Collectionages) | … where x.age in ?1 |
NotIn | findByAgeNotIn(Collectionage) | … where x.age not in ?1 |
TRUE | findByActiveTrue() | … where x.active = true |
FALSE | findByActiveFalse() | … where x.active = false |
IgnoreCase | findByFirstnameIgnoreCase | … where UPPER(x.firstame) = UPPER(?1) |
controller
@Api("api jpa测试返回 controller ")
@RestController
public class StudentController {
@Autowired
private StudentRepository studentRepository;
@RequestMapping("test1")
public void test1(){
Student s1 = new Student("张三5", "123123", "123@qq.com","nickName1", LocalDate.now().toString());
studentRepository.save(s1);
List<Student> student = studentRepository.findByUserNameOrEmailLike("张", "3");
System.out.println(student);
s1 = new Student("张三6", "1231232222", "234@qq.com","nickName2", LocalDate.now().toString());
studentRepository.save(s1);
//studentRepository.deleteById(1L);
s1 = new Student("张三7", "12312322221", "2345@qq.com","nickName3", LocalDate.now().toString());
studentRepository.save(s1);
List<Student> nic = studentRepository.findByNickNameLikeOrEmailLike("%nic%", "%3%");
System.out.println("nic " + nic);
List<Student> byuserNameLike = studentRepository.findByuserNameLike("%张%");
System.out.println("byuserNameLike " + byuserNameLike);
}
}
控制台打印结果
Hibernate: insert into student (email, nick_name, pass_word, reg_time, user_name) values (?, ?, ?, ?, ?)
2019-06-29 17:07:18.492 INFO 1256 --- [nio-8181-exec-1] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory
Hibernate: select student0_.id as id1_0_, student0_.email as email2_0_, student0_.nick_name as nick_nam3_0_, student0_.pass_word as pass_wor4_0_, student0_.reg_time as reg_time5_0_, student0_.user_name as user_nam6_0_ from student student0_ where student0_.user_name=? or student0_.email like ?
[]
Hibernate: insert into student (email, nick_name, pass_word, reg_time, user_name) values (?, ?, ?, ?, ?)
Hibernate: insert into student (email, nick_name, pass_word, reg_time, user_name) values (?, ?, ?, ?, ?)
Hibernate: select student0_.id as id1_0_, student0_.email as email2_0_, student0_.nick_name as nick_nam3_0_, student0_.pass_word as pass_wor4_0_, student0_.reg_time as reg_time5_0_, student0_.user_name as user_nam6_0_ from student student0_ where student0_.nick_name like ? or student0_.email like ?
nic [Student(id=1, userName=张三5, passWord=123123, email=123@qq.com, nickName=nickName1, regTime=2019-06-29, username=张三5), Student(id=2, userName=张三6, passWord=1231232222, email=234@qq.com, nickName=nickName2, regTime=2019-06-29, username=张三6), Student(id=3, userName=张三7, passWord=12312322221, email=2345@qq.com, nickName=nickName3, regTime=2019-06-29, username=张三7)]
Hibernate: select student0_.id as id1_0_, student0_.email as email2_0_, student0_.nick_name as nick_nam3_0_, student0_.pass_word as pass_wor4_0_, student0_.reg_time as reg_time5_0_, student0_.user_name as user_nam6_0_ from student student0_ where student0_.user_name like ?
byuserNameLike [Student(id=1, userName=张三5, passWord=123123, email=123@qq.com, nickName=nickName1, regTime=2019-06-29, username=张三5), Student(id=2, userName=张三6, passWord=1231232222, email=234@qq.com, nickName=nickName2, regTime=2019-06-29, username=张三6), Student(id=3, userName=张三7, passWord=12312322221, email=2345@qq.com, nickName=nickName3, regTime=2019-06-29, username=张三7)]
数据库生成的表
总结
使用Jpa可以大大大解决我们操作数据库,解放生产力,自动生成sql,自动建表等。
但也存在一个缺点,sql已经生成,我们无法对sql进行操作,无法进行sql调优。
单标crud效率贼高,但是多表查询,级联查询等操作就要用到 Jpa高级特性了,面对实际开发中的复杂业务就显得有点儿地效率了,这方面用orm框架 mybatis比较多。