如果模式与数据库结构不同,如何将 graphql 与 jpa 一起使用
How to use graphql with jpa if schema is different to database structure
有给定的数据库结构和 graphql 模式。
幸运的是,它们有很多共同点,但不幸的是,它们存在一些差异。
假设 java 中有实体匹配以下数据库结构。
SQL:
TABLE ANIMAL
+ID NUMBER(19)
+NR_OF_LEGS NUMBER(19)
TABLE SHEEP
+ID NUMBER
+LAST_TIME_SHEARED DATETIME
+ANIMAL_ID NUMBER(19)
TABLE COW
+MILK_IN_L NUMBER(3)
+ANIMAL_ID NUMER(19)
Java:
@Entity
@Table(name = "ANIMAL")
public class Animal
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column(name="nrOfLegs", nullable=false)
private long nrOfLegs;
}
@Entity
@Table(name = "SHEEP")
public class SheepE
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column(name="lastTimeSheared", nullable=false)
private Datetime lastTimeSheared;
@ManyToOne(targetEntity = AnimalE.class, cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = true)
@JoinColumn(name = "animalId", nullable = false, insertable = false, updatable = false)
private Animal animal;
}
@Entity
@Table(name = "COW")
public class CowE
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column(name="milkInL", nullable=false)
private int milkInL;
@ManyToOne(targetEntity = AnimalE.class, cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = true)
@JoinColumn(name = "animalId", nullable = false, insertable = false, updatable = false)
private Animal animal;
}
现有的 GraphQl 架构被认为是这样的:
type Sheep{
id: int!
lastTimeSheard: String!
nrOfLegs: int!
}
type Cow {
id: int!
milkInL: int!
nrOfLegs: int
}
该项目使用 graphql-java 版本 11.0(估计我们应该尽快更新)
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java</artifactId>
<version>11.0</version>
</dependency>
graphql 工作正常并且是这样实现的:
@Component
public class GraphQLProvider {
@Autowired
GraphQLDataFetchers graphQLDataFetchers;
private GraphQL graphQL;
@PostConstruct
public void init() {this.graphQL = /*init;*/null;}
private RuntimeWiring buildWiring() {
RuntimeWiring.Builder b = RuntimeWiring.newRuntimeWiring()
.type(TypeRuntimeWiring.newTypeWiring("Query")
.dataFetcher("freightCarrier", graphQLDataFetchers.getCow()))
.type(TypeRuntimeWiring.newTypeWiring("Query")
.dataFetcher("personCarrier", graphQLDataFetchers.getSheep())));
return b.build();
}
}
@Component
public class GraphQLDataFetchers {
@AutoWired
private CowRepository cowRepo;
@AutoWired
private sheepRepository sheepRepo;
public DataFetcher getCow() {
DataFetcher dataFetcher = (DataFetchingEnvironment dfe) -> {
int id = dfe.getArgument("id");
return getGraphQlCowFromCowEntity(cowRepo.getById(id));//dirty!
};
return dataFetcher;
}
public DataFetcher getCow() {
DataFetcher dataFetcher = (DataFetchingEnvironment dfe) -> {
int id = dfe.getArgument("id");
return getGraphQlSheepFromSheepEntity(cowRepo.getById(id));//dirty!
};
return dataFetcher;
}
private Cow getGraphQlCowFromCowEntity(CowE ce){//dirty!
return new Cow(ce.getId(), ce.getMilkInL(),ce.getLegs());
}
private Sheep getGraphQlSheepFromSheepEntity(SheepE se){//dirty!
return new Sheep(se.getId(), se.getLastTime(),se.getLegs());
}
public class Sheep
private long id;
private Datetime lastTimeSheared;
private int nrOfLegs;
public Sheep(long id, DateTime lasttimeSheared, int nrOfLegs){
//u know what happens here
}
}
public class Cow
private long id;
private int milkInL;
private int nrOfLegs;
public Sheep(long id, int milkInL, int nrOfLegs){
//u know what happens here
}
}
那么如何摆脱 getGraphQlCowFromCowEntity 和 getGraphQlSheepFromSheepEntity。它加倍了代码,也与 graphql 被认为是数据抽象的东西直接冲突。使用此设计,每次通过 jpa 加载所有字段,而不仅仅是请求的字段。
想象一下,这是一个具有更多字段的更复杂的环境。
无法更改 graphql 架构,因为这不是我的责任,更改整个后端以匹配架构也不是我想要存档的内容。
亲切的问候
你应该使用 DTO。检索和发送实体对象是不好的做法,因为您不希望每次重构数据库模型时或在您的情况下更改 grahql api。你的 Sheep 和 Cow 对象是 DTO,但你需要一些方法将你的实体转换为 DTO(getGraphQlCowFromCowEntity
很好,但你可以使用多态性 - CowEntity.toDTO()
- 或者让服务层进行转换,有很多方法可以做到这一点)。
为了解决您对仅加载请求的数据的担忧,您希望 DTO 对象仅填充请求的字段。一种方法是不填充所有字段,而是让 DTO 拥有对实体对象的引用,并仅在请求时从实体对象中检索数据。
public class Sheep {
private SheepE entity;
public Sheep(SheepE entity){
this.entity=entity;
}
public getId() {
return entity.getId();
}
public getLastTimeSheared() {
return entity.getLastTimeSheared();
}
...
}
请参阅我对类似问题的回答:
有给定的数据库结构和 graphql 模式。 幸运的是,它们有很多共同点,但不幸的是,它们存在一些差异。 假设 java 中有实体匹配以下数据库结构。
SQL:
TABLE ANIMAL
+ID NUMBER(19)
+NR_OF_LEGS NUMBER(19)
TABLE SHEEP
+ID NUMBER
+LAST_TIME_SHEARED DATETIME
+ANIMAL_ID NUMBER(19)
TABLE COW
+MILK_IN_L NUMBER(3)
+ANIMAL_ID NUMER(19)
Java:
@Entity
@Table(name = "ANIMAL")
public class Animal
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column(name="nrOfLegs", nullable=false)
private long nrOfLegs;
}
@Entity
@Table(name = "SHEEP")
public class SheepE
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column(name="lastTimeSheared", nullable=false)
private Datetime lastTimeSheared;
@ManyToOne(targetEntity = AnimalE.class, cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = true)
@JoinColumn(name = "animalId", nullable = false, insertable = false, updatable = false)
private Animal animal;
}
@Entity
@Table(name = "COW")
public class CowE
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column(name="milkInL", nullable=false)
private int milkInL;
@ManyToOne(targetEntity = AnimalE.class, cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = true)
@JoinColumn(name = "animalId", nullable = false, insertable = false, updatable = false)
private Animal animal;
}
现有的 GraphQl 架构被认为是这样的:
type Sheep{
id: int!
lastTimeSheard: String!
nrOfLegs: int!
}
type Cow {
id: int!
milkInL: int!
nrOfLegs: int
}
该项目使用 graphql-java 版本 11.0(估计我们应该尽快更新)
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java</artifactId>
<version>11.0</version>
</dependency>
graphql 工作正常并且是这样实现的:
@Component
public class GraphQLProvider {
@Autowired
GraphQLDataFetchers graphQLDataFetchers;
private GraphQL graphQL;
@PostConstruct
public void init() {this.graphQL = /*init;*/null;}
private RuntimeWiring buildWiring() {
RuntimeWiring.Builder b = RuntimeWiring.newRuntimeWiring()
.type(TypeRuntimeWiring.newTypeWiring("Query")
.dataFetcher("freightCarrier", graphQLDataFetchers.getCow()))
.type(TypeRuntimeWiring.newTypeWiring("Query")
.dataFetcher("personCarrier", graphQLDataFetchers.getSheep())));
return b.build();
}
}
@Component
public class GraphQLDataFetchers {
@AutoWired
private CowRepository cowRepo;
@AutoWired
private sheepRepository sheepRepo;
public DataFetcher getCow() {
DataFetcher dataFetcher = (DataFetchingEnvironment dfe) -> {
int id = dfe.getArgument("id");
return getGraphQlCowFromCowEntity(cowRepo.getById(id));//dirty!
};
return dataFetcher;
}
public DataFetcher getCow() {
DataFetcher dataFetcher = (DataFetchingEnvironment dfe) -> {
int id = dfe.getArgument("id");
return getGraphQlSheepFromSheepEntity(cowRepo.getById(id));//dirty!
};
return dataFetcher;
}
private Cow getGraphQlCowFromCowEntity(CowE ce){//dirty!
return new Cow(ce.getId(), ce.getMilkInL(),ce.getLegs());
}
private Sheep getGraphQlSheepFromSheepEntity(SheepE se){//dirty!
return new Sheep(se.getId(), se.getLastTime(),se.getLegs());
}
public class Sheep
private long id;
private Datetime lastTimeSheared;
private int nrOfLegs;
public Sheep(long id, DateTime lasttimeSheared, int nrOfLegs){
//u know what happens here
}
}
public class Cow
private long id;
private int milkInL;
private int nrOfLegs;
public Sheep(long id, int milkInL, int nrOfLegs){
//u know what happens here
}
}
那么如何摆脱 getGraphQlCowFromCowEntity 和 getGraphQlSheepFromSheepEntity。它加倍了代码,也与 graphql 被认为是数据抽象的东西直接冲突。使用此设计,每次通过 jpa 加载所有字段,而不仅仅是请求的字段。 想象一下,这是一个具有更多字段的更复杂的环境。
无法更改 graphql 架构,因为这不是我的责任,更改整个后端以匹配架构也不是我想要存档的内容。
亲切的问候
你应该使用 DTO。检索和发送实体对象是不好的做法,因为您不希望每次重构数据库模型时或在您的情况下更改 grahql api。你的 Sheep 和 Cow 对象是 DTO,但你需要一些方法将你的实体转换为 DTO(getGraphQlCowFromCowEntity
很好,但你可以使用多态性 - CowEntity.toDTO()
- 或者让服务层进行转换,有很多方法可以做到这一点)。
为了解决您对仅加载请求的数据的担忧,您希望 DTO 对象仅填充请求的字段。一种方法是不填充所有字段,而是让 DTO 拥有对实体对象的引用,并仅在请求时从实体对象中检索数据。
public class Sheep {
private SheepE entity;
public Sheep(SheepE entity){
this.entity=entity;
}
public getId() {
return entity.getId();
}
public getLastTimeSheared() {
return entity.getLastTimeSheared();
}
...
}
请参阅我对类似问题的回答: