1. 程式人生 > >Spring Data Rest遇到複合主鍵

Spring Data Rest遇到複合主鍵

如果專案持久層使用Spring Data JPA且某些資料表中含有複合主鍵(聯合主鍵),使用Spring Data Rest生成的介面如何訪問這些資料表中的某個主鍵對應的資料呢?

假設資料庫有2個有複合主鍵的資料表film_actor和film_category,對應以下4個類。

import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
import java.sql.Timestamp;

@Entity
@Table(name = "film_actor")
public class
FilmActor implements Serializable {
@EmbeddedId @AttributeOverrides({ @AttributeOverride(name = "actorId", column = @Column(name = "actor_id", nullable = false)), @AttributeOverride(name = "filmId", column = @Column(name = "film_id", nullable = false))}) private
FilmActorId id; @Temporal(TemporalType.TIMESTAMP) @Column(name = "last_update", nullable = false, length = 19) private Date lastUpdate; //constructor、getter/setter } @Embeddable public class FilmActorId implements java.io.Serializable { @Column(name = "actor_id", nullable = false
) private short actorId; @Column(name = "film_id", nullable = false) private short filmId; //constructor、getter/setter、equals、hashCode } @Entity @Table(name = "film_category") @IdClass(FilmCategoryPK.class) public class FilmCategory { @Id @Column(name = "film_id", nullable = false) private short filmId; @Id @Column(name = "category_id", nullable = false) private byte categoryId; @Basic @Column(name = "last_update", nullable = false) private Timestamp lastUpdate; //constructor、getter/setter、equals、hashCode } public class FilmCategoryPK implements Serializable { @Column(name = "film_id", nullable = false) @Id private short filmId; @Column(name = "category_id", nullable = false) @Id private byte categoryId; //constructor、getter/setter、equals、hashCode }

以上程式碼代表了ORM的兩種方式。
那如何通過Spring Data Rest生成的介面獲取film_actor表中ID都是1的記錄呢?

那就涉及如何將請求URL轉化為實體類ID了。需要新增如下配置

import org.springframework.data.rest.webmvc.spi.BackendIdConverter;
import org.springframework.stereotype.Component;
import java.io.Serializable;
import java.util.Arrays;

@Component
public class IdConverter implements BackendIdConverter {

    private ThreadLocal<String> threadLocal = new ThreadLocal<>();

    @Override
    public Serializable fromRequestId(String s, Class<?> clazz) {
        System.out.println(String.format("fromRequestId(%s,%s)", s, clazz));
        threadLocal.set(s);

        String[] ss = s.split(",");
        long p1 = Long.parseLong(ss[0]);
        long p2 = Long.parseLong(ss[1]);

        if (clazz == FilmActor.class)
            return new FilmActorId(((short) p1), ((short) p2));

        if (clazz == FilmCategory.class)
            return new FilmCategoryPK(((short) p1), ((byte) p2));

        return null;
    }

    @Override
    public String toRequestId(Serializable serializable, Class<?> clazz) {
        System.out.println(String.format("toRequestId(%s,%s)", serializable, clazz));
        System.out.println(serializable.getClass());
        return threadLocal.get();
    }

    @Override
    public boolean supports(Class<?> clazz) {
        System.out.println(String.format("supports(%s)", clazz));
        return Arrays.asList(FilmActor.class, FilmCategory.class).contains(clazz);
    }
}

Spring data Rest生成的介面預設不輸出ID,如果需要輸出ID,可以配置一個Bean。如下是FilmActor、 FilmCategory都暴露ID的配置。

import org.springframework.context.annotation.Bean;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer;
import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurerAdapter;

@Bean
public RepositoryRestConfigurer repositoryRestConfigurer() {
    return new RepositoryRestConfigurerAdapter() {
        @Override
        public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
            config.exposeIdsFor(FilmActor.class, FilmCategory.class);
        }
    };
}

附:
對Spring Data Rest的評價:
優點:用極少的程式碼就能實現資料庫常用CRUD操作向RESTful API的轉化
缺點:
1、springfox swagger還不能探測到生成的介面
2、與Spring Data JPA的結合還有很多問題
3、生成的介面難以應對複雜的業務場景
4、效能問題和安全問題
……