- 本文作者:唐亚峰
- 本文链接:http://blog.battcn.com/2018/05/08/springboot/v2-orm-jpa/
- 版权声明:本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0许可协议。转载请注明出处!
SpringBoot
是为了简化Spring
应用的创建、运行、调试、部署等一系列问题而诞生的产物,自动装配的特性让我们可以更好的关注业务本身而不是外部的XML配置,我们只需遵循规范,引入相关的依赖就可以轻易的搭建出一个 WEB 工程
SpringBoot系列博客目录,含1.5.X版本和2.X版本
上一篇介绍了Spring JdbcTemplate
的使用,对比原始的JDBC
而言,它更加的简洁。但随着表的增加,重复的CRUD工作让我们苦不堪言,这时候Spring Data Jpa
的作用就体现出来了…..
JPA是Java Persistence API
的简称,中文名Java持久层API,是官方(Sun)在JDK5.0后提出的Java持久化规范。其目的是为了简化现有JAVA EE
和JAVA SE
应用开发工作,以及整合现有的ORM技术实现规范统一
JPA的总体思想和现有Hibernate
、TopLink
、JDO
等ORM框架大体一致。总的来说,JPA包括以下3方面的技术:
JPA只是一种规范,它需要第三方自行实现其功能,在众多框架中Hibernate
是最为强大的一个。从功能上来说,JPA就是Hibernate功能的一个子集。Hibernate 从3.2开始,就开始兼容JPA。同时Hibernate3.2获得了Sun TCK的JPA(Java Persistence API) 兼容认证。
常见的ORM框架中Hibernate
的JPA最为完整,因此Spring Data JPA
是采用基于JPA规范的Hibernate
框架基础下提供了Repository
层的实现。Spring Data Repository
极大地简化了实现各种持久层的数据库访问而写的样板代码量,同时CrudRepository
提供了丰富的CRUD功能去管理实体类。
优点
SpringBoot
简化的大量的配置,关系映射多表查询配置依旧不容易JdbcTemplate
、Mybatis
等ORM框架,它的性能无异于是最差的在pom.xml
中添加spring-boot-starter-data-jpa
的依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-jpaartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
在application.properties
中添加如下配置。值得注意的是,SpringBoot默认会自动配置DataSource
,它将优先采用HikariCP
连接池,如果没有该依赖的情况则选取tomcat-jdbc
,如果前两者都不可用最后选取Commons DBCP2
。通过spring.datasource.type
属性可以指定其它种类的连接池
spring.datasource.url=jdbc:mysql://localhost:3306/chapter5?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false
spring.datasource.password=root
spring.datasource.username=root
#spring.datasource.type
# JPA配置
spring.jpa.hibernate.ddl-auto=update
# 输出日志
spring.jpa.show-sql=true
# 数据库类型
spring.jpa.database=mysql
ddl-auto 几种属性
由于上面我们采用的是spring.jpa.hibernate.ddl-auto=update
方式,因此这里可以跳过手动建表的操作
JPA规范注解坐落在javax.persistence
包下,@Id
注解一定不要引用错了,否则会报错。@GeneratedValue(strategy = GenerationType.IDENTITY)
自增策略,不需要映射的字段可以通过@Transient
注解排除掉
常见的几种自增策略
package com.battcn.entity;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import java.io.Serializable;
/**
* Created by Donghua.Chen on 2018/6/1.
*/
@Entity(name = "t_user")
public class User implements Serializable {
private static final long serialVersionUID = 8655851615465363473L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
/**
* TODO 忽略该字段的映射
*/
@Transient
private String email;
// TODO 省略get set
}
创建UserRepository
数据访问层接口,需要继承JpaRepository
,第一个泛型参数是实体对象的名称,第二个是主键类型。只需要这样简单的配置,该UserRepository
就拥常用的CRUD
功能,JpaRepository
本身就包含了常用功能,剩下的查询我们按照规范写接口即可,JPA支持@Query
注解写HQL,也支持findAllByUsername
这种根据字段名命名的方式(强烈推荐IntelliJ IDEA
对JPA支持非常NICE)
package com.battcn.repository;
import com.battcn.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* t_user 操作
*
* @author Levin
* @since 2018/5/7 0007
*/
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
/**
* 根据用户名查询用户信息
*
* @param username 用户名
* @return 查询结果
*/
List findAllByUsername(String username);
}
完成数据访问层接口后,最后编写一个junit
测试类来检验代码的正确性。
下面的几个操作中,只有findAllByUsername
是我们自己编写的代码,其它的都是继承自JpaRepository
接口中的方法,更关键的是分页及排序是如此的简单实例化一个Pageable
即可…
package com.winterchen;
import com.winterchen.domain.User;
import com.winterchen.repository.UserRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootDataJpaApplicationTests {
@Test
public void contextLoads() {
}
private static final Logger log = LoggerFactory.getLogger(SpringBootDataJpaApplicationTests.class);
@Autowired
private UserRepository userRepository;
@Test
public void test1() throws Exception {
final User user = userRepository.save(new User("u1", "p1"));
log.info("[添加成功] - [{}]", user);
final List u1 = userRepository.findAllByUsername("u1");
log.info("[条件查询] - [{}]", u1);
Pageable pageable = PageRequest.of(0, 10, Sort.by(Sort.Order.desc("username")));
final Page users = userRepository.findAll(pageable);
log.info("[分页+排序+查询所有] - [{}]", users.getContent());
userRepository.findById(users.getContent().get(0).getId()).ifPresent(user1 -> log.info("[主键查询] - [{}]", user1));
final User edit = userRepository.save(new User(user.getId(), "修改后的ui", "修改后的p1"));
log.info("[修改成功] - [{}]", edit);
userRepository.deleteById(user.getId());
log.info("[删除主键为 {} 成功] - [{}]", user.getId());
}
}
运行结果
Hibernate: insert into t_user (password, username) values (?, ?)
2018-06-01 14:50:18.008 INFO 54881 --- [ main] c.w.SpringBootDataJpaApplicationTests : [添加成功] - [com.winterchen.domain.User@175c5c3a]
2018-06-01 14:50:18.038 INFO 54881 --- [ main] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory
Hibernate: select user0_.id as id1_0_, user0_.password as password2_0_, user0_.username as username3_0_ from t_user user0_ where user0_.username=?
2018-06-01 14:50:18.313 INFO 54881 --- [ main] c.w.SpringBootDataJpaApplicationTests : [条件查询] - [[com.winterchen.domain.User@2baac4a7, com.winterchen.domain.User@25f0c5e7]]
Hibernate: select user0_.id as id1_0_, user0_.password as password2_0_, user0_.username as username3_0_ from t_user user0_ order by user0_.username desc limit ?
2018-06-01 14:50:18.342 INFO 54881 --- [ main] c.w.SpringBootDataJpaApplicationTests : [分页+排序+查询所有] - [[com.winterchen.domain.User@760f1081, com.winterchen.domain.User@52621501]]
Hibernate: select user0_.id as id1_0_0_, user0_.password as password2_0_0_, user0_.username as username3_0_0_ from t_user user0_ where user0_.id=?
2018-06-01 14:50:18.357 INFO 54881 --- [ main] c.w.SpringBootDataJpaApplicationTests : [主键查询] - [com.winterchen.domain.User@111a7973]
Hibernate: select user0_.id as id1_0_0_, user0_.password as password2_0_0_, user0_.username as username3_0_0_ from t_user user0_ where user0_.id=?
Hibernate: update t_user set password=?, username=? where id=?
2018-06-01 14:50:18.387 INFO 54881 --- [ main] c.w.SpringBootDataJpaApplicationTests : [修改成功] - [com.winterchen.domain.User@6befbb12]
Hibernate: select user0_.id as id1_0_0_, user0_.password as password2_0_0_, user0_.username as username3_0_0_ from t_user user0_ where user0_.id=?
Hibernate: delete from t_user where id=?
2018-06-01 14:50:18.420 INFO 54881 --- [ main] c.w.SpringBootDataJpaApplicationTests : [删除主键为 2 成功] - [{}]
2018-06-01 14:50:18.456 INFO 54881 --- [ Thread-2] o.s.w.c.s.GenericWebApplicationContext : Closing org.springframework.web.context.support.GenericWebApplicationContext@7526515b: startup date [Fri Jun 01 14:50:09 CST 2018]; root of context hierarchy
2018-06-01 14:50:18.462 INFO 54881 --- [ Thread-2] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2018-06-01 14:50:18.464 INFO 54881 --- [ Thread-2] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2018-06-01 14:50:18.491 INFO 54881 --- [ Thread-2] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
更多内容请参考官方文档
目前很多大佬都写过关于SpringBoot
的教程了,如有雷同,请多多包涵,本教程基于最新的spring-boot-starter-parent:2.0.1.RELEASE
编写,包括新版本的特性都会一起介绍…
springboot技术交流群:681513531
个人博客:https://winterchen.com
全文代码:https://github.com/WinterChenS/springboot-learning-experience