背景说明
目前Spring Boot大行其道,其便捷性给开发人员带来了很大的效率提升。它简化了样板配置,通过关键的说明或者约定就能快速搭建起想要的框架。
Spring Boot可适配的组件众多,由于绝大多数应用系统都会同数据库打交道的,这就涉及到Spring Data家族的使用。为什么说是家族呢,因为Spring Data包含JDBC、JPA、LDAP、MongoDB、Redis、Elasticsearch等多种类型的数据源适配操作。
通过Spring Data的封装组件化之后,可带来同等的编程体验。那么简化的背后其实是复杂的逻辑支撑,例如无需任何编码只需@Autowired private JdbcTemplate jdbcTemplate;
就能通过jdbcTemplate
执行sql操作数据库,它背后发生了什么呢,本系列博客就将通过源码和示例结合的方式,解析Spring Data背后的实现原理。目的是为了通过了解Spring Data的框架结构、设计思路来提升平时业务系统开发中的代码设计能力,搬砖也要搬的有水平!
工程搭建
首先创建一个Spring Boot工程,有两种方式:
- 方式一
通过在线的https://start.spring.io/创建,其中依赖的模块选上JDBC、H2
点击 Generte Project就能下载一个zip的工程文件,解压导入即可。第一次导入可能有点慢,会下载相关的maven依赖包 - 方式二
通过idea创建,idea集成了Spring Initializr向导
编码实现
工程搭好后,我们用最少的代码量,实现一个对User的操作(其中依赖了Lombok减少样板代码)。
- 定义Entity
@Data
@Entity
@Table(name = "t_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
}
- 定义Repository
public interface UserRepository extends JpaRepository<User,Long> {
}
- application.propertites
这里为了能直接Run起来,使用h2作为数据源
spring.datasource.url=jdbc:h2:mem:test;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.show-sql=true
- Test
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class JpaTests {
@Autowired
private UserRepository userRepository;
@Before
public void before(){
User user = new User();
user.setFirstName("张");
user.setLastName("三");
userRepository.save(user);
}
@Test
public void queryAll() {
List<User> users = userRepository.findAll();
log.info("result:{}",users);
}
}
- 控制台输出
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.0.4.RELEASE)
2018-10-25 09:32:19.848 INFO 37469 --- [ main] com.learn.data.JpaTests : Starting JpaTests on fangliangshengdeMacBook-Pro.local with PID 37469 (started by fangliangsheng in /Users/fangliangsheng/Documents/git/learn-data)
2018-10-25 09:32:19.849 INFO 37469 --- [ main] com.learn.data.JpaTests : No active profile set, falling back to default profiles: default
2018-10-25 09:32:19.868 INFO 37469 --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@2d52216b: startup date [Thu Oct 25 09:32:19 CST 2018]; root of context hierarchy
2018-10-25 09:32:20.645 INFO 37469 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2018-10-25 09:32:20.791 INFO 37469 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2018-10-25 09:32:20.846 INFO 37469 --- [ main] j.LocalContainerEntityManagerFactoryBean : Building JPA container EntityManagerFactory for persistence unit 'default'
2018-10-25 09:32:20.867 INFO 37469 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [
name: default
...]
2018-10-25 09:32:20.937 INFO 37469 --- [ main] org.hibernate.Version : HHH000412: Hibernate Core {5.2.17.Final}
2018-10-25 09:32:20.938 INFO 37469 --- [ main] org.hibernate.cfg.Environment : HHH000206: hibernate.properties not found
2018-10-25 09:32:21.020 INFO 37469 --- [ main] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.0.1.Final}
2018-10-25 09:32:21.160 INFO 37469 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
Hibernate: drop table t_user if exists
Hibernate: create table t_user (id bigint generated by default as identity, first_name varchar(255), last_name varchar(255), primary key (id))
2018-10-25 09:32:21.588 INFO 37469 --- [ main] o.h.t.schema.internal.SchemaCreatorImpl : HHH000476: Executing import script 'org.hibernate.tool.schema.internal.exec.ScriptSourceInputNonExistentImpl@5d97caa4'
2018-10-25 09:32:21.591 INFO 37469 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2018-10-25 09:32:22.023 INFO 37469 --- [ main] com.learn.data.JpaTests : Started JpaTests in 2.639 seconds (JVM running for 3.416)
Hibernate: insert into t_user (id, first_name, last_name) values (null, ?, ?)
2018-10-25 09:32:22.178 INFO 37469 --- [ main] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory
Hibernate: select user0_.id as id1_0_, user0_.first_name as first_na2_0_, user0_.last_name as last_nam3_0_ from t_user user0_
2018-10-25 09:32:22.276 INFO 37469 --- [ main] com.learn.data.JpaTests : result:[User(id=1, firstName=张, lastName=三)]
2018-10-25 09:32:22.281 INFO 37469 --- [ Thread-2] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@2d52216b: startup date [Thu Oct 25 09:32:19 CST 2018]; root of context hierarchy
2018-10-25 09:32:22.283 INFO 37469 --- [ Thread-2] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2018-10-25 09:32:22.283 INFO 37469 --- [ Thread-2] .SchemaDropperImpl$DelayedDropActionImpl : HHH000477: Starting delayed drop of schema as part of SessionFactory shut-down'
Hibernate: drop table t_user if exists
2018-10-25 09:32:22.287 INFO 37469 --- [ Thread-2] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2018-10-25 09:32:22.288 INFO 37469 --- [ Thread-2] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
在控制台的日志中,可以清楚的看到运行过程
-
先是启动了hikari数据源
HikariPool-1 - Starting…在不指定数据源类型的情况下,Spring Boot默认以hikari作为数据源
-
创建EntityManagerFactory
Building JPA container EntityManagerFactory for persistence unit ‘default’EntityManagerFactory是 JSR 338 JPA规范中定义的一个接口,用来创建EntityManager。这是出现的第一个Factory,在后续的源码分析中,还会看到很多Factory
-
启动Hibernate
HHH000412: Hibernate Core {5.2.17.Final}在我们的pom.xml和application.properties中都没有出现Hibernate的字样,这里为什么会出现Hibernate呢。是由于Spring Data JPA使用Hibernate作为JPA的默认实现
-
创建Entity表
Hibernate: drop table t_user if exists因为Hibernate实现了JPA规范,而@Entity正是JPA规范中的重要注解,用来标识一个domain是否为数据映射实体。Hibernate发现该注解后,就开始为该domain创建对应的表,
-
启动完成
Initialized JPA EntityManagerFactory for persistence unit ‘default’至此应用才算启动完成
-
执行Test
Hibernate: insert into t_user (id, first_name, last_name) values (null, ?, ?)先是执行了
@Before
注解的方法,创建一条User数据Hibernate: select user0_.id as id1_0_, user0_.first_name as first_na2_0_, user0_.last_name as last_nam3_0_ from t_user user0_
执行
@Test
中的findAll方法,可见findAll被翻译成了上面了select语句,这也是后续我们将深入分析的地方,Spring Data JPA的查询方式非常丰富 -
释放资源
Closing JPA EntityManagerFactory for persistence unit ‘default’关闭EntityManagerFactory
HikariPool-1 - Shutdown initiated…
关闭数据源
结束语
入门的JPA使用就结束了,其中Spring Boot、Spring Data为我们做了很多初始化的工作,后续将以该简单示例为基础,来深入分析其背后的工作原理。