Spring Boot Data Rest POST returns 204 but only SELECTS - spring

So this was working before I switched to Boot. Basically I was able to POST a text/uri-list to a #OneToMany resource just fine. I switched my project to use Boot and somewhere in the process it stopped working. I can PUT a text/uri-list at the #ManyToOne end, but that's not what I want to do.
When I submit the POST, I get a 204 response, but I can see the SQL on my console only Selecting and not inserting anything.
EDIT: I use Postman, but here's a curl command that does/returns the same
curl -v -X POST -H "Content-Type: text/uri-list" -d "http://localhost:8080/games/2" http://localhost:8080/developers/1/gameList
And the logger on IDEA:
Hibernate: select developer0_.developer_id as develope1_1_0_, developer0_.name as name2_1_0_ from developer developer0_ where developer0_.developer_id=?
Hibernate: select game0_.game_id as game_id1_6_0_, game0_.developer_id as develope5_6_0_, game0_.esrb_rating as esrb_rat2_6_0_, game0_.name as name3_6_0_, game0_.release_date as release_4_6_0_, developer1_.developer_id as develope1_1_1_, developer1_.name as name2_1_1_ from game game0_ left outer join developer developer1_ on game0_.developer_id=developer1_.developer_id where game0_.game_id=?
Here are my relevant classes:
#Entity
public class Developer {
#Id
#GeneratedValue
#Column(name = "developerId")
private Long id;
private String name;
#OneToMany(mappedBy = "developer", cascade = CascadeType.ALL)
private List<Game> gameList;
Other one:
#Entity
public class Game {
#Id
#GeneratedValue
#Column(name = "gameId")
private Long id;
private String name;
private Date releaseDate;
private ESRBRating esrbRating;
#ManyToMany(mappedBy = "gameList", cascade = CascadeType.ALL)
private List<User> userList;
#ManyToOne
#JoinColumn(name = "developerId")
private Developer developer;
If I'm missing any other relevant info let me know and I'll provide it.

If you want to keep it bi-directional you seem to have 2 options :
Remove the mappedBy = "developer" and let JPA use a jointable to manage the one-to-many relationship.
Developer:
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(
name="DeveloperGame",
joinColumns = #JoinColumn( name="dev_id"),
inverseJoinColumns = #JoinColumn( name="game_id")
)
private List<Game> gameList;
Game:
#ManyToOne
#JoinTable(
name="DeveloperGame",
joinColumns = #JoinColumn( name="game_id"),
inverseJoinColumns = #JoinColumn( name="dev_id")
)
private Developer developer;
Remove the mappedBy = "developer" and add a #JoinColumn if you don't want to use a jointable (make you have a joincolumn on both sides of the relationship with the same column name
Developer:
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "devId")
private List<Game> gameList;
Game:
#ManyToOne
#JoinColumn(name = "devId")
private Developer developer;
I do wonder if this is by design or if this is a bug in Spring Data REST.

Hibernate collects insert, updates and deletes until the entitymanager is flushed. This is normally done at the end of a transaction.
So it could be that your transaction management does not work correctly.
Set the logging for org.springframework.transaction to debug, than you should see when transactions are opened and closed.

Related

Query result Infinite Recursion on ManyToMany relationship on hibernate

I have a entity mapping like this:
As you can see it is the bidirectional relationship and the team_users table has it own primary key and extra column - active.
Key code in team entity:
#OneToMany(mappedBy = "team", fetch = FetchType.LAZY)
private Set<TeamUsers> team_users = new HashSet<TeamUsers>();
Key code in user entity:
#OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private Set<TeamUsers> team_users = new HashSet<TeamUsers>();
Key code in team_user entity:
#ManyToOne(fetch = FetchType.LAZY, optional = false, cascade = CascadeType.ALL)
#JoinColumn(name = "TEAM_ID")
private Team team;
#ManyToOne(fetch = FetchType.LAZY, optional = false, cascade = CascadeType.ALL)
#JoinColumn(name = "USER_ID")
private User user;
I have a API which will return all team information, then I have service class like:
#Autowired
private TeamRepository teamRepo;
public List<Team> listAll() {
return (List<Team>) teamRepo.findAll();
}
Then the chrome log me that I have a "undefined" value, and when I test it by postman it shows me the infinite loop value:
I want to figure out what is the best approach to fetch the data?
I want to all information that tells me the team situation, including team... users...active status , almost everthing.
Any suggestions?
update
I tried to use #JsonIgnore on intermidate table :
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY, optional = false, cascade = CascadeType.ALL)
#JoinColumn(name = "TEAM_ID")
private Team team;
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY, optional = false, cascade = CascadeType.ALL)
#JoinColumn(name = "USER_ID")
private User user;
but I won't get user information in that case and it avoid infinite loop:
What else I can do to get all information for teams?

ManyToMany relation use in service

Job entity
#Column(name = "name")
private String name;
#ManyToMany
#JoinTable(name = "user_job",
joinColumns = #JoinColumn(name = "job_id"),
inverseJoinColumns = #JoinColumn(name = "user_id")
)
private List<User> user;
User entity
#Column(name = "email")
private String email;
#ManyToMany
#JoinTable(name = "user_job",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "role_id")
)
private Set<Role> roles;
Role entity
#Column(name = "name")
private String name;
#ManyToMany(mappedBy = "roles")
private Set<User> users;
Here we have a table user_job with 3 ids and I want to insert data in service layer. How I can do it and what repository I should implement or use existent like user/role/job?
class UserJobService{
public void setUserJob(User user, Job job, Role role){
}
}
The problem with #ManyToMany association is you can't delete a record directly from user_job table, using Hibernate. To delete the record, you need to load a user with all his jobs. So better to add UserJobEntity
#Entity
#Table(name = "USER_JOBS")
class UserJobEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "USER_ID")
private User user;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "JOB_ID")
private Job job;
}
public UserJobEntity createUserJob(UserEntity user, JobEntity job) {
UserJobEntity userJob = new UserJobEntity();
userJob.setUser(user);
userJob.setJob(job);
return userJobRepository.save(userJob);
}
Probably you will want to add (user, job) unique constraint to user_jobs table.
Some advices
Use plurals for table names. user_jobs in place of user_job
Role is tabular data. So it shouldn't have a users List.
Don't use Set for associated collections. Definitely you will encounter "multiple bugs fetch exception" and this exception will help you to change queries. With Set you can have large cross products and even don't notice them.

Hibernate mapping user relation to entities

Let's se we have Hibernate entity User with basic fields such as username, password, roles etc..
Now we have an entity such as Car.
User has a OneToOne relationship with Car, cause he can own a car. But he also has besides this a OneToMany relationship to Car, because he also owns the cars of his children. But in the frontend I want to know which cars he owns for himself and which cars he owns for his children. The same applies to the relationship between User and motorbike (his own, his childrens, etc...)
How would the User entity class look like? Is it good to have the relationships mapped in an "Helper" entity such as UserData:
#Entity
#Data
#Table( name = "users",
uniqueConstraints = {
#UniqueConstraint(columnNames = "username")
})
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank
#Size(max = 150)
private String username;
#NotBlank
#Size(max = 120)
private String password;
#OneToOne(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
#JoinColumn(name = "USER_DATA_ID")
private UserData userData;
UserData:
#Entity
#Data
#Table( name = "user_data")
public class UserData {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToOne(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
#JoinColumn(name = "OWN_CAR_ID")
private Car ownCar;
#OneToOne(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
#JoinColumn(name = "PARTNER_CAR_ID")
private Car partnerCar;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable( name = "user_children_cars",
joinColumns = #JoinColumn(name = "user_data_id"),
inverseJoinColumns = #JoinColumn(name = "car_id"))
private Set<Car> childrenCars = new HashSet<>();
public boolean addToChildrenCarSet(Car c) {
return childrenCars.add(c);
}
public UserData() {
}
}
As you ask for an opinion, I would say it gets unnecessary complicated if you use the intermediate entity user_data. :-) There is no real drawback to add more fields and keys into the user class - performance is probably also better then using the EAGER fetching. If performance is an issue, better optimize querys later on then splitting the table now.
Also the #ManyToMany I would avoid - better create the intermediate table and relations yourself. You can check out https://bootify.io and create your database schema there. There is no EAGER fetching and also no CascadeType.ALL (both only good ideas in special cases), you would probably add more problems with that then actual helping in any way.
So the addToChildrenCarSet method would end up in a #Service class, in a method with #Transactional, in my proposal.

Can't delete child entity without deleting parent entity, regardless of CascadeTypes?

I'm trying to connect an entity (User) to entities they create which will be Surveys.
I have two repositories, one UserRepository and one SurveyRepository. I can load Surveys according to which User has them and currently they are all mapped by the User_ID, which is a field on the Survey entity.
However, when I try to remove a Survey, this removes my User whenever I define CascadeType.ALL.
But when I don't use that, I get another error "Caused by: java.sql.SQLIntegrityConstraintViolationException:"
I'm gussing this is all related to the password encryption I'm using, but I am not even trying to delete the User entity, I'm just deleting the Survey, which holds a reference, or an ID to the Survey..
I've tried CascadeType.All on both sides, and I've tried not having any CascadeType at all as well.. If I have it on both sides, this deletes the user whenever I tell my surveyRepository.delete(currentSurvey);
And whenever I don't have it on both sides, I get the exception above..
User Entity:
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id")
private Long id;
#NotEmpty
#Email
#Column(unique = true)
private String email;
private String password;
#NotBlank
private String username;
#NotBlank
private String firstName;
#NotBlank
private String lastName;
#NotBlank private String role;
#OneToMany(fetch = FetchType.EAGER)
private Set<Survey> surveys = new HashSet<>();
Survey Entity:
#Entity
#Table(name = "survey")
public class Survey {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "survey_id")
private Long id;
private String title, creator, description;
private LocalDate date = LocalDate.now();
#OneToMany(orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "survey_id")
#OrderBy("position ASC")
private Set<Question> questions = new HashSet<>();
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "user_id")
private User user;
I'm just not sure how I can tell JPA/Hibernate not to touch the User whenever we delete the Survey.
It doesn't matter if I save the User with Survvey or not does it?
Basically I've tried a lot of options and I figure I'm not quite grasping the issue, and I suspect it's about the annotations on the User side, but I still feel as if I should be able to delete the child entity with no problem at all since I am not touching the parent entity?
This is because of EAGER fetch type in User class for surveys.
You delete survey but because it is existed on surveys set in user yet, it wouldn't be deleted actually.
You need to do like this:
// User class
#OneToMany(cascade=CascadeType.ALL, orphanRemoval=true, mappedBy="user")
private Set<Survey> surveys = new HashSet<>();
//Survey class
#ManyToOne
#JoinColumn(name = "user_id")
private User user;

Spring JPa Id empty

I have an entity who own many object
#Entity
public class Lodger implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long lodgerId;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "lodger")
private List<IdentityCard> identityCardList;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "lodger")
private List<Phone> phoneList;
...
}
#Entity
public class IdentityCard {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long identityCardId;
private String identyCardValue;
#OneToOne
#JoinColumn(name = "identity_card_type_id") //without -> identity_card_type_identityCardTypeId
private IdentityCardType identityCardType;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "lodger_id")
private Lodger lodger;
}
When i save my lodger, all my object is saved (identiyCard, phone), but their field lodger_id is null.
I was thinking it was supposed to be done automatically when we use cascadeType.all.
The owner side of the bi-directioinal associations are in IdentityCard and Phone entities, this is the same as saying that the mappedBy is in Lodger's associations.
So for the persistence of the links you must set the lodger attribute in IdentityCard and Phone entities. Isn't necessary to add this entities to the Lodger's collections but it is fine because you want to save this entities along with Lodger using cascade option.

Resources