最近天氣好熱,做甚麼事都覺得很懶,想要寫個spring data jpa也是懶懶的,不過這部分卻也是滿重要的一部分,前一篇介紹JDBCTemplate,已經覺得跟以前寫SQL方式有所差異了,JPA帶來的是物件導向的設計面思考,說到JPA不得不提提ORM,Object-relational mapping主要想法為簡化及物件導向的設計,讓RDB更貼近Object,在設計上可以更加便利,甚至透過一些設計可以讓Table具有物件導向的特性如繼承等等,以往要使用ORM的框架,都會先以Hibernate進行,不過近來慢慢地轉向JPA,主要還是在減少程式碼、增加彈性等等,大體的功能沒有差異很大,所以從Hibernate轉到JPA問題不大,JPA要介紹的東西還滿多的,所以我這裡會再分成三個章節來介紹。
pom.xml 說明
Github code(louisz.springboot.example6)
參考連結:
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods
http://blog.didispace.com/springbootdata2/
https://projectlombok.org/
- SPRING DATA JPA基本操作
- JPQL & Named SQL & Native SQL
- Cache & DB Design Pattern
學習目的:SPRING DATA JPA基本操作。
學習時數:3.5hr
教學影片:
pom.xml 說明
spring-boot-starter-web:配置 Web Project所需的函式庫。 spring-boot-starter-test:配置 unit or mock test 所需的函式庫。 spring-boot-starter-actuator:配置監控spring boot所需的函式庫,後續spring cloud會使用到,所以一開就導入。 spring-boot-starter-jdbc:配置使用jdbc所需的函式庫。 postgresql:配置postgresql連接Driver所需的函式庫。 jasypt-spring-boot-starter:加解密所需的函式庫。 spring-boot-starter-data-jpa:配置Spring data jpa所需的函式庫。 lombok:本次的彩蛋,降低getter與setter的套件。有使用到上一篇的加解密
程式碼說明
- Example6.java
package louisz.springboot.example6;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* Louisz Spring boot introduce ex.6
*
*/
@SpringBootApplication
@PropertySource(name = "EncryptedProperties", value = "classpath:example5.properties")//針對需加解密的設定檔檔名及位置
@EntityScan(basePackages = {"louisz.springboot.example6"})//載入指定package的entity
@EnableJpaRepositories(basePackages = {"louisz.springboot.example6"})//載入指定package jpaRepositories
@EnableTransactionManagement//啟動交易模式
public class Example6 {
/**
* 程式執行起點
*
* @param args
*/
public static void main(String[] args) {
SpringApplication.run(Example6.class, args);
}
}
- Member.java
package louisz.springboot.example6; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; import lombok.Getter; import lombok.Setter; @Entity//Entity宣告 @Table(name = "member")//指定對應Table,請記得一定要有ID欄位(名稱可以改變)作為PK喔 public class Member { @Id //PK @GeneratedValue //之後針對SEQUENCE會需要 @Setter @Getter //lombok 減少setter及getter method撰寫 private Long id; @Column(nullable = false)//不可為空值 @Setter @Getter private String name; @Column(nullable = false) @Setter @Getter private Integer age; }
- MemberRepository.java
package louisz.springboot.example6;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface MemberRepository extends JpaRepository{
/**
* 使用ID查詢資料,並回傳Member Entity
* @param id
* @return
*/
Member findById(Long id);
/**
* 使用ID查詢資料,並回傳Member Entity
* @param name
* @return
*/
Member findByName(String name);
/**
* 透過JPQL,使用NAME查詢,並回傳Member Entity
* @param name
* @return
*/
@Query("from Member u where u.name=:name")
Member findMember(@Param("name") String name);
/**
* 使用NAME及AGE查詢,並回傳Member Entity
* @param name
* @param age
* @return
*/
Member findByNameAndAge(String name, Integer age);
/**
* 使用NAME進行Count,並回傳筆數
* @param name
* @return
*/
Long countByName(String name);
//不分姓名大小寫的查詢
/**
* 使用NAME進行查詢且不分大小寫,並回傳List
* @param name
* @return
*/
List findByNameIgnoreCase(String name);
}
- TestMemberRepository.java
package louisz.springboot.example6;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.context.junit4.SpringRunner;
//spring boot 1.4以後的方式
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class TestMemberRepository {
@Autowired
private MemberRepository memberRepository;
@Before
public void setUp() {
// 先清除所有資料
memberRepository.deleteAll();
}
@Test
public void test() throws Exception {
/*
* 進行單元測試,先規劃相關情境
*/
// 先確認是否清除完成
Assert.assertEquals(0, memberRepository.findAll().size());
// 新增一筆資料
Member m = new Member();
m.setId(1l);
m.setAge(new Integer(12));
m.setName("louisz");
memberRepository.save(m);
// 確認新增資料筆數
Assert.assertEquals(1, memberRepository.findAll().size());
// 檢查新增的資料是否正確
Assert.assertEquals(12, memberRepository.findByName("louisz").getAge().longValue());
// 修改姓名
m.setName("jessie");
memberRepository.save(m);
// 檢查新增的資料是否正確
Assert.assertEquals("jessie", memberRepository.findById(1l).getName());
// 刪除新增的資料
memberRepository.delete(memberRepository.findById(1l));
// 確認刪除資料筆數
Assert.assertEquals(0, memberRepository.findAll().size());
// 再度儲存
memberRepository.save(m);
// 查詢姓名為jessie的筆數
Assert.assertEquals(1, memberRepository.countByName("jessie").longValue());
// 不分大小寫查詢姓名
Assert.assertEquals(0, memberRepository.countByName("JESSIE").longValue());
Assert.assertEquals(1, memberRepository.findByNameIgnoreCase("JESSIE").size());
}
}
單元測試看結果,執行時請加上-Djasypt.encryptor.password=supersecretzGithub code(louisz.springboot.example6)
參考連結:
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods
http://blog.didispace.com/springbootdata2/
https://projectlombok.org/
留言
張貼留言