因為 Spring Data JPA 用來存取資料庫時很方便,然後 DynamoDB 又是屬於特別囉唆的東西,所以之前在研究 Spring Boot 時,就在想不知道 Spring 是否有支援 DynamoDB 的實作。很幸運地,社群真的有人做了 Spring Data JPA + DynamoDB [1-2] 造福大家!XD
Maven
使用 Spring Data JPA + DynamoDB 的狀況,自然需要的一定是 Spring 的環境以及橋接 DynamoDB 的實作。這裡使用的 Spring 環境是透過 Spring Boot 建立的,Maven 設定如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
< dependencyManagement > < dependencies > < dependency > < groupId >com.amazonaws</ groupId > < artifactId >aws-java-sdk-bom</ artifactId > < version >1.11.301</ version > < type >pom</ type > < scope >import</ scope > </ dependency > </ dependencies > </ dependencyManagement > < parent > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-parent</ artifactId > < version >2.0.2.RELEASE</ version > </ parent > < dependencies > <!-- Spring Boot --> < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-data-jpa</ artifactId > </ dependency > < dependency > < groupId >org.springframework</ groupId > < artifactId >spring-context-indexer</ artifactId > </ dependency > <!-- Amazon SDKs --> < dependency > < groupId >com.amazonaws</ groupId > < artifactId >aws-java-sdk-dynamodb</ artifactId > </ dependency > < dependency > < groupId >com.github.derjust</ groupId > < artifactId >spring-data-dynamodb</ artifactId > < version >5.0.2</ version > </ dependency > </ dependencies > |
上頭的第一部份(1~11 行)是指定 AWS SDK 的版本,這個版本其實是以 [1] 所使用的版本(1.11.301)為準;第二部份(13~17 行)是指定 Spring Boot 的版本,也就是後面的 dependency 中用到 Spring Boot 的時候,都會從這裡決定版號;第三部份就是真的需要的函式庫的引用了,其中 35~39 行宣告的 spring-data-dynamodb 就是本篇的重點 [1],也就是真正提供 DynamoDB 和 Spring Data JPA 串接的函式庫。
如果說想要使用的 Spring 或者 Spring Boot 版本不是 Spring Boot 2.0 以上,那麼可以參考 [1] 的版本資訊去尋找合適的對應版本。
建立 Entity
假設我在 DynamoDB 的 table 中存放的每個 record 要叫做 MyRecord,並且他是個具有複合主鍵(也就是有 Partition Key 和 Sort Key)的特性,那麼就需要按照 Spring Data JPA 本來的結構,做出一個代表主鍵的物件(這裡命名為 RecordId)。不過除此之外,還有一點特殊需要注意的地方。
首先先假設 record 的結構包含了 my_pk、my_sk、display_name 和 flag 這四個欄位,其中 my_pk、my_sk 組成複合主鍵,則 Entity 可以寫成以下的樣子:
MyRecord.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
@DynamoDBTable (tableName = "my_table" ) public class MyRecord implements Serializable { @Id private RecordId id; private String displayName; private Boolean flag; @DynamoDBHashKey (attributeName = "my_pk" ) public String getPartition () { return this .id.getPartition(); } @DynamoDBRangeKey (attributeName = "my_sk" ) public Long getEpochTime () { return this .id.getEpochTime(); } @DynamoDBAttribute (attributeName = "display_name" ) public String getDisplayName () { return this .displayName; } @DynamoDBAttribute (attributeName = "flag" ) public Boolean getFlag () { return this .flag; } public void setPartition (String val) { if ( this .id == null ) { this .id = new RecordId(); } this .id.setPartition(val); } public void setEpochTime (String val) { if ( this .id == null ) { this .id = new RecordId(); } this .id.setEpochTime(val); } public void setDisplayName (String val) { this .displayName = val; } public void setFlag (Boolean val) { this .flag = val; } } |
MyRecord.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public class RecordId implements Serializable { private String partition; private Long epochTime; @DynamoDBHashKey (attributeName = "my_pk" ) public String getPartition () { return this .getPartition(); } @DynamoDBRangeKey (attributeName = "my_sk" ) public Long getEpochTime () { return this .getEpochTime(); } public void setPartition (String val) { this .partition = val; } public void setEpochTime (Long val) { this .epochTime = val; } } |
這裡可以看到比較奇妙的地方,在 MyRecord 以及 RecordId 兩處都同時寫了 @DynamoDBHashKey 和 @DynamoDBRangeKey。雖然不太確定為什麼要這樣寫,不過實務上有一邊沒寫都會導致在存取 Repository 時丟出 Exception。
這裡順利做完以後,其實最困難(?!)的地方已經完成了 XD
Entity 注意事項
- Entity 上的所有 get method 都應該要有 @DynamoDBHashKey、@DynamoDBRangeKey 或者 @DynamoDBAttribute。
- Entity 的 get method 命名一定要以 get 開頭,不能是 is…. 或者 has…. 之類的其他動詞。
- Boolean 預設會以數字形式儲存,想要強制使用 Boolean 時,需要透過 @DynamoDBTyped 指定。
建立 Repository
接下來就是標準的 Spring Data JPA 程序了。假設要使用的是 CrudRepository,那麼 Repository 的介面就這麼寫:
1 2 |
public interface MyRepository extends CrudRepository<MyRecord, RecordId> { } |
建立 DynamoDB 設定
DynamoDB 設定主要是要提供 Spring 能夠用來存取 DynamoDB 的 Bean,因為在 DynamoDB 這個案例,建立資料庫連線的方式不同於一般 RDBMS 那樣,它不是透過 JDBC,所以需要比較不同的方式注入供 spring-data-dynamodb 存取。
1 2 3 4 5 6 7 8 |
@Configuration @EnableDynamoDBRepositories (basePackages = "com.example" ) public class DynamodbConfiguration { @Bean public AmazonDynamoDB amazonDynamoDB() { return AmazonDynamoDBClientBuilder.defaultClient(); } } |
這段主要目的就是提供初始化 AmazonDynamoDB 的方法,因為我的狀況通常都會讓程式自己去環境上取得 credential(例如由 VM 的 Role 取得權限),所以這裡單純就是產生一個預設的 client 即可。如果要產生的 client 有特殊的產生過程,也可以在這裡做修改。
設定 Spring Boot 應用程式
1 2 3 4 5 6 7 |
@ComponentScan @SpringBootApplication (exclude = {DataSourceAutoConfiguration. class }) public class Application { public static void main (String[] args) { SpringApplication.run(Application. class , ARGS); } } |
這段稍微特別一點的地方在於,@SpringBootApplication 的宣告上,同時宣告了要關閉 DataSource 的自動設定。關閉的原因是因為正常來說,Spring Data JPA 會去尋找 application.xml 設定檔,以得知如何透過 JDBC 模式取得資料庫的連線。但是 DynamoDB 有自己獨特的連線方法,因此要讓 Spring Data JPA 略過這個動作。不過如 [1] 的說明那樣,直接提供一個沒有內容的設定檔其實也是可以的。
做到這裡,其實就已經差不多了,最後剩下的就是讓 Spring 注入上面建立的 MyRepository,然後就可以開始快樂(?)地操作 DynamoDB 了。
沒有留言:
張貼留言