欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring Data Rest遇到复合主键

程序员文章站 2022-04-24 22:40:54
...

如果项目持久层使用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);
    }
}

现在访问 http://localhost:8080/FilmActor/1,1 就能获取film_actor表
中actor_id和film_id都是1的记录了。


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、性能问题和安全问题
……