Hi I have two classes like this:
public class Indicator implements Serializable {
...
#OneToMany(mappedBy = "indicator",fetch=FetchType.LAZY)
private List<IndicatorAlternateLabel> indicatorAlternateLabels;
public List<IndicatorAlternateLabel> getIndicatorAlternateLabels() {
return indicatorAlternateLabels;
}
public void setIndicatorAlternateLabels(List<IndicatorAlternateLabel> indicatorAlternateLabels) {
this.indicatorAlternateLabels = indicatorAlternateLabels;
}
...
}
public class IndicatorAlternateLabel implements Serializable {
...
#ManyToOne(cascade = CascadeType.REFRESH, fetch = FetchType.EAGER)
#JoinColumn(name = "IndicatorID")
#XmlTransient
private Indicator indicator;
...
}
When I use them like this:
public MetricTypeDetail getMetricTypeDetail(Integer metricTypeId) {
Criteria crit = sessionFactory.getCurrentSession().createCriteria(Indicator.class, "sub")
.add(Restrictions.eq("number", metricTypeId))
.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).setCacheable(true);
crit.setMaxResults(1);
Indicator indicator=(Indicator) crit.uniqueResult();
MetricTypeDetail metricTypeDetail=new MetricTypeDetail(indicator);
List<IndicatorAlternateLabel> indicatorAlternateLabels = null;
indicatorAlternateLabels=indicator.getIndicatorAlternateLabels();
metricTypeDetail.setIndicatorAlternateLabels(indicatorAlternateLabels);
return metricTypeDetail;
}
This code returns an exception:
failed to lazily initialize a collection of role: com.porism.service.domain.Indicator.indicatorAlternateLabels, no session or session was closed
Any idea? I'm very new to Hibernate
Lazy exceptions occur when you fetch an object typically containing a collection which is lazily loaded, and try to access that collection.
You can avoid this problem by
accessing the lazy collection within a transaction.
Initalizing the collection using Hibernate.initialize(obj);
Fetch the collection in another transaction
Use Fetch profiles to select lazy/non-lazy fetching runtime
Set fetch to non-lazy (which is generally not recommended)
Further I would recommend looking at the related links to your right where this question has been answered many times before. Also see Hibernate lazy-load application design.
It's possible that you're not fetching the Joined Set. Be sure to include the set in your HQL:
public List<Node> getAll() {
Session session = sessionFactory.getCurrentSession();
Query query = session.createQuery("FROM Node as n LEFT JOIN FETCH n.nodeValues LEFT JOIN FETCH n.nodeStats");
return query.list();
}
Where your class has 2 sets like:
public class Node implements Serializable {
#OneToMany(fetch=FetchType.LAZY)
private Set<NodeValue> nodeValues;
#OneToMany(fetch=FetchType.LAZY)
private Set<NodeStat> nodeStats;
}
Try swich fetchType from LAZY to EAGER
...
#OneToMany(fetch=FetchType.EAGER)
private Set<NodeValue> nodeValues;
...
But in this case your app will fetch data from DB anyway.
If this query very hard - this may impact on performance.
More here:
https://docs.oracle.com/javaee/6/api/javax/persistence/FetchType.html
==> 73
as suggested here solving the famous LazyInitializationException is one of the following methods:
(1) Use Hibernate.initialize
Hibernate.initialize(topics.getComments());
(2) Use JOIN FETCH
You can use the JOIN FETCH syntax in your JPQL to explicitly fetch the child collection out. This is somehow like EAGER fetching.
(3) Use OpenSessionInViewFilter
LazyInitializationException often occurs in the view layer. If you use Spring framework, you can use OpenSessionInViewFilter. However, I do not suggest you to do so. It may leads to a performance issue if not used correctly.
Related
First, here are my entities.
Player :
#Entity
#JsonIdentityInfo(generator=ObjectIdGenerators.UUIDGenerator.class,
property="id")
public class Player {
// other fields
#ManyToOne
#JoinColumn(name = "pla_fk_n_teamId")
private Team team;
// methods
}
Team :
#Entity
#JsonIdentityInfo(generator=ObjectIdGenerators.UUIDGenerator.class,
property="id")
public class Team {
// other fields
#OneToMany(mappedBy = "team")
private List<Player> members;
// methods
}
As many topics already stated, you can avoid the StackOverflowExeption in your WebService in many ways with Jackson.
That's cool and all but JPA still constructs an entity with infinite recursion to another entity before the serialization. This is just ugly ans the request takes much longer. Check this screenshot : IntelliJ debugger
Is there a way to fix it ? Knowing that I want different results depending on the endpoint. Examples :
endpoint /teams/{id} => Team={id..., members=[Player={id..., team=null}]}
endpoint /members/{id} => Player={id..., team={id..., members=null}}
Thank you!
EDIT : maybe the question isn't very clear giving the answers I get so I'll try to be more precise.
I know that it is possible to prevent the infinite recursion either with Jackson (#JSONIgnore, #JsonManagedReference/#JSONBackReference etc.) or by doing some mapping into DTO. The problem I still see is this : both of the above are post-query processing. The object that Spring JPA returns will still be (for example) a Team, containing a list of players, containing a team, containing a list of players, etc. etc.
I would like to know if there is a way to tell JPA or the repository (or anything) to not bind entities within entities over and over again?
Here is how I handle this problem in my projects.
I used the concept of data transfer objects, implemented in two version: a full object and a light object.
I define a object containing the referenced entities as List as Dto (data transfer object that only holds serializable values) and I define a object without the referenced entities as Info.
A Info object only hold information about the very entity itself and not about relations.
Now when I deliver a Dto object over a REST API, I simply put Info objects for the references.
Let's assume I deliever a PlayerDto over GET /players/1:
public class PlayerDto{
private String playerName;
private String playercountry;
private TeamInfo;
}
Whereas the TeamInfo object looks like
public class TeamInfo {
private String teamName;
private String teamColor;
}
compared to a TeamDto
public class TeamDto{
private String teamName;
private String teamColor;
private List<PlayerInfo> players;
}
This avoids an endless serialization and also makes a logical end for your rest resources as other wise you should be able to GET /player/1/team/player/1/team
Additionally, the concept clearly separates the data layer from the client layer (in this case the REST API), as you don't pass the actually entity object to the interface. For this, you convert the actual entity inside your service layer to a Dto or Info. I use http://modelmapper.org/ for this, as it's super easy (one short method call).
Also I fetch all referenced entities lazily. My service method which gets the entity and converts it to the Dto there for runs inside of a transaction scope, which is good practice anyway.
Lazy fetching
To tell JPA to fetch a entity lazily, simply modify your relationship annotation by defining the fetch type. The default value for this is fetch = FetchType.EAGER which in your situation is problematic. That is why you should change it to fetch = FetchType.LAZY
public class TeamEntity {
#OneToMany(mappedBy = "team",fetch = FetchType.LAZY)
private List<PlayerEntity> members;
}
Likewise the Player
public class PlayerEntity {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "pla_fk_n_teamId")
private TeamEntity team;
}
When calling your repository method from your service layer, it is important, that this is happening within a #Transactional scope, otherwise, you won't be able to get the lazily referenced entity. Which would look like this:
#Transactional(readOnly = true)
public TeamDto getTeamByName(String teamName){
TeamEntity entity= teamRepository.getTeamByName(teamName);
return modelMapper.map(entity,TeamDto.class);
}
In my case I realized I did not need a bidirectional (One To Many-Many To One) relationship.
This fixed my issue:
// Team Class:
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<Player> members = new HashSet<Player>();
// Player Class - These three lines removed:
// #ManyToOne
// #JoinColumn(name = "pla_fk_n_teamId")
// private Team team;
Project Lombok might also produce this issue. Try adding #ToString and #EqualsAndHashCode if you are using Lombok.
#Data
#Entity
#EqualsAndHashCode(exclude = { "members"}) // This,
#ToString(exclude = { "members"}) // and this
public class Team implements Serializable {
// ...
This is a nice guide on infinite recursion annotations https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion
You can use #JsonIgnoreProperties annotation to avoid infinite loop, like this:
#JsonIgnoreProperties("members")
private Team team;
or like this:
#JsonIgnoreProperties("team")
private List<Player> members;
or both.
I have a Hibernate query with Criteria.
What I would like to do is (just for one query) tell hibernate to ignore an existing #ManyToOne annotation.
that is because hibernate creates and Left join on other tables.
I could figure it how to do it.
I have found this 2 links which didn't solve my problem:
Hibernate: How to remove an entity to which none refers to anymore in ManyToOne?
What is the difference between DELETE_ORPHAN and DELETE?
If you have such mapping:
//Parent
public class A {
...
}
//Child
public class B {
private A parent; //Many to one
...
}
Please try something like this:
Criteria q = ....;
q.setFetchMode("parent", FetchMode.SELECT);
....
I'm trying to set up a bidirectional relationship using JPA. I understand that it's the responsability of the application to maintain both sides of the relationship.
For example, a Library has multiple Books. In the Library-entity I have:
#Entity
public class Library {
..
#OneToMany(mappedBy = "library", cascade = CascadeType.ALL)
private Collection<Book> books;
public void addBook(Book b) {
this.books.add(b);
if(b.getLibrary() != this)
b.setLibrary(this);
}
..
}
The Book-entity is:
#Entity
public class Book {
..
#ManyToOne
#JoinColumn(name = "LibraryId")
private Library library;
public void setLibrary(Library l) {
this.library = l;
if(!this.library.getBooks().contains(this))
this.library.getBooks().add(this);
}
..
}
Unfortunately, the collection at the OneToMany-side is null. So for example a call to setLibrary() fails because this.library.getBooks().contains(this) results in a NullPointerException.
Is this normal behavior? Should I instantiate the collection myself (which seems a bit strange), or are there other solutions?
Entities are Java objects. The basic rules of Java aren't changed just because there is an #Entity annotation on the class.
So, if you instantiate an object and its constructor doesn't initialize one of the fields, this field is initialized to null.
Yes, it's your responsibility to make sure that the constructor initializes the collection, or that all the methods deal with the nullability of the field.
If you get an instance of this entity from the database (using em.find(), a query, or by navigating through associations of attached entities), the collection will never be null, because JPA will always initialize the collection.
It seems that books type of Collection in Library class is not initilized. It is null;
So when class addBook method to add a book object to collection. It cause NullPointerException.
#OneToMany(mappedBy = "library", cascade = CascadeType.ALL)
private Collection<Book> books;
public void addBook(Book b) {
this.books.add(b);
if(b.getLibrary() != this)
b.setLibrary(this);
}
Initilize it and have a try.
Change
private Collection<Book> books;
To
private Collection<Book> books = new ArrayList<Book>();
Try to set the fetch type association property to eager on the OneToMany side. Indeed, you may leave this part (this.library.getBooks().add(this)) to be written within a session:
Library l = new Library();
Book b = new Book();
b.setLibrary(l);
l.getBooks().add(b);
I'm using Jersey and am expecitng a POST as an entity. However thst POST will also contain the UUID for one of its relationships:
Jersey Resource:
#POST
public WorkstationEntity save (WorkstationEntity workstationEntity) {
//WorkflowProcessEntity workflowProcessEntity = workflowProcessService.findByUUID();
workstationService.save(workstationEntity);
return workstationEntity;
}
How can I adjust the following mapping so it'll recognize the relationship and save correctly? Currently the workflow_process_id is NULL when it's saved and I have to query for the entity manually.
The JSON being posted is... {name: Workstation 1; workflow_process_id: 1}
private WorkflowProcessEntity workflowProcess;
#ManyToOne
#JoinColumn(name = "workflow_process_id", referencedColumnName = "id")
public WorkflowProcessEntity getWorkflowProcess() {
return workflowProcess;
}
public void setWorkflowProcess(WorkflowProcessEntity workflowProcess) {
this.workflowProcess = workflowProcess;
}
workstationService
#Transactional
public void save(WorkstationEntity workstationEntity) {
workstationRepository.save(workstationEntity);
}
Can you show code for workstationService? are you using Hibernatr or simple jdbc or any other orm tool?
I think inside workstationService.save(workstationEntity); you will need to attach workstationEntity to session (in case of Hibernate Hibernate Session). and then save it..
If I understand the problem it is that the returning json has a null id for the attached WorkstationProcessEntity id field. This is most likely a problem when you are trying to persist / merge the entity the transaction is not being committed before returning the detached entity. If you are using a persist make sure that you commit the transaction otherwise the id's will be null. Otherwise if you are using a merge this will commonly fix the problem.
protected T persist(T obj) {
EntityManager em = ThreadLocalPersistenceManager.getEntityManager();
EntityTransaction tx = em.getTransaction();
try {
if (! tx.isActive()) {
tx.begin();
}
em.persist(obj);
} finally {
if (!tx.getRollbackOnly()) {
tx.commit();
}
}
return obj;
}
The other likely cause is that your fetch is not set to eager so the datastore will only fetch the entity when it is accessed and by the time you are returning from the post the child entity is not attached. This is the most likely cause for your problem. What you should try is to access the workstation entitites getWorkflowProcess before closing the entity manager. Otherwise the attached entities will be null. Or add the FetchType.Eager annotation to fetch the child entities from the database when the parent is accessed.
I'm using Hibernate with jpa and I'm getting LazyInizializationException trying to do a very simple thing. I know what does LazyInizializationException means but I can't understand why it comes while i'm doing everything in the most common and simple way. This is the "one" side of the relationship:
#Entity
public class User implements Serializable{
#Id #GeneratedValue
private int idUser;
private String name;
private String surname;
private String username;
#OneToMany(mappedBy="user")
private List<Device> dev;
...getters and setters...
and this is the "Many" side:
#Entity
public class Device implements Serializable {
#Id #GeneratedValue
private int idDevice;
private String brand;
private String model;
#ManyToOne
#JoinColumn(name="user_fk")
private User user;
...getters and setters...
the jUnit test that throws the exception is:
#Test
public void testLazyUserSnd() {
User u = uDao.getUser(2);
List<Device> devList = u.getDev();
Device aDevice = devList.get(0); // <--- Here the exception is thrown
aDevice.getModel();
I made the relationship as explained in the Hibernate Documentation. Any hint? Am I making some big and stupid mistakes?
While #Xavi's answer is perfectly reasonable, you may not always want to load the devices for a user. If you don't, there are 2 ways of fixing this.
Create an additional method uDao.getUserWithDevices(id) and call that when you know you need devices, otherwise call the uDao.getUser(id).
Encapsulate the test method, and therefore any production code that uses the method, in a transaction. In other words keep the session open as long as you need to.
Personally I'd use the transaction method since as it allows more flexibility and allows JPA to lazy load whenever it needs to. See also http://community.jboss.org/wiki/OpenSessionInView for more interesting information around session lifecycle.
The exception is telling you that you're trying to get some of the lazy-loaded association's elements when the session is closed. Probably you should call u.getDev() or Hibernate.initialize(u.getDev()) inside the dao's method, when the hibernate session is still open.
Or, if you're using Criteria, you could also use setFetchMode to force eager fetching.
public User getUser(String id) {
Session session = getSession();
Criteria criteriaQuery = session.createCriteria(User.class);
criteriaQuery.add(Expression.eq("id", id));
criteriaQuery.setFetchMode("dev", FetchMode.JOIN);
return criteriaQuery.uniqueResult();
}
The entity is probably detached from the session (transaction context) when you access the relation. Try to enclose your test method in a transaction.