Hibernate relationship mappings - must? - java

In hibernate we can map entity relationships with one to one, one to many, etc. I am little bit skeptical about using the relationship annotations and I prefer to use individual find methods to retrieve child records. Example,
Lets consider I have two tables, User and Roles. Each user can have one role. So the entities are,
class User {
#Column
private String name;
#OneToOne(mappedBy="role_id")
private Role role;
.... getter/setter....
}
class Role {
...
}
Either we have to make eager fetch or it will lead to lazy initialisation exception if the role is accessed outside of the current session.
Instead of this, shall we have the mapping like this?
class User {
#Column
private String name;
#Column
private Long roleId;
....
}
This way, whenever we need the role details, we can get the role_id from the User object and query the role table? Is this a right approach? Yes, I know the benefit of loading object graph, but I think this approach will avoid the unnecessary eager fetches and will run seamlessly if we do database partitions.
(I always consider databases as just datastore and use use individual queries to retrieve data instead of using joins to avoid load on the DB).
Please let me know your thoughts.

I suggest using
class User {
#Column
private String name;
#OneToOne(mappedBy="role_id")
private Role role;
.... getter/setter....
}
class Role {
...
}
Don't see point in calling database select twice. Database can handel joins good and do select very fast so I don't see point to manually do it. Also when using this approach you can easy save/update/delete objects.
To avoid lazy initialisation exception you can use Hibernate.initialize(Object obj) as explained here How Hibernate.initialize() works.

Related

OneToMany lazy initialization when needing collection data

What's a workaround if I have a relation OneToMany and would like to access the collection that is lazy loaded? Currently I get LazyInitializationException having this:
Club entity:
#Entity
public class Club {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#OneToMany(mappedBy = "club", cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
#JsonBackReference
private List<Player> players;
Player entity:
Is it a good idea to have two methods, where one fetches data without players and the second one that fetches also players?
#Override
List<Club> findAll();
#Query("Select clubs from Club clubs left join fetch clubs.players")
List<Club> getAllClubsWithPlayers();
What I'm thinking of is that it is a bad idea, because if I have a situation where I have for example 4 properties that are lazy loaded and I'd need at once 3 of them, I'd have to have a query like: getAllClubsWithPlayersAndSponsorsAndCoaches, so I'd have to have a lot of combinations of such queries.
I don't want to use EAGER, so could you tell me if it's a common way to do this if I need access to players from the Club sometimes which is undoable with the findAll() method that throws LazyInitializationException?
Don't get me wrong - I know where the LazyInitializationException comes from, but I just don't know what's the best way to get access to players if I sometimes need them when fetching clubs. Is my way of doing it correct?
There are 3 choices:
Access all the lazy fields inside a #Transactional method. You don't show your code, but there's usually a Service Facade layer which is responsible for being #Transactional. It invokes Repositories.
Write a query that fetches all the required data. Then you'd have to create a method specifically to fetch all the lazy fields required for that logic.
Use OpenSessionInViewFilter or OpenSessionInViewInterceptor so that Session/EntityManager are started before the execution even reaches the Controller. The Session then would be closed by the same high-level layer at the end of the request processing.
In addition to what Stanislav wrote, I'd like to elaborate on his 2nd point, because I think that this is often the best approach - that's simply because it saves unnecessary calls to the database which results in better performance.
Apart from writing separate JPQL query in your repository for each use-case, you could do one of the following .:
Make your repository extend JpaSpecificationExecutor and programmatically describe what needs to be fetched as described in this answer
Use Entity Graph either described using annotations, or programmatically, and fetch your entities using EntityManager as described in this tutorial
To optionally load what you want you can use EntityGraph.
Declare #NamedEntityGraph at your entity
#Entity
#NamedEntityGraph(name = "Club.players",
attributeNodes = #NamedAttributeNode("players")
)
public class Club {
Then you should annotate your findAll() method with this graph using it's name
#EntityGraph(value = "Club.players")
List<Club> findAll();
However that would override your basic findAll() method.
To avoid this (to have both implementations) you can follow this way:
Add dependency from https://mvnrepository.com/artifact/com.cosium.spring.data/spring-data-jpa-entity-graph/<version>
Then replace your repository with
#Repository
public interface ClubRepository extends JpaSpecificationExecutor<Club>, JpaRepository<Club, Long>, EntityGraphJpaSpecificationExecutor<Club> {
}
And then you'll have basic method findAll() and also from your sevice you can call
List<Club> clubs = clubRepository.findAll(specification, new NamedEntityGraph(EntityGraphType.FETCH, "Club.players"))

Hibernate good practice, lazy/eager loading and saving/deleting children (help me Hibernate sensei)

So, I have found myself in quite a pickle regarding Hibernate. When I started developing my web application, I used "eager" loading everywhere so I could easily access children, parents etc.
After a while, I ran into my first problem - re-saving of deleted objects. Multiple stackoverflow threads suggested that I should remove the object from all the collections that it's in. Reading those suggestions made my "spidey sense" tickle as my relations weren't really simple and I had to iterate multiple objects which made my code look kind of ugly and made me wonder if this was the best approach.
For example, when deleting Employee (that belongs to User in a sense that User can act as multiple different Employees). Let's say Employee can leave Feedback to Party, so Employee can have multiple Feedback and Party can have multiple Feedback. Additionally, both Employee and Party belong to some kind of a parent object, let's say an Organization. Basically, we have:
class User {
// Has many
Set<Employee> employees;
// Has many
Set<Organization> organizations;
// Has many through employees
Set<Organization> associatedOrganizations;
}
class Employee {
// Belongs to
User user;
// Belongs to
Organization organization;
// Has many
Set<Feedback> feedbacks;
}
class Organization {
// Belongs to
User user;
// Has many
Set<Employee> employees;
// Has many
Set<Party> parties;
}
class Party {
// Belongs to
Organization organization;
// Has many
Set<Feedback> feedbacks;
}
class Feedback {
// Belongs to
Party party;
// Belongs to
Employee employee;
}
Here's what I ended up with when deleting an employee:
// First remove feedbacks related to employee
Iterator<Feedback> iter = employee.getFeedbacks().iterator();
while (iter.hasNext()) {
Feedback feedback = iter.next();
iter.remove();
feedback.getParty().getFeedbacks().remove(feedback);
session.delete(feedback);
}
session.update(employee);
// Now remove employee from organization
Organization organization = employee.getOrganization();
organization.getEmployees().remove(employee);
session.update(organization);
This is, by my definition, ugly. I would've assumed that by using
#Cascade({CascadeType.ALL})
then Hibernate would magically remove Employee from all associations by simply doing:
session.delete(employee);
instead I get:
Error during managed flush [deleted object would be re-saved by cascade (remove deleted object from associations)
So, in order to try to get my code a bit cleaner and maybe even optimized (sometimes lazy fetch is enough, sometimes I need eager), I tried lazy fetching almost everything and hoping that if I do, for example:
employee.getFeedbacks()
then the feedbacks are nicely fetched without any problem but nope, everything breaks:
failed to lazily initialize a collection of role: ..., could not initialize proxy - no Session
The next thing I thought about was removing the possibility for objects to insert/delete their related children objects but that would probably be a bad idea performance-wise - inserting every object separately with
child.parent=parent
instead of in a bulk with
parent.children().add(children).
Finally, I saw that multiple people recommended creating my own custom queries and stuff but at that point, why should I even bother with Hibernate? Is there really no good way to handle my problem relatively clean or am I missing something or am I an idiot?
If I understood the question correctly it's all about cascading through simple 1:N relations. In that case Hibernate can do the job rather well:
#Entity
public class Post {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(cascade = CascadeType.ALL,
mappedBy = "post", orphanRemoval = true)
private List<Comment> comments = new ArrayList<>();
}
#Entity
public class Comment {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
private Post post;
}
Code:
Post post = newPost();
doInTransaction(session -> {
session.delete(post);
});
Generates:
delete from Comment where id = 1
delete from Comment where id = 2
delete from Post where id = 1
But if you have some other (synthetic) collections, Hibernate has no chance to know which ones, so you have to handle them yourself.
As for Hibernate and custom queries, Hibernate provides HQL which is more compact then traditional SQL, but still is less transparent then annotations.

Safe embedded entity with objectify

I have two entities.
#Entity
public class Recipe {
#Id
private Long id;
private List<Step> steps;
}
#Entity
public class Step {
#Id
private Long id;
private String instruction;
}
And the following Clound Endpoint
#ApiMethod(
name = "insert",
path = "recipe",
httpMethod = ApiMethod.HttpMethod.POST)
public Recipe insert(Recipe recipe) {
ofy().save().entities(recipe.getSteps()).now(); //superfluous?
ofy().save().entity(recipe).now();
logger.info("Created Recipe with ID: " + recipe.getId());
return ofy().load().entity(recipe).now();
}
I'm wondering how do I skip the step where I have to save the emebedded entity first. The Id of neither entity is set. I want objectify to automatically create those. But if don't save the embedded entity I get an exception.
com.googlecode.objectify.SaveException: Error saving com.devmoon.meadule.backend.entities.Recipe#59e4ff19: You cannot create a Key for an object with a null #Id. Object was com.devmoon.meadule.backend.entities.Step#589a3afb
Since my object structure will get a lot more complex, I need to find a way to skip this manual step.
I presume you are trying to create real embedded objects, not separate objects stored in the datastore and linked. Your extra save() is actually saving separate entities. You don't want that.
You have two options:
Don't give your embedded object an id. Don't give it #Entity and don't give it an id field (or at least eliminate #Id). It's just a POJO. 90% of the time, this is what people want with embedded objects.
Allocate the id yourself with the allocator, typically in your (non-default) constructor.
Assuming you want a true embedded entity with a real key, #2 is probably what you should use. Keep in mind that this key is somewhat whimsical since you can't actually load it; only the container object can be looked up in the datastore.
I suggest going one step further and never use automatic id generation for any entities ever. Always use the allocator in the (non-default) constructor of your entities. This ensures that entities always have a valid, stable id. If you always allocate the id before a transaction start, it fixes duplicate entities that can be created when a transaction gets retried. Populating null ids is just a bad idea all around and really should not have been added to GAE.
The concept of the embedded is that the embedded content is persisted inside the main entity.
Is this the behaviour you are trying to configure?
The default behaviour of a Collection (List) of #Entity annoted class is to refer them instead of embed them. As you current configuration, the List<Step> variable does not have any annotation to override the default configuration, which is a different entity related to another one.
The error you are getting is because Objectify, when it saves the recipe entity, is trying to get the key of each step to create the relationship (and save them in the recipe entity), but if the entity step is not saved yet on the datastore, does not have a key
If you are trying to persist the steps inside the recipe entity, you need to setup objectify like this
#Entity
public class Recipe {
#Id
private Long id;
private List<Step> steps;
}
public class Step {
private Long id;
private String instruction;
}
As you can see, I removed the #Id annotation (an embedded Entity does not require an ID because is inside another entity) and the #Entity from the Step class. With this configuration, Objectify save the step entities inside the recipe entity
Source: https://code.google.com/p/objectify-appengine/wiki/Entities#Embedded_Object_Native_Representation

Persisting third-party classes with no ID's

Say I have the following Java class, which is owned by a vendor so I can't change it:
public class Entry {
private String user;
private String city;
// ...
// About 10 other fields
// ...
// Getters, setters, etc.
}
I would like to persist it to a table, using JPA 2.0 (OpenJPA implementation). I cannot annotate this class (as it is not mine), so I'm using orm.xml to do that.
I'm creating a table containing a column per field, plus another column called ID. Then, I'm creating a sequence for it.
My question is: is it at all possible to tell JPA that the ID that I would like to use for this entity doesn't even exist as a member attribute in the Entry class? How do I go about creating a JPA entity that will allow me to persist instances of this class?
EDIT
I am aware of the strategy of extending the class and adding an ID property it. However, I'm looking for a solution that doesn't involve extending this class, because I need this solution to also be applicable for the case when it's not only one class that I have to persist, but a collection of interlinked classes - none of which has any ID property. In such a scenario, extending doesn't work out.
Eventually, I ended up doing the following:
public class EntryWrapper {
#Id
private long id;
#Embedded
private Entry entry;
}
So, I am indeed wrapping the entity but differently from the way that had been suggested. As the Entry class is vendor-provided, I did all its ORM work in an orm.xml file. When persisting, I persist EntryWrapper.
I don't have much experience with JPA, but I wouldn't extend your base classes, instead I would wrap them:
public class PersistMe<T> {
#Id
private long id;
private T objToWrap;
public(T objToWrap) {
this.objToWrap = objToWrap;
}
}
I can't test it, if it doesn't work let me know so I can delete the answer.

Inserting a Hibernate entity with relationship

Say I have these classes:
public class Loan {
#Id
private Long id;
#ManyToOne
#JoinColumn(name = "lender_id")
private User lender;
}
public class User {
#Id
private Long id;
#Column
private String userName;
#OneToMany
private List<Loan> loans;
}
Now, let's say I have the user (lender) id and in the DAO layer, I want to create a Loan based on the id of the lender?
I realize that I can do the following:
User u = userDao.getUserById(1234L);
loanDao.createLoan(u, "someLoan");
But I'm wondering if it's possible to do it without pre-loading the User record?
There isn't a good way to do that, in part because it would fundamentally lead to incorrect ORM code. You the programmer are responsible for managing the in memory state of the Entities and keeping them correct. If you create a new Loan and say it belongs to a User, and a User has a collection of Loans, it is your responsibility to add that Loan to the User! (This has real consequences as soon as the caches get involved.)
You're using ORM, you need to think in terms of the objects and not in terms of the database. Adding a number in a foreign key column isn't what's important, setting up the correct in-memory representation of the object Model is what's important for you. The database is hibernate's problem.

Categories

Resources