java 弹簧数据休息与复合主键
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/43763250/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
spring data rest with composite primary key
提问by kidfruit
I use spring data rest for crud. But when the entity has composite primary keys, I dont know how to to get an entity by giving the primary key.
我使用 spring 数据休息来处理 crud。但是当实体具有复合主键时,我不知道如何通过给出主键来获取实体。
River class:
河流类:
@Entity
public class River {
private RiverPK id;
private Double length;
private Timestamp date;
private String comment;
@Basic
@Column(name = "length")
public Double getLength() {
return length;
}
public void setLength(Double length) {
this.length = length;
}
@Basic
@Column(name = "date")
public Timestamp getDate() {
return date;
}
public void setDate(Timestamp date) {
this.date = date;
}
@Basic
@Column(name = "comment")
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
@Id
public RiverPK getId() {
return id;
}
public void setId(RiverPK id) {
this.id = id;
}
}
RiverPK class:
河流PK类:
@Embeddable
public class RiverPK implements Serializable {
private String name;
private int upcode;
private int downcode;
@Column(name = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Column(name = "upcode")
public int getUpcode() {
return upcode;
}
public void setUpcode(int upcode) {
this.upcode = upcode;
}
@Column(name = "downcode")
public int getDowncode() {
return downcode;
}
public void setDowncode(int downcode) {
this.downcode = downcode;
}
}
RiverDAO class:
RiverDAO 类:
@RepositoryRestResource(path = "river")
public interface RiverDAO extends JpaRepository<River, RiverPK> {
}
Then I can get river data by call get http://localhost:8080/river/, and also create new entity to db by call post http://localhost:8080/river/{river json}
然后我可以通过调用 get http://localhost:8080/river/获取河流数据,并通过调用 post http://localhost:8080/river/{river json}创建新的实体到数据库
river json is:
河 json 是:
id": {
"name": "1",
"upcode": 2,
"downcode": 3
},
"length": 4.4,
"date": 1493740800000,
"comment": "6"
}
In spring data rest doc, it should be able to call get localhost:8080/river/1 (the primary key) to get the entity which primary key is 1. This can work when the entity has only one primary key. But my entity river has composite primary keys as RiverPK. If I call get localhost:8080/river/{name='1',upcode=2,downcode=3}, it returns a error "No converter found capable of converting from type [java.lang.String] to type [com.example.db.entity.RiverPK]", I means spring use {name='1',upcode=2,downcode=3} as a String, but not RiverPK type.
在spring data rest doc中,应该可以调用get localhost:8080/river/1(主键)来获取主键为1的实体。当实体只有一个主键时,这可以工作。但是我的实体河流具有作为 RiverPK 的复合主键。如果我调用 get localhost:8080/river/{name='1',upcode=2,downcode=3},它会返回错误“找不到能够从 [java.lang.String] 类型转换为 [com .example.db.entity.RiverPK]",我的意思是 spring 使用 {name='1',upcode=2,downcode=3} 作为字符串,而不是 RiverPK 类型。
The question is how to call get\put\delete with composite primary keys as other normal entity?
问题是如何像其他普通实体一样使用复合主键调用 get\put\delete ?
采纳答案by kidfruit
After learn from Customizing HATEOAS link generation for entities with composite ids, I found a much more generic solution.
在从Customizing HATEOAS link generation for entity with composition ids学习之后,我找到了一个更通用的解决方案。
First, create a SpringUtil to get bean from spring.
首先,创建一个 SpringUtil 来从 spring 中获取 bean。
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class SpringUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if(SpringUtil.applicationContext == null) {
SpringUtil.applicationContext = applicationContext;
}
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public static Object getBean(String name){
return getApplicationContext().getBean(name);
}
public static <T> T getBean(Class<T> clazz){
return getApplicationContext().getBean(clazz);
}
public static <T> T getBean(String name,Class<T> clazz){
return getApplicationContext().getBean(name, clazz);
}
}
Then, implement BackendIdConverter.
然后,实现 BackendIdConverter。
import com.alibaba.fastjson.JSON;
import com.example.SpringUtil;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.webmvc.spi.BackendIdConverter;
import org.springframework.stereotype.Component;
import javax.persistence.EmbeddedId;
import javax.persistence.Id;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.net.URLEncoder;
@Component
public class CustomBackendIdConverter implements BackendIdConverter {
@Override
public boolean supports(Class<?> delimiter) {
return true;
}
@Override
public Serializable fromRequestId(String id, Class<?> entityType) {
if (id == null) {
return null;
}
//first decode url string
if (!id.contains(" ") && id.toUpperCase().contains("%7B")) {
try {
id = URLDecoder.decode(id, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
//deserialize json string to ID object
Object idObject = null;
for (Method method : entityType.getDeclaredMethods()) {
if (method.isAnnotationPresent(Id.class) || method.isAnnotationPresent(EmbeddedId.class)) {
idObject = JSON.parseObject(id, method.getGenericReturnType());
break;
}
}
//get dao class from spring
Object daoClass = null;
try {
daoClass = SpringUtil.getBean(Class.forName("com.example.db.dao." + entityType.getSimpleName() + "DAO"));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//get the entity with given primary key
JpaRepository simpleJpaRepository = (JpaRepository) daoClass;
Object entity = simpleJpaRepository.findOne((Serializable) idObject);
return (Serializable) entity;
}
@Override
public String toRequestId(Serializable id, Class<?> entityType) {
if (id == null) {
return null;
}
String jsonString = JSON.toJSONString(id);
String encodedString = "";
try {
encodedString = URLEncoder.encode(jsonString, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return encodedString;
}
}
After that. you can do what you want.
在那之后。你可以做你想做的。
There is a sample below.
下面有一个示例。
- If the entity has single property pk, you can use localhost:8080/demo/1 as normal. According to my code, suppose the pk has annotation "@Id".
- If the entity has composed pk, suppose the pk is demoId type, and has annotation "@EmbeddedId", you can use localhost:8080/demo/{demoId json} to get/put/delete. And your self link will be the same.
- 如果实体具有单一属性 pk,则可以正常使用 localhost:8080/demo/1。根据我的代码,假设 pk 有注释“@Id”。
- 如果实体已经组合了pk,假设pk是demoId类型,并且有“@EmbeddedId”注解,可以使用localhost:8080/demo/{demoId json}来get/put/delete。你的自我链接将是相同的。
回答by Mathias Dpunkt
There is a jira issue you can look at: https://jira.spring.io/browse/DATAREST-598
您可以查看一个 jira 问题:https: //jira.spring.io/browse/DATAREST-598
This comment might be especially interesting for you
这条评论可能对你特别有趣
There you find also a github linkto a sample project. It uses a BackendIdConverterto convert the composite key to string and back. So the trick is to convert your composite id to a string that can be used as the path segment.
在那里,您还可以找到一个示例项目的github 链接。它使用BackendIdConverter将复合键转换为字符串并返回。所以诀窍是将您的复合 id 转换为可用作路径段的字符串。
This answer might also be interesting for you https://stackoverflow.com/a/31830586/5371736
这个答案对你来说也可能很有趣https://stackoverflow.com/a/31830586/5371736