How to handle a post request with a ManyToOne relatshionship on an entity with an embeddedId? - spring-boot

I have a rest service with a pom.xml define like this :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
I have also a Store entity :
#Data
#Entity
#FieldDefaults(level = AccessLevel.PRIVATE)
#Table(name = "magasin")
#Inheritance(strategy = InheritanceType.JOINED)
public class Store implements Serializable {
#EmbeddedId
StoreId storeId;
#Column(name = "nom")
String name;
#MapsId("businessUnitId")
#ManyToOne
#JoinColumn(name = "bu_id", referencedColumnName = "id")
BusinessUnit businessUnit;
The Embeddable StoreId :
#Data
#FieldDefaults(level = AccessLevel.PRIVATE)
#Embeddable
public class StoreId implements Serializable {
#Column(name = "id")
Long id;
#Column(name = "bu_id")
Long businessUnitId;
With the following backendIdConverter :
#Component
public class StoreIdConverter implements BackendIdConverter {
#Override
public Serializable fromRequestId(String id, Class<?> entity) {
if(id != null) {
String[] param = id.split("_");
if (entity.getTypeName().equals(Store.class.getTypeName())) {
StoreId storeId = new StoreId();
storeId.setId(Long.valueOf(param[0]));
storeId.setBusinessUnitId(Long.valueOf(param[1]));
return storeId;
}
}
return id;
}
#Override
public String toRequestId(Serializable id, Class<?> entity) {
if(entity.getTypeName().equals(Store.class.getTypeName())){
StoreId storeId = (StoreId)id;
return String.format("%s_%s", storeId.getId(), storeId.getBusinessUnitId());
}
return id.toString();
}
#Override
public boolean supports(Class<?> entity) {
return true;
}
At this point, i can perform request with the following url without any problems :
http://localhost:9001/stores/1_1
And the resource returned :
{
"storeId" : {
"id" : 11,
"businessUnitId" : 1
},
"name" : "TEST",
"_links" : {
"self" : {
"href" : "http://localhost:9001/stores/11_1"
},
"store" : {
"href" : "http://localhost:9001/stores/11_1"
},
"businessUnit" : {
"href" : "http://localhost:9001/stores/11_1/businessUnit"
}
}
}
But when i'am including a ManyToOne relationship to the store entity into another entity like this :
#Data
#FieldDefaults(level = AccessLevel.PRIVATE)
#Entity
#Table(name = "sms")
#SequenceGenerator(name = "SmsSeq", sequenceName = "sms_seq", allocationSize = 1)
#Inheritance(strategy = InheritanceType.JOINED)
public class Sms implements Serializable {
#Id
#GeneratedValue(generator = "SmsSeq", strategy = GenerationType.SEQUENCE)
#Column(name = "id", nullable = false)
Long id;
#Column(name = "message", nullable = false)
String message;
#ManyToOne(targetEntity = Store.class)
#JoinColumns({
#JoinColumn(name = "magasin_id", referencedColumnName = "id", nullable = false),
#JoinColumn(name = "bu_id", referencedColumnName = "bu_id", nullable = false)
})
Store store;
}
A Post request
http://localhost:9001/smses
{
"message": "Bonjour",
"store": "http://10.1.3.38:9001/stores/1_1"
}
throw this kind of exception :
RepositoryRestExceptionHandler : JSON parse error: No converter found capable of converting from type [java.lang.String] to type [com.test.model.StoreId]; nested exception is com.fasterxml.jackson.databind.JsonMappingException: No converter found capable of converting from type [java.lang.String] to type [com.test.model.StoreId] (through reference chain: com.test.model.Sms["store"])
I understand that the BackendIdConverter does not parse the store associated url, is there a way to achieve the same goal on a Request body ?

Related

Quarkus Reactive with Vert.x and Hibernate Reactive / java.lang.NullPointerException: Cannot store to object array because "this.loadedState" is null

i am trying to use quarkus reactive with vert.x and hibernate reactive.
this is my pom.xml:
<quarkus-plugin.version>1.12.2.Final</quarkus-plugin.version>
and
<quarkus.platform.version>1.12.2.Final</quarkus.platform.version>
with:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-reactive-mysql-client</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-vertx-web</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-reactive</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-reactive-pg-client</artifactId>
</dependency>
this is my application.properties file:
# postgres-configuration
quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=partner_usr
quarkus.datasource.password=postgrespw
quarkus.datasource.reactive.url=vertx-reactive:postgres://localhost:3310/partnerdb
# test, but not working (schema's won't created)
quarkus.hibernate-orm.database.generation.create-schemas=true
# working (drop-and-create only on mysql, not on postgres)
quarkus.hibernate-orm.database.generation=drop-and-create
quarkus.hibernate-orm.log.sql=true
quarkus.http.cors=true
Then, i have following entities:
#Data
#MappedSuperclass
public abstract class IdEntity {
#Id
#SequenceGenerator(name = "entitySeq", sequenceName = "entitiy_id", allocationSize = 1, initialValue = 5)
#GeneratedValue(generator = "entitySeq", strategy = GenerationType.AUTO)
private Long id;
}
#Data
#Entity
#EqualsAndHashCode(callSuper = true)
public class Person extends IdEntity {
private String firstName;
private String lastName;
public Person() {
}
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Address personAddress;
}
#Data
#Entity
#EqualsAndHashCode(callSuper = true)
public class Address extends IdEntity {
private String street;
private String houseNumber;
private int postalCode;
private String city;
#OneToMany(orphanRemoval = true, mappedBy = "personAddress", fetch = FetchType.LAZY)
private List<Person> persons = new ArrayList<>();
public Address() {
}
}
Now, i am calling a reactive web-service with a reactive db access:
#Path("/person")
#ApplicationScoped
public class PersonResource {
#Inject
io.vertx.mutiny.pgclient.PgPool sqlClient;
#Inject
Mutiny.Session mutinySession;
#GET
//#Produces(MediaType.APPLICATION_JSON)
#Path("/list-persons")
#Route(path = "/list-persons", methods = HttpMethod.GET, produces = MediaType.APPLICATION_JSON)
#Transactional
public Multi<Person> listAllPersons() {
// return sqlClient.query("SELECT * FROM Person ORDER BY lastName ASC").execute()
// .onItem().transformToMulti(set -> Multi.createFrom().iterable(set))
// .onItem().transform(this::transformPersons);
return mutinySession.createQuery("SELECT f FROM Person f ORDER BY f.lastName")
.getResults().onItem().transform(this::transformObject);
}
private Person transformObject(Object f) {
return (Person)f;
}
private List<Object> transformPersons(Object f) {
final Person person = (PartnerMockEntity)f;
final List<Object> bogus = new ArrayList<>();
bogus.add(partner);
return bogus;
}
}
Exception:
Resulted in: com.fasterxml.jackson.databind.JsonMappingException: Cannot store to object array because "this.loadedState" is null (through reference chain: de.subito.model.Person["personAddress"]->de.subito.model.Address["person"])
I tried to use :
FetchType.EAGER on Address in Person
I removed the #OneToMany Relation in Address: this solves the error (yay), but the addresses won't be returned in the resulting json (id is existing, but the values are not fetched)
The questions is, how can i fetch in reactive those kind of relations without getting errors?
Or do i need a angular page in order to display this correctly?
Somehow i forgot about how fetchType.Lazy works.
Simply add a join fetch into the hql and everything works as expected.
SELECT p from Person p left join fetch p.personAddress
When using this query, there's no session/closed or any other exception thrown and the json result will be displayed as expected.
Additional note: in order to avoid recursive serialization, it is required to use the
#JsonManagedReference and #JsonBackReference
Annotations, depending on your needs to your relations.

SnippetException: Cannot document response fields as the response body is empty

Here is my Controller:
#PostMapping("post")
#PreAuthorize("hasAuthority('WRITE')")
public ResponseEntity<?> createPost(#RequestBody PostEntity postEntity) {
return new ResponseEntity<>(postService.createPost(postEntity), HttpStatus.CREATED);
}
Service :
#Override
public Post createPost(PostEntity postEntity) {
return postFactory.buildPost(postEntityRepository.save(postEntity));
}
//Post is Immutable class here
public Post buildPost(PostEntity entity) {
return new Post.Builder()
.groupId(entity.getGroupEntity().getGroupId())
.postedBy(entity.getPostedBy().getUsername())
.postType(entity.getType())
.postMedia(entity.getPostMedia())
.postId(entity.getPostId())
.build();
}
Here is my mockMvc:
#BeforeEach
public void setUp(WebApplicationContext webApplicationContext,
RestDocumentationContextProvider restDocumentation) {
this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.apply(documentationConfiguration(restDocumentation))
.alwaysDo(document("{method-name}",
preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint())))
.build();
}
Here is my Test:
this.mockMvc.perform(post("/api/post")
.contentType(MediaTypes.HAL_JSON)
.contextPath("/api")
.content(this.objectMapper.writeValueAsString(postEntity)))
.andExpect(status().isCreated())
.andDo(
document("{method-name}", preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint()),
requestFields(describeCreatePostRequest()),
responseFields(describePostEntityResult())
));
Here is Post call:
#Value.Immutable
#JsonSerialize(as = ImmutablePost.class)
#JsonDeserialize(as = ImmutablePost.class)
#JsonInclude(JsonInclude.Include.NON_NULL)
#Relation(value = "post", collectionRelation = "posts")
public interface Post {
Long getPostId();
String getPostType();
String postedBy();
#Nullable
PostMedia postMedia();
Long groupId();
class Builder extends ImmutablePost.Builder {}
}
PostEntity #Entity class here is json format:
{
"type" : "text",
"postedBy" : {
"username": "sandeep"
},
"postMedia" : {
"mediaType": "text",
"mediaUrl": "null",
"content": "Hi this is testing media content",
"createdAt": 1234,
"updatedAt": 689
},
"groupEntity": {
"groupId": 4
}
}
And Post entity class:
#Entity
#Table(name = "POST")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class PostEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "POST_ID")
private Long postId;
#Column(name = "POST_TYPE")
private String type;
#ManyToOne
#JoinColumn(name = "username", referencedColumnName = "username")
private User postedBy;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "post_media_id", referencedColumnName = "id")
private PostMedia postMedia;
#ManyToOne(cascade = CascadeType.REMOVE, fetch = FetchType.LAZY)
#JoinColumn(name = "GROUP_ID", referencedColumnName = "GROUP_ID")
private GroupEntity groupEntity;
public PostEntity() {
}
}
I have tried
objectMapper.readValue(objectMapper.writeValueAsString(postEntity), ImmutablePost.class);
As well. but still its not working, I am facing same exception:
org.springframework.restdocs.snippet.SnippetException: Cannot document response fields as the response body is empty
at org.springframework.restdocs.payload.AbstractFieldsSnippet.verifyContent(AbstractFieldsSnippet.java:191)
at org.springframework.restdocs.payload.AbstractFieldsSnippet.createModel(AbstractFieldsSnippet.java:147)
at org.springframework.restdocs.snippet.TemplatedSnippet.document(TemplatedSnippet.java:78)
at org.springframework.restdocs.generate.RestDocumentationGenerator.handle(RestDocumentationGenerator.java:191)
The problem is with the "PostMedia" setters "PostMedia" and "GroupEntity". You need to accept strings in the setters parameter and convert them to the corresponding entity. Expecting the setter's arguments as the custom entity type is making the problem.
For example:
public class MyModel {
private CustomEnum myEnum;
public CustomEnum getMyEnum() {
return myEnum;
}
public void setMyEnum(String enumName) {
this.myEnum = CustomEnum.valueOf(enumName);
}
}

Spring Boot getting empty _embedded array for related entity

Using Spring Boot I'm Having the following abbreviated structure of entities:
#Entity
#Table(name = "item")
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class Item implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", updatable = false)
protected Long id;
...
}
#Entity
#Table(name = "book")
public class Book extends Item implements Serializable {
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "item_author", joinColumns = #JoinColumn(name = "item_id", referencedColumnName = "id"), inverseJoinColumns = #JoinColumn(name = "author_id", referencedColumnName = "id"))
private Set<Author> authors;
}
#Entity
#Table(name = "author")
public class Author implements Serializable {
#Id
#GeneratedValue
private Long id;
#ManyToMany(mappedBy="authors")
private List<Book> books = new ArrayList<Book>();
private String name;
}
My DAOs are just plain simple RestResource interfaces for all entities, like:
#RestResource(path="items", rel="items")
public interface ItemDao extends CrudRepository<Item, Long> {
}
When I query an entity by id, it is all good
GET > http://localhost:8080/shelfventory/authors/1
{
"name" : "Jhonny Cash",
"used" : true,
"_links" : {
"self" : {
"href" : "http://localhost:8080/shelfventory/authors/1"
},
"author" : {
"href" : "http://localhost:8080/shelfventory/authors/1"
},
"books" : {
"href" : "http://localhost:8080/shelfventory/authors/1/books"
}
}
}
But when I try to follow the links for a related object I just get an empty embedded:
GET > http://localhost:8080/shelfventory/authors/1/books
{
"_embedded" : {
"books" : [ ]
},
"_links" : {
"self" : {
"href" : "http://localhost:8080/shelfventory/authors/1/books"
}
}
}
What am I doing wrong, how to solve it?
Consider adding these two properties to your application.properties to keep your #Entity and schema in sync:
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=true

Unable to retrieve Spring HATEOAS embedded resource object in case of #ManytoMany relationship and lookup table with extra column

I am unable to retrieve embedded .I am using Spring boot ,spring data rest and spring JPA. I have 3 tables in data base
user
competency
user_competency (join/composite table with extra column)
User
#Entity
#Table(name = "\"user\"", schema = "public")
#JsonIdentityInfo(
generator = ObjectIdGenerators.IntSequenceGenerator.class,
property = "userId")
public class User implements java.io.Serializable {
private Long userId;
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id", unique = true, nullable = false)
public Long getUserId() {
return this.userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
private Set<UserCompetency> userCompetencies = new HashSet<UserCompetency>(0);
#OneToMany(fetch = FetchType.EAGER,cascade = {CascadeType.ALL}, mappedBy = "user")
public Set<UserCompetency> getUserCompetencies() {
return this.userCompetencies;
}
public void setUserCompetencies(Set<UserCompetency> userCompetencies) {
this.userCompetencies = userCompetencies;
}
}
**Competency**
#Entity
#Table(name = "competency", schema = "public")
#JsonIdentityInfo(
generator = ObjectIdGenerators.IntSequenceGenerator.class,
property = "competencyId")
public class Competency implements java.io.Serializable {
private Long competencyId;
private Set<UserCompetency> userCompetencies = new HashSet<UserCompetency>(0);
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "competency_id", unique = true, nullable = false)
public Long getCompetencyId() {
return this.competencyId;
}
public void setCompetencyId(Long competencyId) {
this.competencyId = competencyId;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "competency")
public Set<UserCompetency> getUserCompetencies() {
return this.userCompetencies;
}
public void setUserCompetencies(Set<UserCompetency> userCompetencies) {
this.userCompetencies = userCompetencies;
}
}
UserCompetency
#Entity
#Table(name = "user_competency", schema = "public")
#JsonIdentityInfo(
generator =ObjectIdGenerators.IntSequenceGenerator.class,
property = "id")
public class UserCompetency implements java.io.Serializable {
private UserCompetencyId id;
private Level level;
private User user;
private Competency competency;
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "competencyId", column = #Column(name = "competency_id", nullable = false)),
#AttributeOverride(name = "userId", column = #Column(name = "user_id", nullable = false)) })
public UserCompetencyId getId() {
return this.id;
}
public void setId(UserCompetencyId id) {
this.id = id;
}
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "level_id")
public Level getLevel() {
return this.level;
}
public void setLevel(Level level) {
this.level = level;
}
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "user_id", nullable = false, insertable = false, updatable = false)
public User getUser() {
return this.user;
}
public void setUser(User user) {
this.user = user;
}
#ManyToOne(fetch = FetchType.EAGER,cascade=CascadeType.ALL)
#JoinColumn(name = "competency_id", nullable = false, insertable = false, updatable = false)
public Competency getCompetency() {
return this.competency;
}
public void setCompetency(Competency competency) {
this.competency = competency;
}
}
UserCompetencyId
#Embeddable
public class UserCompetencyId implements java.io.Serializable {
private Long competencyId;
private Long userId;
public UserCompetencyId() {
}
public UserCompetencyId(Long competencyId, Long userId) {
this.competencyId = competencyId;
this.userId = userId;
}
#Column(name = "competency_id", nullable = false)
public Long getCompetencyId() {
return this.competencyId;
}
public void setCompetencyId(Long competencyId) {
this.competencyId = competencyId;
}
#Column(name = "user_id", nullable = false)
public Long getUserId() {
return this.userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public boolean equals(Object other) {
if ((this == other))
return true;
if ((other == null))
return false;
if (!(other instanceof UserCompetencyId))
return false;
UserCompetencyId castOther = (UserCompetencyId) other;
return (this.getCompetencyId() == castOther.getCompetencyId()) && (this.getUserId() == castOther.getUserId());
}
}
UserCompetencyRepository
public interface UserCompetencyRepository extends JpaRepository<UserCompetency, UserCompetencyId> {
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>Demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>Demo</name>
<description>Demo api </description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-rest-hal-browser</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-rest-webmvc</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
and I want to perform GET using URI,it return me embedded object and cannot get real value of objects attributes
GET http://localhost:8080/userCompetencies
How Can I get attribute values of User and Competency object where userId=8 Help is required
After implementing suggested Projection Issue still not resolved and here is screen shot
One way would be to use projections like for example:
#Projection(name = "edit" , types = Employee.class)
public interface EditEmployeeProjection {
String getFirstName();
String getLastName();
Set<Project> getProjects();
}
With this the project list will be embedded in the result for http://localhost:8080/api/employee/1?projection=edit
Projections would be used automatically if you add excerptProjection to you repository like described here: How to expose a complete tree structure with Spring Data REST and HATEOAS?
See for example here: https://shinesolutions.com/2015/04/15/spring-data-rest-and-projections/
EDITED
In you case a projection would look like:
#Projection(name = "edit" , types = UserCompetency.class)
public interface UserCompetencyProjection {
User getUser();
Competency getCompetency();
}
With http://localhost:8080/userCompetencies?projection=edit you would then see wanted result.
EDITED 2
The code I used:
Competency.class
#Entity
#Table(name = "competency", schema = "public")
#JsonIdentityInfo(
generator = ObjectIdGenerators.IntSequenceGenerator.class,
property = "competencyId")
public class Competency implements java.io.Serializable {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "competency_id", unique = true, nullable = false)
private Long competencyId;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "competency")
private List<UserCompetency> userCompetencies = new ArrayList<>();
UserCompetency.class
#Entity
#Table(name = "user_competency", schema = "public")
#JsonIdentityInfo(
generator = ObjectIdGenerators.IntSequenceGenerator.class,
property = "id")
public class UserCompetency implements java.io.Serializable {
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "competencyId", column = #Column(name = "competency_id", nullable = false)),
#AttributeOverride(name = "userId", column = #Column(name = "user_id", nullable = false)) })
private UserCompetencyId id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "user_id", nullable = false, insertable = false, updatable = false)
private User user;
#ManyToOne(fetch = FetchType.EAGER,cascade = CascadeType.ALL)
#JoinColumn(name = "competency_id", nullable = false, insertable = false, updatable = false)
private Competency competency;
UserCompetencyId.class
#Embeddable
public class UserCompetencyId implements java.io.Serializable {
#Column(name = "competency_id", nullable = false)
private Long competencyId;
#Column(name = "user_id", nullable = false)
private Long userId;
UserCompetencyRepository.class
#RepositoryRestResource(excerptProjection = UserCompetencyProjection.class)
public interface UserCompetencyRepository extends JpaRepository<UserCompetency, UserCompetencyId> {
After Implementing This ,In my case its not working to show desired jason [![enter image description here][2]][2]
It worked out ,actually i was missing annotation of #RepositoryRestResource(excerptProjection = UserCompetencyProjection.class)
on UserCompetencyRepository class now the output look like this I am skipping as it is output , and putting necessary output.

Spring Data Rest with Jpa relations

Followed this question but did not work
Have two entities Account and UserTransaction
Account.java
#Entity
#Access(AccessType.FIELD)
public class Account {
#Id
private Integer accountNumber;
private String holderName;
private String mobileNumber;
private Double balanceInformation;
public Account(Integer accountNumber, String holderName, String mobileNumber, Double balanceInformation) {
this.accountNumber = accountNumber;
this.holderName = holderName;
this.mobileNumber = mobileNumber;
this.balanceInformation = balanceInformation;
}
}
UserTransaction.java
#Entity
#Access(AccessType.FIELD)
#Table(name = "user_transaction")
public class Transaction {
#Id
private Long transactionId;
#ManyToOne
#JoinColumn(name = "accountNumber")
private Account accountNumber;
private Double transactionAmount;
#Column(nullable = false, columnDefinition = "TINYINT", length = 1)
private Boolean transactionStatus;
private String statusMessage;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="timestamp", columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
private Date timestamp;
public Transaction(Long transactionId, Account account,
Double transactionAmount,
Boolean transactionStatus,
String statusMessage) {
this.transactionId = transactionId;
this.accountNumber = account;
this.transactionAmount = transactionAmount;
this.transactionStatus = transactionStatus;
this.statusMessage = statusMessage;
}
}
and My TransactionRepository is as follows
#RepositoryRestResource(collectionResourceRel = "transactions", path = "transactions")
public interface JpaTransactionRepository extends JpaRepository<Transaction, Long>, TransactionRepository {
#Query(value = "select t from Transaction t where t.accountNumber.accountNumber = :accountNumber")
Iterable<Transaction> findByAccountNumber(#Param("accountNumber") Integer accountNumber);
}
I have constructed a json as specified in the stackoverflow post at the top
{
"transactionId" : "3213435454342",
"transactionAmount" : 5.99,
"transactionStatus" : true,
"statusMessage" : null,
"timestamp" : "2017-03-09T05:11:41.000+0000",
"accountNumber" : "http://localhost:8080/accounts/90188977"
}
when I try to execute POST with the above json I get
Caused by: java.sql.SQLIntegrityConstraintViolationException: Column 'account_number' cannot be null
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:533)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:513)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:115)
at com.mysql.cj.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:1983)
How do I save an entity that has relationships with Spring data rest????
The problem is that with #JoinColumn(name = "accountNumber") you would hard-code the column name in database as accountNumber. Normally the naming-strategy would add embedded underscores instead of having mixed case column names.
So it should work if you change the line to #JoinColumn(name = "account_number").

Resources