Query result Infinite Recursion on ManyToMany relationship on hibernate - spring

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?

Related

Using nested property access in a JPA repository for composite key entity implemented using #EmbeddedID approach returning null values

My SQL view :-
CREATE OR REPLACE VIEW my_view
AS
SELECT
col1,
col2,
col3,
col4,
col5,
col6
FROM
my_table
My JPA entity :-
#Entity
#Table(name = "my_view")
#Getter
#Setter
public class MyEntity {
#EmbeddedId
private MyPk myPk;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "col1", insertable = false, updatable = false)
private Entity1 entity1 ;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col2", insertable = false, updatable = false)
private Entity2 entity2;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col3", insertable = false, updatable = false)
private Entity3 entity3;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col4", insertable = false, updatable = false)
private Entity4 entity4;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col5", insertable = false, updatable = false)
private Entity5 entity5;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col6", insertable = false, updatable = false)
private Entity6 entity6;
}
My Embedded Id class:-
#NoArgsConstructor
#EqualsAndHashCode
#AllArgsConstructor
#Embeddable
public class MyPk implements Serializable {
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "col1", insertable = false, updatable = false)
private Entity1 entity1 ;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col2", insertable = false, updatable = false)
private Entity2 entity2;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col3", insertable = false, updatable = false)
private Entity3 entity3;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col4", insertable = false, updatable = false)
private Entity4 entity4;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col5", insertable = false, updatable = false)
private Entity5 entity5;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col6", insertable = false, updatable = false)
private Entity6 entity6;
}
My JPA Repository :-
public interface MyEntityRepository extends JpaRepository<MyEntity, MyPk> {
List<MyEntity> findByMyPk_Entity1_Id(Long id);
}
Entity1 has an attribute id
I have tried #IdClass approach for composite key and many other solutions related to composite key but all are giving me some kind of error during runtime
This above approach is giving me no runtime error but giving null elements when invoking findByMyPk_Entity1_Id method but its giving correct count of elements in list
I cant use any kind of sequence from table or any other unique column approach dur to my underlying code. Also I tried using #mapIds approach but its also giving null elements.
For some reason JPA repository is not able to convert into entities but its able to give correct count of entities fetched
Also when using findAll method of repository in debugger its giving me proper entities list with no null elements but when searching through nested property i am not able to get entities
spring-data-jpa:1.9.4 version
hibernate-jpa-2.1-api:1.0.0
java 8
< Its a legacy project :") >
I don't know what you really have mapped in your table/view that you really need all 6 columns to uniquely identify a row. Assuming you do, and assuming they are all references to other tables that you want to have mapped in your entity, there are a number of approaches. The easiest, IMO more flexible approach might be this:
#Entity
#Table(name = "my_view")
public class MyEntity {
#EmbeddedId
private MyPk myPk;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "col1")
#MapsId("entity1")
private Entity1 entity1 ;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col2")
#MapsId("entity1")
private Entity2 entity2;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col3")
#MapsId("entity1")
private Entity3 entity3;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col4")
#MapsId("entity1")
private Entity4 entity4;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col5")
#MapsId("entity1")
private Entity5 entity5;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col6")
#MapsId("entity1")
private Entity6 entity6;
}
#Embeddable
public class MyPk implements Serializable {
private Integer entity1 ;//use the ID type from Entity1's primary key
private Integer entity2;
private Integer entity3;
private Integer entity4;
private Integer entity5;
private Integer entity6;
}
Mappings in the MyPk embeddable class will be basic mappings, and pick up the column name settings from the entity ManyToOne mappings. Should you try to ever write one out (you can't write to views, but JPA treats everything as a table), you only need to set the appropriate relationships and JPA will extract the fk values to populate your MyEntity.myKey.entityX values for you.
Having both the embedded and the ManyToOne references means you can have the fk values within your entity and accessible without having to fetch the related entity just for the ID value.

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.

How to store userid, roleid and privilegeid in single table using #JoinTable?

Requirement : Create table using #JoinColumn annotation in User entity class, which contain columns like, user_id, authority_id(means role), privilege_id.
Condition: Authority and Privileges also have one to many relationship.
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "pm_authority_privilege",
joinColumns = #JoinColumn(name = "authority_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "privilege_id", referencedColumnName = "id"))
private Set<Privilege> privileges = new HashSet<>();
As per above configuration i got privilege as part of authority json.
Issue : I'm confused what should be variable type if i take two value in 'inverseJoinColumns'.
When i try to do same thing using following way it gives error of transactional object because privilege is exist on two place.
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "pm_user_authority_privilege",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = {
#JoinColumn(name = "authority_id", referencedColumnName = "id")
// #JoinColumn(name = "privilege_id", referencedColumnName = "id")
})
private Set<Authority> authorities = new HashSet<>();
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "pm_user_authority_privilege",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "privilege_id", referencedColumnName = "id"))
private Set<Privilege> privileges = new HashSet<>();
Note : There is page which have no. of roles and all relevant privilege of those role.(handle using code define in issue)
And from that i select some of role and save them when user created.
{
"authorities": [
{
"authorityId": "ec071816-31e6-11ea-bae1-0242ac110005",
"authorityName": "MANAGER",
"description": null,
"privileges": [
{
"description": "",
"privilege": "CREATE_USER",
"privilegeId": "ba9a4952-4d53-42e9-94fe-8373d334819e"
},
{
"description": null,
"privilege": "SHOW_DATA",
"privilegeId": "ec06de1b-31e6-11ea-bae1-0242ac110005"
}
]
}
],
"email": "test#gmail.com",
"password": "test#123",
"phoneNo": "8575456595",
"status": true,
"userId": null,
"username": "test"
}
i found something. It is not up to the mark but it help solve your problem.
As per i understand you need two kind of relationship,
Authority & Privileges
User & Authority & Privileges (Privileges also exist in Authority object)
In such case you have to follow this,
Create one UserDTO which access user info and authority JSON (So privilege not show two times).
Based on that JSON create two entity and their object. One for User and another for UserAuthorityPrivilege.
UserAuthorityPrivilege is the table which contain your three required data and as obvious all three are required so define them as embedded key.
Give the relation between user and UserAuthorityPrivilege table
Embaded Class :
#Data
#Embeddable
#NoArgsConstructor
#AllArgsConstructor
public class EmbeddedUserAccess implements Serializable {
#Column(name = "user_id") private UUID userId;
#Column(name = "authority") private String authority;
#Column(name = "privilege") private String privilege; }
Entity Class
#Entity
#Table(name = "pm_user_authority_privilege")
#Data
#NoArgsConstructor
#AllArgsConstructor
public class UserAuthorityPrivilege {
#EmbeddedId
private EmbeddedUserAccess id;
#Null
#Column(name = "store_id", nullable = true)
private UUID storeId;
}
DTO:
#Data
#NoArgsConstructor
#AllArgsConstructor
public class UserDTO {
private UUID userId;
private String username;
private String email;
private String phoneNumber;
private String password;
private Boolean status;
private Authority authority;
}
Fetch:
UserAuthorityPrivilege access = null;
EmbeddedUserAccess embedded = null;
Set<UserAuthorityPrivilege> accessibilities = new HashSet<>();
Authority authority = userDTO.getAuthority();
for (Privilege privilege : authority.getPrivileges()) {
access = new UserAuthorityPrivilege();
embedded = new EmbeddedUserAccess();
embedded.setUserId(user.getUserId());
embedded.setAuthority(authority.getAuthority());
embedded.setPrivilege(privilege.getPrivilege());
access.setId(embedded);
accessibilities.add(access);
}
You have to update code similerly.
I have one query, your user relate with single authority or multiple.
For more brief description add comment.

mappedBy reference an unknown target entity property with one to many [duplicate]

I am having an issue in setting up a one to many relationship in my annotated object.
I have the following:
#MappedSuperclass
public abstract class MappedModel
{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="id",nullable=false,unique=true)
private Long mId;
then this
#Entity
#Table(name="customer")
public class Customer extends MappedModel implements Serializable
{
/**
*
*/
private static final long serialVersionUID = -2543425088717298236L;
/** The collection of stores. */
#OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Collection<Store> stores;
and this
#Entity
#Table(name="store")
public class Store extends MappedModel implements Serializable
{
/**
*
*/
private static final long serialVersionUID = -9017650847571487336L;
/** many stores have a single customer **/
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn (name="customer_id",referencedColumnName="id",nullable=false,unique=true)
private Customer mCustomer;
what am i doing incorrect here
The mappedBy attribute is referencing customer while the property is mCustomer, hence the error message. So either change your mapping into:
/** The collection of stores. */
#OneToMany(mappedBy = "mCustomer", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Collection<Store> stores;
Or change the entity property into customer (which is what I would do).
The mappedBy reference indicates "Go look over on the bean property named 'customer' on the thing I have a collection of to find the configuration."
I know the answer by #Pascal Thivent has solved the issue. I would like to add a bit more to his answer to others who might be surfing this thread.
If you are like me in the initial days of learning and wrapping your head around the concept of using the #OneToMany annotation with the 'mappedBy' property, it also means that the other side holding the #ManyToOne annotation with the #JoinColumn is the 'owner' of this bi-directional relationship.
Also, mappedBy takes in the instance name (mCustomer in this example) of the Class variable as an input and not the Class-Type (ex:Customer) or the entity name(Ex:customer).
BONUS :
Also, look into the orphanRemoval property of #OneToMany annotation. If it is set to true, then if a parent is deleted in a bi-directional relationship, Hibernate automatically deletes it's children.
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "USER_ID")
Long userId;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "sender", cascade = CascadeType.ALL)
List<Notification> sender;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "receiver", cascade = CascadeType.ALL)
List<Notification> receiver;
}
public class Notification implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "NOTIFICATION_ID")
Long notificationId;
#Column(name = "TEXT")
String text;
#Column(name = "ALERT_STATUS")
#Enumerated(EnumType.STRING)
AlertStatus alertStatus = AlertStatus.NEW;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "SENDER_ID")
#JsonIgnore
User sender;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "RECEIVER_ID")
#JsonIgnore
User receiver;
}
What I understood from the answer. mappedy="sender" value should be the same in the notification model. I will give you an example..
User model:
#OneToMany(fetch = FetchType.LAZY, mappedBy = "**sender**", cascade = CascadeType.ALL)
List<Notification> sender;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "**receiver**", cascade = CascadeType.ALL)
List<Notification> receiver;
Notification model:
#OneToMany(fetch = FetchType.LAZY, mappedBy = "sender", cascade = CascadeType.ALL)
List<Notification> **sender**;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "receiver", cascade = CascadeType.ALL)
List<Notification> **receiver**;
I gave bold font to user model and notification field. User model mappedBy="sender " should be equal to notification List sender; and mappedBy="receiver" should be equal to notification List receiver; If not, you will get error.

Spring Boot Data Rest POST returns 204 but only SELECTS

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.

Resources