1. 程式人生 > >Spring Data之JPA開篇

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.

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

  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為我們做了很多初始化的工作,後續將以該簡單示例為基礎,來深入分析其背後的工作原理。