In this application we are developing, we noticed that a view was particularly slow. I profiled the view and noticed that there was one query executed by hibernate which took 10 seconds even if there only were two object in the database to fetch. All OneToMany and ManyToMany relations were lazy so that wasn't the problem. When inspecting the actual SQL being executed, I noticed that there were over 80 joins in the query.
Further inspecting the issue, I noticed that the problem was caused by the deep hierarchy of OneToOne and ManyToOne relations between entity classes. So, I thought, I'll just make them fetched lazy, that should solve the problem. But annotating either #OneToOne(fetch=FetchType.LAZY) or #ManyToOne(fetch=FetchType.LAZY) doesn't seem to work. Either I get an exception or then they are not actually replaced with a proxy object and thus being lazy.
Any ideas how I'll get this to work? Note that I do not use the persistence.xml to define relations or configuration details, everything is done in java code.
First off, some clarifications to KLE's answer:
Unconstrained (nullable) one-to-one association is the only one that can not be proxied without bytecode instrumentation. The reason for this is that owner entity MUST know whether association property should contain a proxy object or NULL and it can't determine that by looking at its base table's columns due to one-to-one normally being mapped via shared PK, so it has to be eagerly fetched anyway making proxy pointless. Here's a more detailed explanation.
many-to-one associations (and one-to-many, obviously) do not suffer from this issue. Owner entity can easily check its own FK (and in case of one-to-many, empty collection proxy is created initially and populated on demand), so the association can be lazy.
Replacing one-to-one with one-to-many is pretty much never a good idea. You can replace it with unique many-to-one but there are other (possibly better) options.
Rob H. has a valid point, however you may not be able to implement it depending on your model (e.g. if your one-to-one association is nullable).
Now, as far as original question goes:
A) #ManyToOne(fetch=FetchType.LAZY) should work just fine. Are you sure it's not being overwritten in the query itself? It's possible to specify join fetch in HQL and / or explicitly set fetch mode via Criteria API which would take precedence over class annotation. If that's not the case and you're still having problems, please post your classes, query and resulting SQL for more to-the-point conversation.
B) #OneToOne is trickier. If it's definitely not nullable, go with Rob H.'s suggestion and specify it as such:
#OneToOne(optional = false, fetch = FetchType.LAZY)
Otherwise, if you can change your database (add a foreign key column to owner table), do so and map it as "joined":
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name="other_entity_fk")
public OtherEntity getOther()
and in OtherEntity:
#OneToOne(mappedBy = "other")
public OwnerEntity getOwner()
If you can't do that (and can't live with eager fetching) bytecode instrumentation is your only option. I have to agree with CPerkins, however - if you have 80!!! joins due to eager OneToOne associations, you've got bigger problems then this :-)
To get lazy loading working on nullable one-to-one mappings you need to let hibernate do compile time instrumentation and add a #LazyToOne(value = LazyToOneOption.NO_PROXY) to the one-to-one relation.
Example Mapping:
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name="other_entity_fk")
#LazyToOne(value = LazyToOneOption.NO_PROXY)
public OtherEntity getOther()
Example Ant Build file extension (for doing the Hibernate compile time instrumentation):
<property name="src" value="/your/src/directory"/><!-- path of the source files -->
<property name="libs" value="/your/libs/directory"/><!-- path of your libraries -->
<property name="destination" value="/your/build/directory"/><!-- path of your build directory -->
<fileset id="applibs" dir="${libs}">
<include name="hibernate3.jar" />
<!-- include any other libraries you'll need here -->
</fileset>
<target name="compile">
<javac srcdir="${src}" destdir="${destination}" debug="yes">
<classpath>
<fileset refid="applibs"/>
</classpath>
</javac>
</target>
<target name="instrument" depends="compile">
<taskdef name="instrument" classname="org.hibernate.tool.instrument.javassist.InstrumentTask">
<classpath>
<fileset refid="applibs"/>
</classpath>
</taskdef>
<instrument verbose="true">
<fileset dir="${destination}">
<!-- substitute the package where you keep your domain objs -->
<include name="/com/mycompany/domainobjects/*.class"/>
</fileset>
</instrument>
</target>
Unless you are using Bytecode Enhancement, you cannot fetch lazily the parent-side #OneToOne association.
However, most often, you don't even need the parent-side association if you use #MapsId on the child-side:
#Entity(name = "PostDetails")
#Table(name = "post_details")
public class PostDetails {
#Id
private Long id;
#Column(name = "created_on")
private Date createdOn;
#Column(name = "created_by")
private String createdBy;
#OneToOne(fetch = FetchType.LAZY)
#MapsId
private Post post;
public PostDetails() {}
public PostDetails(String createdBy) {
createdOn = new Date();
this.createdBy = createdBy;
}
//Getters and setters omitted for brevity
}
With #MapsId, the id property in the child table serves as both Primary Key and Foreign Key to the parent table Primary Key.
So, if you have a reference to the parent Post entity, you can easily fetch the child entity using the parent entity identifier:
PostDetails details = entityManager.find(
PostDetails.class,
post.getId()
);
This way, you won't have N+1 query issues that could be caused by the mappedBy #OneToOne association on the parent side.
Here's something that has been working for me (without instrumentation):
Instead of using #OneToOne on both sides, I use #OneToMany in the inverse part of the relationship (the one with mappedBy). That makes the property a collection (List in the example below), but I translate it into an item in the getter, making it transparent to the clients.
This setup works lazily, that is, the selects are only made when getPrevious() or getNext() are called - and only one select for each call.
The table structure:
CREATE TABLE `TB_ISSUE` (
`ID` INT(9) NOT NULL AUTO_INCREMENT,
`NAME` VARCHAR(255) NULL,
`PREVIOUS` DECIMAL(9,2) NULL
CONSTRAINT `PK_ISSUE` PRIMARY KEY (`ID`)
);
ALTER TABLE `TB_ISSUE` ADD CONSTRAINT `FK_ISSUE_ISSUE_PREVIOUS`
FOREIGN KEY (`PREVIOUS`) REFERENCES `TB_ISSUE` (`ID`);
The class:
#Entity
#Table(name = "TB_ISSUE")
public class Issue {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected Integer id;
#Column
private String name;
#OneToOne(fetch=FetchType.LAZY) // one to one, as expected
#JoinColumn(name="previous")
private Issue previous;
// use #OneToMany instead of #OneToOne to "fake" the lazy loading
#OneToMany(mappedBy="previous", fetch=FetchType.LAZY)
// notice the type isnt Issue, but a collection (that will have 0 or 1 items)
private List<Issue> next;
public Integer getId() { return id; }
public String getName() { return name; }
public Issue getPrevious() { return previous; }
// in the getter, transform the collection into an Issue for the clients
public Issue getNext() { return next.isEmpty() ? null : next.get(0); }
}
The basic idea behing the XToOnes in Hibernate is that they are not lazy in most case.
One reason is that, when Hibernate have to decide to put a proxy (with the id) or a null,
it has to look into the other table anyway to join. The cost of accessing the other table in the database is significant, so it might as well fetch the data for that table at that moment (non-lazy behaviour), instead of fetching that in a later request that would require a second access to the same table.
Edited: for details, please refer to ChssPly76 's answer. This one is less accurate and detailed, it has nothing to offer. Thanks ChssPly76.
In native Hibernate XML mappings, you can accomplish this by declaring a one-to-one mapping with the constrained attribute set to true. I am not sure what the Hibernate/JPA annotation equivalent of that is, and a quick search of the doc provided no answer, but hopefully that gives you a lead to go on.
As already perfectly explained by ChssPly76, Hibernate's proxies don't help with unconstrained (nullable) one-to-one associations, BUT there is a trick explained here to avoid to set up instrumentation. The idea is to fool Hibernate that the entity class which we want to use has been already instrumented: you instrument it manually in the source code. It's easy! I've implemented it with CGLib as bytecode provider and it works (ensure that you configure lazy="no-proxy" and fetch="select", not "join", in your HBM).
I think this is a good alternative to real (I mean automatic) instrumentation when you have just one one-to-one nullable relation that you want to make lazy. The main drawback is that the solution depends on the bytecode provider you are using, so comment your class accurately because you could have to change the bytecode provider in the future; of course, you are also modifying your model bean for a technical reason and this is not fine.
This question is quite old, but with Hibernate 5.1.10, there are some new better comfortable solution.
Lazy loading works except for the parent side of a #OneToOne association. This is because Hibernate has no other way of knowing whether to assign a null or a Proxy to this variable. More details you can find in this article
You can activate lazy loading bytecode enhancement
Or, you can just remove the parent side and use the client side with #MapsId as explained in the article above. This way, you will find that you don’t really need the parent side since the child shares the same id with the parent so you can easily fetch the child by knowing the parent id
.
For Kotlin devs: To allow Hibernate to inherit from the #Entity types that you want to be lazy-loadable they have to be inheritable/open, which they in Kotlin by default are not. To work around this issue we can make use of the all-open compiler plugin and instruct it to also handle the JPA annotations by adding this to our build.gradle:
allOpen {
annotation("javax.persistence.Entity")
annotation("javax.persistence.MappedSuperclass")
annotation("javax.persistence.Embeddable")
}
If you are using Kotlin and Spring like me, you are most probably also using the kotlin-jpa/no-args and kotlin-spring/all-open compiler plugins already. However, you will still need to add the above lines, as that combination of plugins neither makes such classes open.
Read the great article of Léo Millon for further explanations.
Most efficient mapping of a one-to-one association
You can avoid all these problems and get rid of the foreign key column by using the same primary key value for both associated entities. You can do that by annotating the owning side of the association with #MapsId.
#Entity
public class Book {
#Id
#GeneratedValue
private Long id;
#OneToOne(mappedBy = "book", fetch = FetchType.LAZY, optional = false)
private Manuscript manuscript;
...
}
#Entity
public class Manuscript {
#Id
private Long id;
#OneToOne
#MapsId
#JoinColumn(name = "id")
private Book book;
...
}
Book b = em.find(Book.class, 100L);
Manuscript m = em.find(Manuscript.class, b.getId());
More Detail click on this url
If the relation must not be bidirectional then an #ElementCollection might be easier than using a lazy One2Many collection.
If the child entity is used readonly, then it's possible to simply lie and set optional=false.
Then ensure that every use of that mapped entity is preloaded via queries.
public class App {
...
#OneToOne(mappedBy = "app", fetch = FetchType.LAZY, optional = false)
private Attributes additional;
and
String sql = " ... FROM App a LEFT JOIN FETCH a.additional aa ...";
... maybe even persisting would work...
Related
I have a bit of a catch-22 that I need some help wrapping my head around. I have two EAR applications, 'Product' and 'Manufacturer'. I also have a 'Common' library jar with a bunch of shared code.To avoid circular dependencies I don't want either application to directly depend on the other. To do this, I have abstract #MappedSuperclass versions of my domain classes (also called Product and Manufacturer) in the common library. So far so good.
The problem comes when I need to define some relationships between the classes. The child version of Product has a 'primary manufacturer' property defined like this:
#ManyToOne
#JoinColumn(name = "primary_mfg_id")
protected common.domain.Manufacturer primaryMfg;
and a collection of manufacturers:
#ContainedIn
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "products_to_manufacturers", inverseJoinColumns = {
#JoinColumn(name = "manufacturer_id", referencedColumnName = "id") }, joinColumns = {
#JoinColumn(name = "product_id", referencedColumnName = "id") })
#OrderBy("name")
#AuditJoinTable(inverseJoinColumns = { #JoinColumn(name = "manufacturer_id", referencedColumnName = "id") })
protected Set<common.domain.Manufacturer> manufacturers;
If I try to deploy that I will get an error saying that #ManyToOne references an unknown entity type. That makes sense, Manufacturer is a MappedSuperClass not an entity. Unfortunately, I can't think of a way to solve this without major headaches. I could combine the parent and child Manufacturer classes, but that would require me to also move a good portion of the domain model for the Manufacturer application into the common jar. I could have a Product specific version of the Manufacturer class but that would require adding a DTYPE to the table that is totally meaningless outside of hibernate.
I feel like there must be a cleaner way to structure this code, but I'm all out of ideas. Any help would be appreciated.
Update:
I was able to work around the 'primaryMfg' property by changing it to an id and looking up the manufacturer via the entity manager when I need the full object. Unfortunately, I can't figure out a way to do that for the 'manufacturers' property so I am still stuck.
Update 2:
Using the ORM mapping solved the compile issues, but there are still runtime problems. Those issues go beyond the scope of the original question so I have posted another question here: Getting An Instance Of A Mapped Superclass With Hibernate EntityManager
Turns out the solution was staring me in the face the whole time. While I was looking at my orm.xml file I happened to notice some commented out code that I must have disabled during migration. I uncommented it and the problem went away. For anyone else who happens to run into this, the snippet was:
<entity class="com.tura.common.domain.Manufacturer" name="Manufacturer">
<table name="manufacturers" />
</entity>
By adding that in the orm.xml of Product, it is telling hibernate to treat the parent class as an entity.
On a bidirectional relationship beetwen two entities (a ControlTable made up of ControlSteps), i'm simply trying by different ways to request a ControlTable by knowing the collection ControlSteps of it. I know that it's not recommended to have this bidirectionnal mapping but i need to know each childs of a parent, and the parent for each child.
I configured it like this in ControlTable class:
#OneToMany(mappedBy = "controlTable",cascade = CascadeType.ALL, fetch=FetchType.EAGER)
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Fetch(FetchMode.JOIN)
private Set<ControlStep> controlSteps;
And like this for ControlStep class :
#ManyToOne(optional=false, fetch=FetchType.LAZY)
#JoinColumn(name="ctrl_table_id", referencedColumnName = "id")
private ControlTable controlTable;
When i use the default JPA query findAll(), it's not working to get the list of ControlTables (or only one) because it's requesting recursively the parent in the child's parent (infinite response).
In another way, itried to put all in LAZY loading, with an HQL query fetching the childs, but the result is the same.
Do you have any idea of how to get these collections without problems?
Thank you very much by advance
Found it. The problem was Spring Data Rest and JSON transformation, for more details :
Infinite Recursion with Jackson JSON and Hibernate JPA issue
I have three tables with simple structure:
pub [id, name]
days [id, name]
pub_days [id, pub_id, days_id]
For some unholy reason, somebody thought that compound identity for pub_days table (that would be pub_id + days_id) is not enough and added own primary key. I can't change it now, other and larger system depends on that. #sigh
I am trying to map this to Hibernate with standard #ManyToMany JPA annotation like so (I omitted getters, setters, #Entitiy annotations and other clutter):
class Pub {
#ManyToMany(cascade = {CascadeType.ALL})
#JoinTable(name = "pub_days",
joinColumns = {#JoinColumn(name = "pub_id")},
inverseJoinColumns = {#JoinColumn(name = "days_id")})
#OrderBy("id")
private List<Day> pubOpeningDays;
}
class Day {
#Id Long id;
String name.
}
when I execute following code:
Day day = repository.find(Day.class, id);
pub.getPubOpeningDays().add(day);
repository.persist(pub);
I get this error:
ERROR: ORA-01400: cannot insert NULL into ("PUB"."pub_days"."id")
Sadly, that makes perfect sense, because I haven't mapped that ID anywhere. The thing is, I don't even want to. I want it to be generated, but not sure how do I overcome this issue with #ManyToMany mapping. Any ideas?
What you can do is like I mentioned in my comments you can create a separate entity CD which will in turn connect with two classes A and B, Now relationship would be many to many between A and B, and hence A (many to many) CD (many to many) B. Now as per your requirement whenever you need to fetch the instance of A or B, what you can do is simply fire a query in the DB with proper parameters i.e id of a or id of b this will help you get your required result.
I only see two choices, either you change your mapping to a list of PubDay as samwise-gamgee told you in the comments or you add a trigger on insert on table pub_days which set a value for the column id if it is null (it could be using a sequence). But this approach depends on the features supported by your DB.
I am currently trying to use inheritance within Hibernate and came across InheritanceType.JOINED. I like the idea of concentrating all data in one table and sharing IDs rather than having duplicate columns in all the sub type tables (#MappedSuperClass). But Hibernate automatically generates indexes on my sub class tables on the id column like FK_idx3wiwdm8yp2qkkddi726n8o everytime I initialize my Hibernate singleton. I noticed that by hitting the 64 key limit on my MySQL Table as the names are generated differently on every startup.
What is the proper way to handle this? Can this be fixed by annotations? What else could I try?
I know that there are countless similar Questions on SO but haven't been able to identify one solving my specific problem.
I am not going to disable hbm2ddl.auto during dev mode.
I am using MyISAM. There are no actual Foreign Keys. This is why Hibernate generates default indexes, I think. Anyway, the problem would be identical with InnoDB and real Foreign Keys as the names would still be quite random. Or maybe Hibernate would actually check for existence in this case. I don't really see, why it does not do this on MyISAM tables.
As I hit similar problems before, the solution could also be to specify a name for that single-column index. But how?
Super Class: FolderItem
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class FolderItem implements Comparable<FolderItem>
{
#Id
#GeneratedValue
protected int id;
protected String name;
#OneToOne
#ForeignKey(name = "fkParent")
protected Folder parent;
...
}
Sub Class: Folder
#Entity
public class Folder extends FolderItem
{
#OneToMany(mappedBy = "parent")
#OrderBy(value = "sortOrder")
private List<FolderItem> children;
...
}
What I tried
add #Index to FolderItem.id - this created an index on the FolderItem table as one would expect, but didn't affect the Folder table
copy protected int id; to Folder and tried to add an #Index to it, which resulted in an Exception similar to "duplicate definition of ID"
add #Table(appliesTo = "Folder", indexes = { #Index(name = "fkId", columnNames = { "id" }) }) to Folder class, which actually created my specified index as expected, but still created it's own FK_9xcia6idnwqdi9xx8ytea40h3 which is identical to mine, except for the name
Try #PrimaryKeyJoinColumn(name = "foler_item_id") annotation for Folder class.
I have the following situation:
One Destination can have Many Aliases
Logically: I would like Destination to be the owner of this relationship, since if there was no destination, alias would not hold any meaning.
However, the database schema is like this:
DestinationAlias has idDestination as the Foreign Key and thus the #JoinColumn in hibernate annotation would be on DestinationAlias:
Destination:
#OneToMany(fetch=FetchType.LAZY, cascade={CascadeType.ALL}, mappedBy = "mainCity")
public Set<DestinationAlias> getAliases() {
return aliases;
}
DestinationAlias:
#ManyToOne(fetch=FetchType.LAZY, cascade={CascadeType.ALL})
#JoinColumn(name="IDDESTINATION", nullable=false)
public Destination getMainCity() {
return mainCity;
}
From this definition, DestinationAlias is the owner of this relationship since the mappedBy is an attribute on Destination class.
Does hibernate require me to follow the database schema and mark entity as the owner of the relationship or can it be done based on Logical reasons ?
Ownership affects how hibernate would model the underlying database tables. If you omit the mappedBy attribute, hibernate would generate a join-table (like for an M-M relationship) between Destination and DestinationAlias, instead of a simple join column. Other than that, I'm curious as to what your actual issue is here. Is this purely cosmetic, or are there functional issues in play for you here?
EDIT: as this seems to be a purely cosmetic issue, my advice is to just accept the Hibernate semantic. While I agree that it might appear counter-intuitive that Hibernate would call DestinationAlias the "owner", that is just the nomenclature Hibernate decided on. It's not unique either and it comes from the notion that the "owner" is where the join-column resides.
Don't waste time trying to force hibernate to conform to your definitions when you don't have to.