Spring Data REST javax.persistence.RollbackException: Error while committing the transaction - spring

I have tow Entities:
#Data
#Entity(name = "users")
#NoArgsConstructor
#RequiredArgsConstructor
#EqualsAndHashCode
public User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="id", unique = true, nullable = false)
private Integer userId;
private String userName;
#OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
#JoinColumn(name = "user_id")
#Fetch(value = FetchMode.SUBSELECT)
private List<Degree> degreeList;
}
and
#Data
#Entity(name = "degrees")
#NoArgsConstructor
#RequiredArgsConstructor
#EqualsAndHashCode
public Degree {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="id", unique = true, nullable = false)
private Integer degreeId;
private String degreeTitle;
}
RestResources are:
#RepositoryRestResource(collectionResourceRel = "users", path = "users")
public interface UserRestRepository extends CrudRepository<User, Integer> {
}
and then:
#RepositoryRestResource(collectionResourceRel = "degrees", path = "degrees")
public interface DegreeRestRepository extends CrudRepository<Degree, Integer> {
}
I have to add new bar in barList of foo using https://www.myhost.com/api/users/{userId}/degreeList endpoint as shown in image:
but I am getting:
{
"timestamp": "2020-04-17T05:16:51.520+0000",
"status": 500,
"error": "Internal Server Error",
"message": "Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction",
"path": "/api/users/4/degreeList"
}
I have searched and found that I have to save Degree then update degreeList, how can I do that please guide

Spring Data REST is URI-based (following the HATEOAS rules). If you want to add "degree" to "degreeList", indeed you first need to create the degree:
HTTP POST to https://www.myhost.com/api/degrees
The request body has to be of content type application/json
After that, you get back the Degree ID in the response. You use this ID to add the Degree to the list:
HTTP POST to https://www.myhost.com/api/users/{userId}/degreeList
Important: this time the request content type is text/uri-list! The body must be plain text and should just contain the URI
https://www.myhost.com/api/degrees/{degreeId}

Related

Bidirectional ManyToOne/OneToMany

I think i miss misunderstood how bidirectional relation works in JPA.
Club Enitity:
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
#Entity
#Table(name = "club")
public class Club {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Long id;
#Column(name = "club_name")
private String clubName;
#OneToMany(mappedBy = "club", orphanRemoval = true)
private List<Player> players = new ArrayList<>();
}
Player Entity:
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
#Entity
#Table(name = "player")
public class Player {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Long id;
#Column(name = "player_name")
private String playerName;
#ManyToOne(optional = false)
#JoinColumn(name = "club_id", nullable = false)
private Club club;
}
One club may have several players, several players can be in one club. Next i create repositiories for for these entities
public interface ClubRepository extends JpaRepository<Club, Long> {}
public interface PlayerRepository extends JpaRepository<Player, Long> { }
and controlers.
#RestController
#RequestMapping("/api/club")
public class ClubController {
#Autowired
ClubRepository clubRepository;
#GetMapping
public List<Club> getALlClubs(){
return clubRepository.findAll();
}
}
#RestController
#RequestMapping("/api/player")
public class PlayerController {
#Autowired
PlayerRepository playerRepository;
#GetMapping
public List<Player> getALlPlayers(){
return playerRepository.findAll();
}
}
I added some data to these tables, but when i call api i have error:
ERROR 11128 --- [nio-8080-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError);
I want to my data in endpoint looks like:
[
"id": 1
"clubName": "madrid"
"players": {
"id": 1
"player_name": "kaka"
}
]
Or something like that, i just want to be able to easily refer to particular data on the frontend side. How should I do it correctly?

Why does DataJpaTest fail when saving OneToMany-related data in this pattern?

I have three Hibernate #Entity's below that mimic a failure in my production app:
#Entity
#Data
#SuperBuilder(toBuilder = true)
#EqualsAndHashCode(callSuper = true)
#NoArgsConstructor
#AllArgsConstructor
public class Dog extends Animal {
String barkType;
}
The Dog entity uses JOINED inheritance with this class, Animal:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#Data
#SuperBuilder(toBuilder = true)
#NoArgsConstructor
#AllArgsConstructor
public class Animal {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Type(type = "uuid-char")
private UUID id;
#OneToMany(cascade = CascadeType.REMOVE)
#JoinColumn(name = "animalId", referencedColumnName = "id", insertable = false, updatable = false)
#Builder.Default
private List<Toy> toys = new ArrayList<>();
}
This Toy Entity is related to the parent class, Animal
#Entity
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class Toy {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Type(type = "uuid-char")
private UUID id;
#Type(type = "uuid-char")
private UUID animalId;
private String shape;
}
And here is my implementation I am testing:
#Service
#AllArgsConstructor
public class DogService {
DogRepository repository;
ToyRepository toyRepository;
#Transactional
public Dog saveDogDTO(DogDTO dogDTO) {
Dog entity = Dog.builder()
.barkType(dogDTO.getBarkType())
.build();
repository.save(entity);
toyRepository.save(Toy.builder()
.shape(dogDTO.getToyShape())
.animalId(entity.getId())
.build());
return entity;
}
}
Here is my failing Test, which fails on the LAST line:
#DataJpaTest
class DogServiceTests {
private DogService dogService;
#Autowired
private DogRepository dogRepository;
#Autowired
private ToyRepository toyRepository;
#Test
void save_not_working_example() {
dogService = new DogService(dogRepository, toyRepository);
var dogDTO = DogDTO.builder()
.barkType("big bark")
.toyShape("some shape")
.build();
var savedDog = dogService.saveDogDTO(dogDTO);
assertThat(dogRepository.count()).isEqualTo(1);
assertThat(toyRepository.count()).isEqualTo(1);
var findByIdResult = dogRepository.findById(savedDog.getId());
assertThat(findByIdResult.get().getToys()).hasSize(1);
}
}
The test failure message:
Expected size: 1 but was: 0 in:
[]
java.lang.AssertionError:
Expected size: 1 but was: 0 in:
[]
The issue seems to be that the double JPA repository save clashes within the #Transaction. Is there a way to overcome this issue? I tried adding #Transactional(propagation = Propagation.NEVER) to the test, but then I get this failure:
failed to lazily initialize a collection of role: com.example.datajpatest.demo.models.Animal.toys, could not initialize proxy - no Session
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.example.datajpatest.demo.models.Animal.toys, could not initialize proxy - no Session
#DataJpaTest is annotated #Transactional so your test method is all wrapped in a single transaction, and hence a single EntityManager. You could make your test pass by calling EntityManager.detach() on the savedDog before querying using findById(). You could also fix it by manually setting up the dog's toys in the DogService. That would be my recommendation because otherwise sooner or later you might find the same inconsistency bug in production code - the transaction boundaries just have to shift a bit and that would be quite hard to spot. In a way #DataJpaTest has done you a favour by pointing out the problem, albeit somewhat indirectly.
Ultimately, the database state doesn't match the state of the EntityManager cache, so you have to clear the cache to get the result you want. Starting a new transaction would clear the cache too, and that's what is probably happening in production. Hibernate trusts you to make the object graph match the database state when you save (or flush). If they don't match then Hibernate has no way of knowing without querying the database, which it would regard as redundant and inefficient.
Try this mapping here instead:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#Data
#SuperBuilder(toBuilder = true)
#NoArgsConstructor
#AllArgsConstructor
public class Animal {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Type(type = "uuid-char")
private UUID id;
#OneToMany(mappedBy = "animal", cascade = CascadeType.REMOVE)
#Builder.Default
private List<Toy> toys = new ArrayList<>();
}
#Entity
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class Toy {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Type(type = "uuid-char")
private UUID id;
#ManyToOne(fetch = LAZY)
#JoinColumn(name = "animalId")
private Animal animal;
private String shape;
}

Spring Boot, Hibernate, bidirectional One-To-Many. Strange behaviour. Why is there two selects insdead of an error?

Spring Boot, Hibernate, bidirectional One-To-Many. Strange behavior. Why is there two selects instead of an error?
I have a basic Spring boot application.
It simulates throwing dices.
I have two entity classes Dice and DiceBatch.
DiceBatch has List<Dice> dices;
Dice has DiceBatch diceBatch; as two sides of bidirectional ManyToOne, or OneToMany.
I use JpaRepository<DiceBatch, UUID> to get one instance of DiceBatch by callig a method of JpaRepository findById(UUID id)
I call this method inside DiceBatchService's method findDiceBatchById(UUID diceBatchId).
Method is marked as #Transactional.
When i do that Hibernate logs one SQL select:
/* select
d
from
DiceBatch d
where
d.id = ?1 */ select
dicebatch0_.dice_batch_id as dice_bat1_1_,
dicebatch0_.batch_creation_time as batch_cr2_1_,
dicebatch0_.batch_name as batch_na3_1_
from
dice_batch dicebatch0_
where
dicebatch0_.dice_batch_id=?
At this point everything is ok.
Method returns DiceBatch entity with lazily initialized List<Dice> dices.
This is important. Method is #Transactional when method returns I should leave transactionla context.
Lazy fields should stay lazy and should cause LazyInitializationException if I try to access them.
Now control goes back to the controller method of DiceBatchController findDiceBatchById(UUID diceBatchId)
And here something strange happens.
Hibernate logs another select
select
dices0_.dice_batch_id as dice_bat5_0_0_,
dices0_.dice_id as dice_id1_0_0_,
dices0_.dice_id as dice_id1_0_1_,
dices0_.dice_batch_id as dice_bat5_0_1_,
dices0_.sequential_number as sequenti2_0_1_,
dices0_.throw_result as throw_re3_0_1_,
dices0_.throw_time as throw_ti4_0_1_
from
dice dices0_
where
dices0_.dice_batch_id=?
...and response JSON contains DiceBatch with all Dice entities related to it.
So I have several question.
Why didn't I get LazyInitializationException?
How come the List<Dice> inside DiceBatch got initialized outside of Transactional context?
How Spring managed to build a complete entity of DiceBatch, including the content of the List<Dice> without any exceptions?
How to modify my code to avoid this strange implicit bahavior?
Here is all the relevant code.
package org.dice.model;
#Entity
#Builder
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class Dice {
#Id
#GenericGenerator(name = "UUID",
strategy = "org.hibernate.id.UUIDGenerator")
#GeneratedValue(strategy = javax.persistence.GenerationType.AUTO,
generator = "UUID")
#Column(name = "dice_id",
nullable = false)
private UUID id;
#Column(name = "throw_result",
nullable = false)
private Integer throwResult;
#Column(name = "throw_time",
nullable = false)
private LocalDateTime throwTime;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "dice_batch_id",
nullable = false,
foreignKey = #ForeignKey(name = "fk_dice_dice_batch_id_dice_batch_dice_batch_id")
)
#JsonBackReference
private DiceBatch diceBatch;
#Embedded
private SequentialNumber sequentialNumber;
}
package org.dice.model;
#Entity
#Builder
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class DiceBatch {
#Id
#GenericGenerator(name = "UUID",
strategy = "org.hibernate.id.UUIDGenerator")
#GeneratedValue(strategy = GenerationType.AUTO,
generator = "UUID")
#Column(name = "dice_batch_id",
nullable = false)
private UUID id;
#Column(name = "batch_name",
nullable = false)
private String batchName;
#Column(name = "batch_creation_time",
nullable = false)
private LocalDateTime batchCreationTime;
#OneToMany(
mappedBy = "diceBatch",
cascade = CascadeType.ALL,
orphanRemoval = true,
fetch = FetchType.LAZY)
#JsonManagedReference
private List<Dice> dices = new ArrayList<>();
public void addDice(Dice dice) {
dices.add(dice);
dice.setDiceBatch(this);
}
public void removeDice(Dice dice) {
dices.remove(dice);
dice.setDiceBatch(null);
}
}
package org.dice.repo;
#Repository
public interface DiceBatchRepo extends JpaRepository<DiceBatch, UUID> {}
package org.dice.service;
#Service
#RequiredArgsConstructor
public class DiceBatchService {
#Transactional
public DiceBatch findDiceBatchById(UUID diceBatchId) {
DiceBatch diceBatch = diceBatchRepo
.findById_my(diceBatchId)
.orElseThrow();
return diceBatch;
}
}
package org.dice.controller;
public class DiceBatchController {
#GetMapping(path = "/get/{diceBatchId}")
public ResponseEntity<DiceBatch> findDiceBatchById(
#PathVariable(name = "diceBatchId") UUID diceBatchId) {
log.info("<C>[/batch/get] endpoint reached.\n" +
"Dice Batch Id: {}\n",
diceBatchId);
return ResponseEntity.ok(diceBatchService.findDiceBatchById(diceBatchId));
}
}

Spring JPA Unable To Find Composite Foreign Key Target Column (Non-PK)

User.java
#Entity
#Table(name = "users")
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_role_id", referencedColumnName = "id")
private UserRole userRole;
}
UserRole.java
#Data
#Entity
#Table(name = "user_roles")
public class UserRole implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
}
Client.java
#Data
#Entity
#Table(name = "clients")
public class Client implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumns({ #JoinColumn(name = "user_id", referencedColumnName = "id"),
#JoinColumn(name = "user_role_id", referencedColumnName = "user_role_id") })
private User user;
}
Error
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.MappingException: Unable to find column with logical name: user_role_id in users
In RDBMS, users.(id, user_role_id) is unique so clients table can refer to that.
Last time, I was using insertable = false, updatable = false on user_role_id, but when I want to add records of new client, I always need to add user_role_id manually user.setUserRoleId(userRole.getId()) after user.setUserRole(userRole) and I think that is bad practice of ORM (it should be added automatically when I set user.setUserRole(userRole))
#Column(name = "user_role_id", insertable = false, updatable = false)
private Integer userRoleId;
What should I do so the relation can be mapped in Spring JPA? and what is the best practice?
In other words, this is also mean how to reference to foreign key generated logical name column?
OK! Please try following configuration:
Below is a important code part and under this link you may find repository with working example
UserRole.java
#Data
#Entity
#Table(name = "user_roles")
public class UserRole implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "role_id")
private Integer roleId;
}
User.java
#Data
#Entity
#Table(name = "users")
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private Integer userId;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "user_role_id", referencedColumnName = "role_id")
private UserRole userRole;
}
Client.java
#Data
#Entity
#Table(name = "clients")
public class Client implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "client_id")
private Integer clientId;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumns(
value = {
#JoinColumn(name = "client_role_id", referencedColumnName = "user_role_id"),
#JoinColumn(name = "client_user_id", referencedColumnName = "user_id"),
}
,
foreignKey = #ForeignKey(
name = "FK_user_with_role",
foreignKeyDefinition = "FOREIGN KEY (client_user_id, client_role_id)\n" +
" REFERENCES users \n" +
" (user_id, user_role_id) \n" +
" ON UPDATE CASCADE\n" +
" ON DELETE CASCADE")
)
private User user;
}
Please note that beside adding a foreignKey in the Client implementation, you MUST keep the sequence of #JoinColum annotations.. I don't know what is the reason behind, but if you flip those lines you'll still get your error as it was before :)
EDIT: I've added another answer which fits best in my opinion. I'm leaving this one as well to see the other steps I tried.
Though the solution is not elegant and not using JPA as requested. Just in case anything in here would be helpful
If I understand the main issue correctly - you want to bind Client entity with Role entity via User entity, by first setting User's Role and then transfer that "property" by using only UserId instead setting additionally RoleId while creating Client.
Basically after playing for a while with your model I think the main issue is to assign data to each other within a #Transactional methods. That seems to be caused ba Lazy fetch strategy.
My proposal for solution that binds all your Entities according expectations differs only from yours with ommiting the RoleId JoinColumn in Clients table. I have checked that when calling a service that would have #Transactional methods, you can assign a Role to the User and User to the Client with simple user.setRole(roleEntity) followed by client.setUser(userEntity).
All the data is then consistent. No need to call further like getters and setters as you mentioned in the second part of your question. Question is if for any reason you need to have RoleId as well in your Clients Table, then this soultion would have to be enhanced by additional column?
UserRole.java
#Entity
#Table(name = "user_roles")
public class UserRole implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "role_id")
private Integer roleId;
//getters and setters and toString
}
User.java
#Entity
#Table(name = "users")
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private Integer userId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_role_id", referencedColumnName = "role_id")
private UserRole userRole;;
//getters and setters and toString;
}
Client.java
#Entity
#Table(name = "clients")
public class Client implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "client_id")
private Integer clientId;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "client_user_id", referencedColumnName = "user_id"),
})
private User user;
#Column(name = "client_role_id")
private Integer roleId;
#PrePersist
#PreUpdate
private void prePersist(){
try {
roleId = getUser().getUserRole().getRoleId();
} catch (NullPointerException e){
roleId = null;
}
}
//getters and setters and toString
}
UserService.java
#Service
public class UserService {
UserRepo userRepo;
public UserService(UserRepo userRepo) {
this.userRepo = userRepo;
}
#Transactional
public void save(User user) {
userRepo.save(user);
}
#Transactional
public User getReferenceById(int i) {
return userRepo.getReferenceById(i);
}
}
ClientService.java
#Service
public class ClientService {
private ClientRepo clientRepo;
private UserService userService;
public ClientService(ClientRepo clientRepo, UserService userService) {
this.clientRepo = clientRepo;
this.userService = userService;
}
#Transactional
public Client save(Client client){
return clientRepo.save(client);
}
#Transactional
public Client getReferenceById(int i) {
return clientRepo.getReferenceById(i);
}
#Transactional
public void printClient(Client client){
client = clientRepo.getReferenceById(client.getClientId());
System.out.println(client);
}
#Transactional
public void bindUserToClient(int userId, int clientId) {
Client entity = clientRepo.findById(clientId).orElseGet(Client::new);
entity.setUser(userService.getReferenceById(userId));
}
#Transactional
public void printClient(int i) {
clientRepo.findById(i).ifPresentOrElse(this::printClient, EntityNotFoundException::new);
}
}
This configuration after running this commandLineRunner:
#Configuration
public class Config {
#Bean
#Transactional
public CommandLineRunner commandLineRunner(
#Autowired UserRoleRepo roleRepo,
#Autowired UserService userService,
#Autowired ClientService clientService
) {
return args -> {
for (int i = 0; i < 5; i++) {
roleRepo.save(new UserRole());
}
for (int i = 5; i > 0; i--) {
User user = new User();
user.setUserRole(roleRepo.getReferenceById(i));
userService.save(user);
}
Client client = new Client();
client.setUser(userService.getReferenceById(2));
client = clientService.save(client);
clientService.printClient(client);
client = new Client();
client.setClientId(1);
clientService.printClient(client);
int userId = 5;
clientService.bindUserToClient(userId, 1);
clientService.printClient(1);
};
}
}
gave me correct output in the console:
Client{id=1, user=User{id=2, userRole=UserRole{id=4}}}
Client{id=1, user=User{id=2, userRole=UserRole{id=4}}}
Client{id=1, user=User{id=5, userRole=UserRole{id=1}}}
WORKAROUND
I tried to reach the goal by use of Spring JPA but could'nt.
The workaround that keeps the referential integrity was by creating a constrains through DB like below and add #PrePersist and #PreUpdate annotated method which is updating the client's roleId as intended.
create table clients
(
client_id integer not null,
client_user_id integer,
client_role_id integer,
primary key (client_id)
);
create table user_roles
(
role_id integer generated by default as identity,
primary key (role_id)
);
create table users
(
user_id integer generated by default as identity,
user_role_id integer,
primary key (user_id),
CONSTRAINT User_Role UNIQUE (user_id, user_role_id)
);
alter table users
add constraint FK_role_id foreign key (user_role_id) references user_roles (role_id);
alter table clients
add constraint FK_user_id foreign key (client_user_id, client_role_id) references users (user_id, user_role_id) on update cascade ;
Thanks to that I could for instance update userRole in user entity, and the change was reflected in the clients table as well without any further actions

Spring data rest add element to collection [duplicate]

This question already has answers here:
How to maintain bi-directional relationships with Spring Data REST and JPA?
(4 answers)
Closed 6 years ago.
I'm trying to add an element to a #OneToMany relation following this tutorial on Spring Data Rest, I'm using the request below to add an account to a user accounts collection.
curl -v -X PUT -H "Content-Type: text/uri-list" -d "http://localhost:8080/account/1" http://localhost:8080/user/1/accounts
The request respond with a 204 and nothing changes when I check the user accounts list. Debugging the app I can see that the entity is being saved, so maybe a transaction commit is not happening. I also noticed that if I change the relation to a #OneToOne, the request works as expected.
User class
#Entity
#Getter
#Setter
#Builder
#NoArgsConstructor
#AllArgsConstructor
#ToString(exclude = "accounts")
#EqualsAndHashCode(of = "id", callSuper = false)
#SequenceGenerator(name = "user_sq", sequenceName = "user_sq")
public final class User extends BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "user_sq")
private Long id;
//other properties
#OneToMany(cascade = CascadeType.ALL, mappedBy = "owner", orphanRemoval = true)
private List<Account> accounts;
// #OneToOne(cascade = CascadeType.ALL)
// private Account accounts;
}
Account class
#Entity
#Getter
#Setter
#Builder
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode(of = "id", callSuper = false)
#SequenceGenerator(name = "account_sq", sequenceName = "account_sq")
public final class Account extends BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "account_sq")
private Long id;
//other properties
#ManyToOne
private User owner;
}
Repositories
#RepositoryRestResource(collectionResourceRel = "accounts", path = "account")
public interface AccountRepository extends PagingAndSortingRepository<Account, Long> {}
#RepositoryRestResource(collectionResourceRel = "users", path = "user")
public interface UserRepository extends PagingAndSortingRepository<User, Long> {}
Try to use something like this body in your POSt or PUT/PATCH requests:
{
// user properties...
"accounts": [
"http://localhost:8080/accounts/1",
"http://localhost:8080/accounts/2"
]
}
To create new User (with his accounts) use POST request to http://localhost:8080/users
To update current User (and his accounts) use PUT/PATCH request to http://localhost:8080/users/1 (1 - is the ID of current user)

Resources