Spring Data之JPA開篇
背景說明
目前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.hiber[email protected]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為我們做了很多初始化的工作,後續將以該簡單示例為基礎,來深入分析其背後的工作原理。