JPA retrieve entities relationships - java

I am trying to retrieve the list of all entities containing relations that references the specified entity.
"Role" Entity :
#Entity
#Table(name="Role")
#Access(AccessType.PROPERTY)
public class Role implements Serializable {
private String description;
private Long roleId;
#Column(name = "role_id")
#Id
public Long getRoleId() {}
#Column(name = "description")
public String getDescrition() {}
}
"User" Entity :
#Entity
#Table(name="users")
#Access(AccessType.PROPERTY)
public class User implements Serializable {
private Long userId;
private String name;
private Role role;
#Column(name = "user_id")
#Id
public Long getUserId() {}
#ManyToOne()
JoinColumn(name="role_id")
public Role getRole() {}
public String getName() {}
}
"Group" Entity :
#Entity
#Table(name="groups")
#Access(AccessType.PROPERTY)
public class User implements Serializable {
private Long groupId;
private String description;
private Role role;
#Column(name = "group_id")
#Id
public Long getGroupId() {}
#ManyToOne()
JoinColumn(name="role_id")
public Role getRole() {}
public String getDescription() {}
}
I need a that works like this :
List<Class<?>> entities = getEntityReferences(Role.class);
foreach (Class<?> entity : entities )
System.out.println(entity.getName());
The output would be :
User
Group
I think JPA use something like this for the bean validations or cascade mechanics but I can't find a simple way to achieve this.
The only way I found by now is to iterate through all the annotations of all the entities (returned by "entityManagerFactory.getMetamodel().getEntities()") to look for relations (ManyToOne, OneToOne ect.). It seems a bit tedious, I'm sure there's a better way to do it...
Thank you all...

Related

Lifecycle event not working for non aggregate entity Spring Data JDBC

I am using two entities, Employee and Address, where Employee has the controller to do CRUD operations. So for both the entities I am using lifecycle events where Employee events are working fine but not the Address events. So I am trying to save Employee which has Address in it (one to one relations) and expecting Employee and Address both lifecycle events to get trigger.
Please help me.
Am I doing wrong anywhere?
Here is my code.
#Table("EMPLOYEE")
#Builder // these are lombok code
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode
#Data
public class Employee {
#LastModifiedBy
private String updatedBy;
#CreatedBy
private String createdBy;
private Date dob;
#Size(max = 10)
private String name;
#Id
private Integer id;
#LastModifiedDate
private Date updatedOn;
#Version
private Long version;
#CreatedDate
private Date createdOn;
private Integer age;
#Valid
private Address address;
}
#Table("ADDRESS")
#Builder
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode
#Data
public class Address {
private Integer zip;
#Id
private Integer id;
#Size(max = 10)
#NotNull
private String line1;
}
#RestController
public class EmployeeController
{
//CRUD APIs code
}
#Component
public class EmployeeEvents
extends AbstractRelationalEventListener<Employee>
{
#Override
protected void onBeforeSave(BeforeSaveEvent event) {
System.out.println("........"+ event.getEntity());
}
}
#Component
public class AddressEvents
extends AbstractRelationalEventListener<Address>
{
#Override
protected void onBeforeSave(BeforeSaveEvent event) {
System.out.println("........"+ event.getEntity());
}
}
EDIT 1:
Data getting saved properly. All I want is events to get trigger. And since it's Spring Data JDBC one to one will work without any extra annotations.
Lifecycle events and callbacks only get fired for aggregates.
Part of the design rule for aggregates says that you should access entities internal to an aggregate only by the aggregate.
This would be violated when Spring Data JDBC hands out references to such entities to a listener or callback.
So the correct solution is to listen to BeforeSaveEvent for the aggregate root, i.e. Employee and access the Address via the Employee.
do OneToOne Mapping with Address entity and than try
Example of a one-to-one relationship, in this case between user and address entities.
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
//...
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "address_id", referencedColumnName = "id")
private Address address;
// ... getters and setters
}
#Entity
#Table(name = "address")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
//...
#OneToOne(mappedBy = "address")
private User user;
//... getters and setters
}

Attribute many ids to single from other tables JPA

I want to create entity USERS_GROUP to link some user_ids to group_id.
I guess I overthink this case and now I can't find a solution.
#Entity
#Table(name = "users")
#Data
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private String userId;
private String name;
#OneToMany(mappedBy = "user")
private List<UserGroups> userGroups;
#Table(name = "groups")
#Data
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "group_id")
private String groupId;
private String category;
private String name;
private String description;
#OneToMany(mappedBy = "group")
private List<UserGroups> userGroups;
#Entity
#Data
#Table(name = "user_groups")
public class UserGroups {
#EmbeddedId
UserGroupsCompositeKey id;
#ManyToOne
#MapsId("userId")
#JoinColumn(name = "user_id")
private Users user;
#ManyToOne
#MapsId("featureId")
#JoinColumn(name = "group_id")
private Group group;
#Embeddable
public class UserGroupsCompositeKey implements Serializable {
#Column(name = "user_id")
String userId;
#Column(name = "group_id")
String groupId;
}
I want to sent POST requests like "/group/{group_id}/users"
to send in request body some lists of user_ids to connect them.
I think I overconfigured this solution with a composite key.
Is there some easier and more readable solution for that case ?
EDIT: This solution is not working as I want it to do.
I create jpaRepository class with CompositeKey (Not sure if it's correct way )
#Repository
public interface ProductFeaturesRepository extends JpaRepository<UserGroups, UserGroupsCompositeKey> {
}
And I want to add some Controller method to group class to add some users to group with request body like:
Endpoint:/group/{group_id}/users
Body:
[
{
"userId": "USER-NR423423534634"
},
{
"userId": "USER-NR2355321"
}
]
It's not working properly nothing is added to database after that request

Null values are inserted in the foreign key fields with Hibernate

I have a Question Entity and Tag entity with getter, setter methods and a OneToMany relationship from question to tag and a OneToOne relationship from question to user
public class Question {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
#Column(name="title")
private String title;
#Column(name="body")
private String body;
#Temporal(TemporalType.DATE)
#Column(name="date_created")
private Date date_created;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="user_id")
private User user;
#OneToMany(cascade=CascadeType.ALL)
#JoinColumn(name="tag_id")
private Tag tag;
#Column(name="answer_count")
private int answer_count;
#Column(name="view_count")
private int view_count;
public Question() {
}
Tag entity
public class Tag {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
#Column(name="name")
private String name;
#Column(name="username")
private String username;
#Temporal(TemporalType.DATE)
#Column(name="date_created")
private Date date_created;
public Tag() {
}
When I try to insert a question using Postman with the following details:
{
"title": "stefanyyyxx",
"body": "stefandoyee44",
"date_created": "2019-02-27",
"user_id" : 1,
"tag_id": 1,
"answer_count": 0,
"view_count": 0
}
QuestionRepository.java:
#Override
public void save(Question theQuestion) {
// get the current hibernate session
Session currentSession = entityManager.unwrap(Session.class);
// save employee
currentSession.saveOrUpdate(theQuestion);
}
Null values are being inserted for user_id and tag_id though I used JoinColumn().
MySQL:
As #Karol Dowbecki Suggested,
convert the JSON to DTO object and use that DTO to get the User, Tag Entities from their respective repositories.
Finally create the Question entity object and store it.
Question Entity
#Entity
#Table(name = "question")
public class Question {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "title")
private String title;
#Column(name = "body")
private String body;
#Temporal(TemporalType.DATE)
#Column(name = "date_created")
private Date dateCreated;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "user_id")
private User user;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "tag_id")
private Set<Tag> tag;
#Column(name = "answer_count")
private int answerCount;
#Column(name = "view_count")
private int viewCount;
}
User Entity
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
Tag Entity
#Entity
#Table(name = "tag")
public class Tag {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#Column(name = "username")
private String username;
#Temporal(TemporalType.DATE)
#Column(name = "date_created")
private Date dateCreated;
}
DTO Class
public class QuestionDTO {
private Long id;
private String title;
private String body;
private Date dateCreated;
private Long user;
private Long tag;
private int answerCount;
private int viewCount;
}
Test Class
#Service
public class TestService {
#Autowired
private QuestionRepository questionRepository;
#Autowired
private UserRepository userRepository;
#Autowired
private TagRepository tagRepository;
public void addQuestion(QuestionDTO dto) {
Tag tag = null;
User user = null;
Question question = null;
Set<Tag> tags = null;
tag = tagRepository.findById(dto.getTag());
tags = new HashSet<>();
tags.add(tag);
user = userRepository.findById(dto.getUser());
question = new Question();
question.setTag(tags);
question.setUser(user);
question.setId(dto.getId());
question.setBody(dto.getBody());
question.setTitle(dto.getTitle());
question.setViewCount(dto.getViewCount());
question.setAnswerCount(dto.getAnswerCount());
question.setDateCreated(dto.getDateCreated());
questionRepository.save(question);
}
}
NOTE :
The relation between Question and Tag are in OneToMany you have to use Collection type.
You have a mismatch between JSON and #Entity structure. JSON contains numeric identifiers while the #Entity contains actual objects representing relationships. You most likely should introduce a separate DTO class to map this JSON while in #Repository you should load User and Tag objects based on their id or create new ones. You already have CascadeType.ALL so Hibernate will cascade the persist operation.
Generally the controller layer should be separate from repository layer unless you are doing something very, very simple. This helps to evolve the service without changing the API contract e.g. adding new columns for auditing changes. By exposing the #Entity as DTO you make your life harder down the road.
You should add referencedColumnName in your Child Entity Foreign Key Column
referencedColumnName="your primaray key column name"
EDIT:
referencedColumnName
The name of the column referenced by this foreign key column.
When used with entity relationship mappings other than the cases
described here, the referenced column is in the table of the target
entity.
When used with a unidirectional OneToMany foreign key mapping, the
referenced column is in the table of the source entity.
When used inside a JoinTable annotation, the referenced key column is
in the entity table of the owning entity, or inverse entity if the
join is part of the inverse join definition.
When used in a CollectionTable mapping, the referenced column is in
the table of the entity containing the collection.
Default (only applies if single join column is being used): The same
name as the primary key column of the referenced table.
Asset is Parent Entity and AssetDetails is Child Entity
Here I have taken OneToOne Relationship
Asset.java
#Entity
#Table(name="asset")
public class Asset {
#Id
#GeneratedValue
#Column(name="assetid")
private BigInteger assetid;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "asset")
#JsonBackReference
private AssetDetails assetDetails;
public AssetDetails getAssetDetails() {
return assetDetails;
}
public void setAssetDetails(AssetDetails assetDetails) {
this.assetDetails = assetDetails;
assetDetails.setAsset(this);
}
public Asset(your fields, AssetDetails assetDetails) {
super();
// your fields
this.assetDetails = assetDetails;
this.assetDetails.setAsset(this);
}
public Asset() {
super();
}
public BigInteger getAssetid() {
return assetid;
}
public void setAssetid(BigInteger assetid) {
this.assetid = assetid;
}
}
AssetDetails.java
#Entity
#Table(name="assetDetails")
public class AssetDetails {
#Id
#GeneratedValue
private BigInteger assetdetailid;
#JoinColumn(name = "assetid",nullable = false, updatable = false,referencedColumnName="assetid")
#OneToOne(cascade=CascadeType.ALL)
#JsonManagedReference
private Asset asset;
public Asset getAsset() {
return asset;
}
public void setAsset(Asset asset) {
this.asset = asset;
}
public AssetDetails(your fields,Asset asset) {
super();
//your fields
this.asset = asset;
}
}

JPA Entity with two fields of the same type

I'm trying to have 2 fields of the same domain class in my entity and I'm getting this error:
org.hibernate.MappingException: Could not determine type for: com.packt.webapp.domain.User, at table: opinions, for columns: [org.hibernate.mapping.Column(author)]
My entities:
#Entity
#Table(name="opinions")
public class Opinion {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#NotNull
private String text;
#NotNull
private String date;
#ManyToOne
#JoinColumn(name="commented_user")
private User writtenTo;
private User author;
#Entity
#Table(name="user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String username;
private String password;
#OneToMany(mappedBy="writtenTo")
private List<Opinion> opinions;
I just want to map opinions to commented users and storage author of comment in author field. When I remove author field, everything works. Whats wrong with this example?
It's complaining that it doesn't know how to map the author field. You can provide a mapping similar to how you mapped writtenTo. An opinion has one author and an author can have authored many opinions.
If you would like to ignore a field for mapping, annotate it with #Transient. The transient annotation prevents that field from being persisted to the database otherwise you have to map it like so:
Opinion entity:
#Entity
#Table(name="opinions")
public class Opinion {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#NotNull
private String text;
#NotNull
private String date;
#ManyToOne
#JoinColumn(name="commented_user")
private User writtenTo;
// map author to User entity
#ManyToOne
#JoinColumn(name="authored_user")
private User author;
// getters and setters
}
User entity:
#Entity
#Table(name="user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String username;
private String password;
#OneToMany(mappedBy="writtenTo")
private List<Opinion> opinions;
// map opinions to the author
#OneToMany(mappedBy="author")
private List<Opinion> authoredOpinions;
// getters and setters
}
Try annotating also author?
#ManyToOne
#JoinColumn(name="author")
private User author;
Just apply the #ManyToOne annotation at both the User fields.
#ManyToOne
#JoinColumn(name="commented_user")
private User writtenTo;
#ManyToOne
#JoinColumn(name="author")
private User author;
But there is a more flexible solution to problems like this. Replace the #OneToMany and #ManyToOne relations by #ManyToMany ones. Create a User and a Role entity (with descendats for the ones with specific fields). A User could have many roles (writer, author, etc) and a Role could be played by many users. In this case you can change your mind and create/remove/attach/detach roles dynamically without any data structure changing on the existing tables.
#Entity
public class User
{
#Id
private Long id;
#Column
private String name;
#ManyToMany
#JoinTable(
name="User_Role",
joinColumns=#JoinColumn(name="UserID", referencedColumnName="ID"),
inverseJoinColumns=#JoinColumn(name="RoleID", referencedColumnName="ID"))
private List<Role> roles;
}
#Entity
public class Role
{
#Id
private Long id;
#Column
private String name;
#ManyToMany( mappedBy="roles" )
private List<User> users;
}
And you can get/check the user roles by role ids/names using a utility class:
puclic class RoleUtility
{
public Role getUserRoleByName( User user_, String roleName_ )
{
User retRole = null;
Iterator<Role> i = roles_.iterator();
while ( ( retRole == null ) && i.hasNext() )
{
Role role = (Role) i.next();
if ( roleName_.isEqual( role.getName ) )
retRole = role;
}
return retRole;
}
}
The client code to checking a role:
User user = ...
Role role = RoleUtility.getRoleByName( user.getRoles(), roleName );
Stay at you example with this solution you can add a censor/moderator to the opinion or something like that without any data structure changing.

many-to-many-relationship between two entities in spring boot

I have two Entities in my Spring-Boot Application:
User.java
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
Long id;
String firstname;
String lastname;
String username;
String password;
}
and
Role.java
Entity
#Table(name = "role")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
Long id;
String name;
String description;
}
for my MySql database
I have excluded the getter and setter methods for this question.
I want to realise a Many-to-Many-Relationship between both Entities. Every user should be able to assign multiple roles to himself
I already Created a mapping table for both tables in my database. It has the rows
user_id
role_id.
I also created a new Entity UserRole.java which looks like this:
#Entity
#Table(name = "user_role")
public class UserRole implements Serializable{
private User user;
private Role role;
#Id
#ManyToOne
#JoinColumn(name = "user_id")
public User getuser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
#Id
#ManyToOne
#JoinColumn(name = "role_id")
public Role getrole(){
return role;
}
public void setRole(Role role){
this.role = role;
}
}
Now my question: is this construction correct? If yes, how do i add existing roles to an existing user and get the roles of this user in spring-boot?
You can find any tutorial connected with many-to-many relationship using Hibernate/Spring Data, example:
Spring Data many-to-many
With your model it's simple to add the relationship mappings, like this:
#Entity
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String description;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable
private Set<User> users;
}
and this:
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstname;
private String lastname;
private String username;
private String password;
#ManyToMany(mappedBy = "users")
private Set<Role> roles;
}

Categories

Resources