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.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.

在控制檯的日誌中,可以清楚的看到運行過程

  1. 先是啓動了hikari數據源
    HikariPool-1 - Starting…

    在不指定數據源類型的情況下,Spring Boot默認以hikari作爲數據源

  2. 創建EntityManagerFactory
    Building JPA container EntityManagerFactory for persistence unit ‘default’

    EntityManagerFactory是 JSR 338 JPA規範中定義的一個接口,用來創建EntityManager。這是出現的第一個Factory,在後續的源碼分析中,還會看到很多Factory

  3. 啓動Hibernate
    HHH000412: Hibernate Core {5.2.17.Final}

    在我們的pom.xml和application.properties中都沒有出現Hibernate的字樣,這裏爲什麼會出現Hibernate呢。是由於Spring Data JPA使用Hibernate作爲JPA的默認實現

  4. 創建Entity表
    Hibernate: drop table t_user if exists

    因爲Hibernate實現了JPA規範,而@Entity正是JPA規範中的重要註解,用來標識一個domain是否爲數據映射實體。Hibernate發現該註解後,就開始爲該domain創建對應的表,

  5. 啓動完成
    Initialized JPA EntityManagerFactory for persistence unit ‘default’

    至此應用纔算啓動完成

  6. 執行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的查詢方式非常豐富

  7. 釋放資源
    Closing JPA EntityManagerFactory for persistence unit ‘default’

    關閉EntityManagerFactory

    HikariPool-1 - Shutdown initiated…

    關閉數據源

結束語

入門的JPA使用就結束了,其中Spring Boot、Spring Data爲我們做了很多初始化的工作,後續將以該簡單示例爲基礎,來深入分析其背後的工作原理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章