I have this class:
public class Tenant {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#NaturalId
#Column(name = "name", nullable = false, updatable = false, unique = true)
private String name;
#OneToMany(mappedBy = "tenant", cascade = CascadeType.ALL, orphanRemoval = true)
private List<User> users;
#OneToMany(mappedBy = "tenant", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Role> roles;
#OneToOne(mappedBy = "tenant", cascade = CascadeType.ALL, orphanRemoval = true, optional = false)
private TenantLimits limits;
}
Where of course all referenced classes are entities. I'm able to create, update and retrieve everything from here, but since private TenantLimits limits; refers to an entity created after Tenant was created many of my Tenants elements don't contains any matched TenantLimits.
So my question is: How can I insert in the database a value in TenantLimits if is null when I'm going to retrieve Tenant? In Java I can easily check (of course) if the property is null and insert manually foreach retrieve, but since the retrieve of this entity is present in different places in my code I'd to have something that manage this automatically if exists
You are telling Hibernate that Tenant.limits cannot be null by mapping it with "optional=false". It will 100% adhere to this definition. It will only create valid tenants and I assume it will throw you exceptions if the state of the database is invalid. It won't let you fix your data.
You should fix the state of your database by any other means than with this particular Hibernate mapping.
You might have to migrate in 2 steps. Let's say, make the mapping "optional=true". Then you can run a Java process to fix your data (maybe by using an entity listener). Then - change it back to "optional=false".
Related
I have a list with 25 MyApplication objects that I want to save using hibernate/JPA. This is done with the following method:
MyApplicationRepository.saveAll(myAppList);
However I noticed that hibernate creates over 60.000 MyApplication objects (close to the total amount of records already in database for this entity) while inserting/updating this list of 25 in the database. I don't have a lot of hibernate experience which leads me to believe I created a inefficient entity relations. A part of the MyApplication class:
public class MyApplication {
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "APPLICATION_CATEGORY", joinColumns = {
#JoinColumn(name = "applicationid", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "categoryid",
nullable = false, updatable = false) })
private Set<Category> categorySet;
#OneToMany(mappedBy = "myApplication",
cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Screenshot> screenshotSet;
}
Category class (one example of multiple of the many to many relations of MyApplication):
public class Category {
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "categorySet")
private Set<MyApplication> myApplicationSet;
}
Screenshot class:
public class Screenshot {
#ManyToOne
#JoinColumn(name = "applicationid")
private MyApplication myApplication;
}
What did I do wrong that resulted in Hibernate creating so many instances of MyApplication when saving?
Note 1: In the end all of the information of MyApplication and the information of it's categories and screenshots is saved correctly in the database.
Note 2: It's important that not only MyApplication is saved but also everything from all its categories and screenshots as well.
I was able to fix the issue. The problem was caused by the bidirectional nature of the manyToMany relation. This resulted in the category also querying all the applications from the database before saving. As this is not what I want, I resolved the issue by turning it into a unidirectional relation by removing the myApplicationSet from the Category class. Now only 25 MyApplication instances are constructed to save 25 applications and my memory usage remains stable.
Update
I'd like to note that #sainr's answer Converting Hibernate proxy to real entity object does solve the problem. But the issue behind the scene is my SiteEntity having a final modifier of it's setControllerEntity and getControllerEntity, which I didn't raise in my question. And I apologize.
Remove the final modifier. Then Hibernate can initialize the proxy objects just fine.
The explanation can be found in another answer on Stack Overflow.
I have three entities as following
#Entity
#Table(name = "controller")
public class ControllerEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(nullable = false, updatable = false)
private long id;
}
#Entity
#Table(name = "site")
public class SiteEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(nullable = false)
private long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "controller_id", nullable = false)
private ControllerEntity controllerEntity;
}
#Entity
#Table(name = "device")
public class DeviceEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(nullable = false)
private long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "site_id", nullable = true)
private SiteEntity siteEntity;
}
After I found the device entity, I try to get the controllerEntity directly from it.
final DeviceEntity deviceEntity1 = deviceRepository.findOne(1L);
System.out.println(deviceEntity1.getSiteEntity().getControllerEntity().getId());
But it results a java.lang.NullPointerException which is caused by the null controllerEntity in the siteEntity.
Also, even if I tried to use siteRepositoy to fetch the siteEntity again. the controllerEntity of it is still null.
After I removed the fetch = FetchType.LAZY from both the DeviceEntity and SiteEntity, NPE doesn't happen anymore.
But it seems odd and doesn't make sense. Can I use FetchType.LAZY while expecting hibernate fetch the correct value?
Thanks.
To give you an access to the field declared with FetchType.LAZY, Hibernate constructs a proxy with CGLIB. Consequently, when you're calling a getter for such field (in your case, getSiteEntity() or getControllerEntity()), you're not accessing the field value directly -- instead, the call is passed to the proxy object of Hibernate. In turn, Hibernate tries to load the actual value from the data store and in order to do this, it would require an active Hibernate session to access the DB. Most likely, in your case, the Hibernate session is already closed and such lazy load fails, giving you an effectively null value of the field.
There are basically two ways to solve this:
Use FetchType.EAGER that would load all field values along with the holding object DeviceEntity
Transform a proxy object into a real object (check Converting Hibernate proxy to real entity object) and access it in a regular way
Think about it, whether you really need a lazy load in your case. If you are not storing plenty of heavy objects in child fields to load them on demand, probably switching to FetchType.EAGER will be the easiest way.
Hope that helps.
Hibernate work with primitive types sometimes not very well. Try to replace
private long id
to
private Long id
For primary keys in Hibernate it is better to use wrapper classes instead of primitive types.
I have defined a one to One relationship between my objects. I have set
#OneToOne(cascade = CascadeType.ALL)
I am using CrudRepository to read the values of one the classes. However I don't want the dependent object to retrieved unless I fetch it explicitly. In the below example I don't want Order to be retrieved when I fetch customer. I guess this run an additional query in the background to fetch order details , which I want to avoid.
public Customer{
private String id;
private String fname;
#OneToOne(cascade = CascadeType.ALL)
private Order order;
}
Add fetch = FetchType.LAZY to the annotation
resulting in
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
Posting this here as I wasn't seeing much interest here: http://www.java-forums.org/jpa/96175-openjpa-one-many-within-one-many-merge-problems.html
Trying to figure out if this is a problem with OpenJPA or something I may be doing wrong...
I'm facing a problem when trying to use OpenJPA to update an Entity that contains a One to Many relationship to another Entity, that has a One to Many relationship to another. Here's a quick example of what I'm talking about:
#Entity
#Table(name = "school")
public class School {
#Column(name = "id")
protected Long id;
#Column(name = "name")
protected String name;
#OneToMany(mappedBy = "school", orphanRemoval = true, cascade = CascadeType.ALL)
protected Collection<ClassRoom> classRooms;
}
#Entity
#Table(name = "classroom")
public class ClassRoom {
#Column(name = "id")
protected Long id;
#Column(name = "room_number")
protected String roomNumber;
#ManyToOne
#JoinColumn(name = "school_id")
protected School school;
#OneToMany(mappedBy = "classRoom", orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
protected Collection<Desk> desks;
}
#Entity
#Table(name = "desk")
public class Desk {
#Column(name = "id")
protected Long id;
#ManyToOne
#JoinColumn(name = "classroom_id")
protected ClassRoom classRoom;
}
In the SchoolService class, I have the following update method:
#Transactional
public void update(School school) {
em.merge(school);
}
I'm trying to remove a Class Room from the School. I remove it from the classRooms collection and call update. I'm noticing if the Class Room has no desks, there are no issues. But if the Class Room has desks, it throws a constraint error as it seems to try to delete the Class Room first, then the Desks. (There is a foreign key constraint for the classroom_id column)
Am I going about this the wrong way? Is there some setting I'm missing to get it to delete the interior "Desk" instances first before deleting the Class Room instance that was removed?
Any help would be appreciated. If you need any more info, please just let me know.
Thanks,
There are various bug reports around FK violations in OpenJPA when cascading remove operations to child entities:
The OpenJPA FAQ notes that the following:
http://openjpa.apache.org/faq.html#reorder
Can OpenJPA reorder SQL statements to satisfy database foreign key
constraints?
Yes. OpenJPA can reorder and/or batch the SQL statements using
different configurable strategies. The default strategy is capable of
reordering the SQL statements to satisfy foreign key constraints.
However ,you must tell OpenJPA to read the existing foreign key
information from the database schema:
It would seem you can force the correct ordering of the statements by either setting the following property in your OpenJPA config
<property name="openjpa.jdbc.SchemaFactory"> value="native(ForeignKeys=true)"/>
or by adding the org.apache.openjpa.persistence.jdbc.ForeignKey annotation to the mapping:
#OneToMany(mappedBy = "classRoom", orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#org.apache.openjpa.persistence.jdbc.ForeignKey
protected Collection<Desk> desks;
See also:
https://issues.apache.org/jira/browse/OPENJPA-1936
I am having following problem. I have a user entity that has a many to many relationship with other user entities. Hence I want to make a self-join with manytomany annotation. This relation is based on already existing table that is used across the system so I cannot make changes to the DB at all. So we have 2 tables User(Id, ShortName) and UserLink(ParentId, ChildId).
The annotation of ID is assigned to ShortName, but the actual keys in both User and UserLink are ID from User and ParentId and ChildId from UserLink.
I am trying to handle this the following way from the User entity:
#Id
#Column(name = "ShortName")
private String shortName;
#Column(name = "Id")
private long id;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "UserLink",
joinColumns = { #JoinColumn(name = "ParentId", referencedColumnName = "Id") },
inverseJoinColumns = { #JoinColumn(name = "ChildId", referencedColumnName = "Id") })
private Collection<UserEntity> children;
Since the key in the User entity is on the ShortName field, I have to specify the "Id" as referenced column name param. If I don't do that, it takes the ShortName as the key and doesn't fetch any data.
When I try to do this the way I showed above, I get the following exception:
Caused by: org.hibernate.MappingException: Duplicate property mapping of **_entity_UserEntity_children found in **.entity.UserEntity
at org.hibernate.mapping.PersistentClass.checkPropertyDuplication(PersistentClass.java:486)
at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:476)
at org.hibernate.mapping.RootClass.validate(RootClass.java:268)
at org.hibernate.cfg.Configuration.validate(Configuration.java:1287)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1729)
at org.hibernate.ejb.EntityManagerFactoryImpl.<init>(EntityManagerFactoryImpl.java:84)
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:904)
... 81 more
Do you have any idea how this could be fixed? One idea is that I could change the #Id in the entity and move it to the Id property that is used for joins, but this would need a lot of effort to rewrite bad existing code.
Anyway, is it possible to make a self-join manytomany on columns that are not keys?
Cheers
Adam