Getting certain value from the foreign entity using JPA - java

I have 2 entities User and Profile where one user has one profile.
The User entity mapping is pretty clear and looks like this:
#Entity
public class User {
#Id
private String id;
#Column(unique = true, nullable = false)
private String email;
#Column(unique = true, nullable = false)
private String name;
}
So the question is about Profile entity mapping, the tricky thing
here is that Profile includes user's email(not entire user's entity), but it shouldn't be either updated or stored by Profile, so the email is readonly attribute from the foreign User entity.
I used the following Profile's entity mapping for getting User's email:
#Entity
public class Profile {
#Id
private String userId;
#PrimaryKeyJoinColumn
private User user;
#Basic
private String firstName;
#Basic
private String lastName;
// ...
public String getEmail() {
return user.getEmail();
}
}
So i decided to join the entire entity and delegate the work to it.
As far as i understand it is impossible to use #JoinColumn in couple with #Column like this:
#OneToOne
#JoinColumn(name = "userId", insertable = false, updatable = false)
#Column(name = "email")
private String email;
I am also not sure about using of #SecondaryTable as it seems that it is designed for a different purpose.
Is there any better approach for getting foreign entity field using JPA mappings?
JPA Backend: EclipseLink 2.6.2

That's not really what JPA was designed to do. Getting the email by just calling user.getEmail() is the cleanest option you have.
You shouldn't be worried too much about loading the entire user; the way I see it it's a single join, and JPA should do it for you. The performance impact should be minimal. (you can simple not expose the internal user object to not impact your object design too much. When using JPA, you're always limiting your OO design options though).
If you were using hibernate, the story would be different. Then you could use the #Formula annotation. It would not be more performant though. Eclipselink has nothing like it.

Related

Mapping DTO with circular self dependency

I have a entity user with self dependency. When i Map this entity to DTO I have the problem of circular dependency. .
User.class:
public class User {
#Id
#GeneratedValue
private Long id;
#NotNull
#Column(name = "first_name")
private String firstName;
#NotNull
#Column(name = "last_name")
private String lastName;
#JsonBackReference
#ManyToMany(
private List<User> friedns_of = new ArrayList<>();
#ManyToMany(cascade = CascadeType.ALL,
mappedBy = "followers")
private List<User> friends = new ArrayList<>();
UserMapper method in UserMapper:
public static UserResponse toUser(User user) {
UserResponse userResponse = new UserResponse();
userResponse.setId(user.getId());
userResponse.setFollowers(user.getFollowers().stream().map(UserMapper::toUser).toList());
userResponse.setFollowing(user.getFollowing().stream().map(UserMapper::toUser).toList());
return userResponse;
}
When i run the method toUser() I get stackOverFlowError exception caused by the infinite circular dependency. Any advise how to solve this?
One way to resolve this is to model the 'follows' relationship as a separate entity:
#Table(name="user_followers")
public class Follows {
#Id
#GeneratedValue
private Long id;
#NotNull
#Column(name = "follower_Id")
private User follower;
#NotNull
#Column(name = "user_id")
private User user;
}
Then you could give your user two one-to-many lists of these entities:
public class User {
#Id
#GeneratedValue
private Long id;
#OneToMany(mappedBy = "user_id")
private List<Follows> followers;
#OneToMany(mappedBy = "follower_Id")
private List<Follows> following;
}
EDIT: instead of the id field in Follows you could use the user_id and follower_id as a composite primary key using #Embeddable. Omitted here for brevity. See here for more details: https://www.baeldung.com/jpa-many-to-many
Since you already have a DTO of UserResponse, you are on the right path towards a correct solution. My suggestion would be to avoid #ManyToMany on an entity level, and manage followers on a service level.
This means you will have to split relation ManyToMany join column into a separate entity, such as UserFollowsEntity with fields userId and followsUserId. Then remove followers and following lists from your User entity entirely.
Now when creating UserResponse in a service, you will have to
Select the actual user from repository – userRepository.findById(userId)
Select followers – userFollowsRepository.findByFollowsUserId(userId)
Select following – userFollowsRepository.findByUserId(userId)
It is a good practice to try and avoid bidirectional in entities relationships entirely if possible.
EDIT: This will give you two lists: followers and following. You will probably want to know their user names, so what you can do is to merge followers and following lists into one, then extract all user ids from that list. Then query user repository with a list of those IDs, and just attach the required user information to your response model.
Yes it does sound like a bit more work compared to the seeming simplicity of utilizing JPA annotations, but this is the best way to avoid circular dependency as well as decouple the Follower functionality from your user entity.

Bidirectional OneToMany JPA mapping with eager fetch on both sides worked [duplicate]

This question already has answers here:
Problem with LazyInitializationException
(2 answers)
Closed 4 months ago.
I have 3 tables in the DB and 3 JPA entities respectively in Java application.
#Data
#Entity
public class Fraud {
#Id
#Column(name = "id")
private Integer id;
#Column(name = "fraud_type")
private String fraudType;
#Column(name = "fraud_value")
private String fraudValue;
#OneToMany(mappedBy = "fraud", fetch = FetchType.EAGER)
private List<FraudActionEntity> fraudActions;
}
#Data
#Entity
public class FraudActionEntity {
#Id
#Column(name = "id")
private Integer id;
#ManyToOne
#JoinColumn(name = "fraud_id")
private Fraud fraud;
#ManyToOne
#JoinColumn(name = "action_id")
private Action action;
#Column(name = "enabled")
private Boolean enabled;
}
#Data
#Entity
public class Action {
#Id
#Column(name = "id")
private Integer id;
#Column(name = "attribute_key")
private String attributeKey;
#Column(name = "attribute_value")
private String attributeValue;
}
#Repository
public interface FraudRepository extends JpaRepository<Fraud, Integer> {
public Fraud findByFraudTypeAndFraudValue(String fraudType, String fraudValue);
}
My use case
On a certain type of fraud, I want to traverse all the actions that triggers from that type of fraud and act on them.
Access code
Fraud fraud = fraudRepository.findByFraudTypeAndFraudValue("Type", "Value");
log.info(fraud.getFraudActions().get(0).getAction());
When I above code runs, everything works OK. I get the fraud and fraudActions associations as well, without getting any error.
I was under the impression that as both entities Fraud and FraudActionEntity are fetching each other eagerly, so it should give some error like cyclic fetch/infinite fetch loop, but it didn't!
Why did it work? And when exactly will give it error like cyclic fetch error OR infinite fetch loop error? And if it does give a cyclic fetch error, can we fix it using lazy fetch at #ManyToOne side as given below:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "fraud_id")
private Fraud fraud;
Update: A simple and very effective work-around towards the LazyInitializationException is to annotate your method with #Transactional annotation. This will create and maintain the transaction while the method is being executed, thereby allowing your code to make the necessary calls to the DB's lazy init objects. Learn more about it here.
The return type of your JPA repository method should be a List of the Entity object, since the result could be more than one row (that is probably why you are getting the null of the fraud variable).
Regarding the Fetch strategy, you could use Eager on that particular association or maybe other strategies. One possible solution would be to make a second query in case you need the lazy-loaded FraudAction list of objects.
Also, as a side-note avoid using lombok data annotation, and always make sure that you have a NoArgsConstructor in your Entity/DTO classes (in your case #Data adds that by accident since it includes #RequiredArgsConstructor and you do not have any final variables.

JPA: Ignore field on Fetch, but save all while saving it. Is there any annotation attribute to do so?

I have the following Entity. In this I want to fetch all data except phoneNumber. What will be the best solution? It would be fine if I could do it with annotation.
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "employee_name")
private String name;
#Column(name = "gender")
private char gender;
#Column(name = "date_of_birth")
private String dob;
#Column(name = "skills")
private String[] skills;
#Column(name = "phone_number")
private String phoneNumber;
//getter setter
}
To tell what will be the best way to do this you have to say why you want to do this and what you want to achieve.
There are many options:
omit the getter
use a projection (DTO or interface)
use inhreitance
use inheritance with #MappedSuperclass
Can you please use #Transient on your field. If it is a subclass of anyclass then on class level please use #Embedded.
If not then you need to read this as this is always lazy fetch From Hibernate, Chapter 19. Improving performance:
Lazy attribute fetching: an attribute or single valued association is fetched when the instance variable is accessed. This approach requires buildtime bytecode instrumentation and is rarely necessary.
You can use #Column(insertable = true, updatable = false) and I am not sure if we can ignore while fetching using entity. you can achieve your requirements using JPA Projections
Also,#JsonIgnore may be useful. it is used to tell Jackson to ignore a certain property of a Java object but

Make Hibernate ignore fields on certain HTTP requests

I have a class User defined as
#Entity
#Table(name = "users")
#JsonIdentityInfo(generator = ObjectIdGenerators.UUIDGenerator.class, property = "jsonUUID")
public class User implements Serializable, UserDetails
{
private static final long serialVersionUID = -7035881497059422985L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
protected Long id;
protected String firstname;
protected String lastname;
protected String username;
protected ProfessionalCategory professional;
protected String companyName;
#Email
protected String email;
protected String password;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable
(
name = "role_user",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "role_id")
)
protected Set<Role> roles;
}
When I perform a GET request on /users/{id} I want Hibernate to fetch the value from all the fields in user and return the "full" object. But when I perform a GET request on /users/ I want to return a list of users containing only firstname and lastname. I do not just want Jackson to ignore certain fields during serialization, I also want Hibernate not to fetch data it does not need (because fetching all the roles for each user can be very costly since I use a join table).
I know I could write my own SQL queries, but then I would loose the benefits of using Hibernate. So is there an elegant way of solving this problem?
The most elegant solution is to use Hibernate criteria and specify two different methods inside your DAO. One method will fetch a single user based on their ID, the other will fetch a list of all users with only first name and last name populated by using a ProjectionList.
public List<User> getAllUsers() {
Criteria query = sessionFactory.getCurrentSession().createCriteria(User.class);
query.setProjection(Projections.projectionList()
.add(Projections.property("firstName"), "firstName")
.add(Projections.property("lastName"), "lastName"))
.setResultTransformer(Transformers.aliasToBean(User.class));
return query.list();
}
The above code causes Hibernate to only fetch the firstName and lastName fields from the database, and then map the results back to your User class using the ResultTransformer. This method is less than ideal, because it is confusing that all the fields aren't populated.
The ideal solution would be to lazily load your collection of roles, so that Hibernate only loads it on request. For more information on how you can set this up, refer to my Q&A here.

JPA Best Practice: Static Lookup Entities

Imagine, an Event entity references a Status Entity:
#Entity
#Table(name = "event")
public class Event()
{
#Id
#Column(name = "id", nullable = false)
private long id;
...
#ManyToOne
#JoinColumn(name = "status_code", nullable = false)
private Status status;
}
#Entity
#Table(name = "status")
public class Status()
{
#Id
#Column(name = "code", nullable = false)
private String code;
#Column(name = "label", nullable = false, updatable = false)
private String label;
}
Status is mapped to a small table 'status'. Status is a typical reference data / lookup Entity.
code label
----- --------------
CRD Created
ITD Initiated
PSD Paused
CCD Cancelled
ABD Aborted
I'm not sure if it is a good idea to model Status as an Entity. It feels more like an enumeration of constants...
By mapping Status as an Entity, I can use Status objects in Java code, and the Status values are equally present in the database. This is good for reporting.
On the other hand, if I want to set a particular Status to an Event, I can't simply assign the constant status I have in mind. I have to lookup the right entity first:
event.setStatus(entityManager.find(Status.class, "CRD"))
Can I avoid the above code fragment? I'm affraid for a performance penalty and it looks very heavy...
Do I have to tweak things with read-only attributes?
Can I prefetch these lookup entities and use them as constants?
Did I miss a crucial JPA feature?
...?
All opinions / suggestions / recommendations are welcome!
Thank you!
J.
You could use entityManager.getReference(Status.class, "CRD"), which might not fetch the entity from the database if it is only used to set a foreign key.
Can I avoid the above code fragment? I'm affraid for a performance penalty and it looks very heavy?
Well, you could use an enum instead. I don't really see why you don't actually.
But if you really want to use an entity, then it would be a perfect candidate for 2nd level caching and this would solve your performance concern.

Categories