@ManyToOne (fetch = FetchType.LAZY) does not work with a non-primary key column

I'm having trouble getting the @ManyToOne association loaded by lazilly. I use fetch = LAZY, but it does not work when the connection is not made in the primary key column.

I know that this question has already been asked , but I think I have not answered it properly, so I provide detailed information to clarify the problem.

This is my model:

DummyB -> DummyA 

These are the tables:

 create table dummyA ( id number(18,0), --pk name varchar2(20) -- unique field ); create table dummyB ( id number(18,0), dummya_id number(18,0), dummya_name varchar2(20) ); 

And these are the entities:

 @Entity public class DummyA implements Serializable { private Long id; private String name; @Id public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } @Entity public class DummyB implements Serializable { private Long id; private DummyA dummyA; @Id public Long getId() { return id; } public void setId(Long id) { this.id = id; } /* Case 1: mapping DummyB -> DummyA by DummyA NON primary key (field name) */ // @ManyToOne(fetch = FetchType.LAZY) // @JoinColumn(name = "dummya_id") // public DummyA getDummyA() { // return dummyA; // } /* Case 2: mapping DummyB -> DummyA by DummyA primary key */ @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "dummya_name", referencedColumnName = "name") @LazyToOne(LazyToOneOption.PROXY) public DummyA getDummyA() { return dummyA; } public void setDummyA(DummyA dummyA) { this.dummyA = dummyA; } } 

Note. The getDummyA method in the DummyB entity is a duplicate to try two cases for combining objects.

Case 1: mapping DummyB -> DummyA using the primary key DummyA

@ManyToOne (fetch = FetchType.LAZY) @JoinColumn (name = "dummya_id")

This works fine, only one query is executed to retrieve DummyB objects.

Case 2: mapping DummyB → DummyA by DummyA NON primary key (field name)

@ManyToOne (fetch = FetchType.LAZY) @JoinColumn (name = "dummya_name", referenceColumnName = "name")

The same dummy choiceB is executed, but immediately after choosing dummyA, filtering is performed using name =? to get related object A.

I use very simple jUnit to do the filtering:

 public class DummyTest { @Autowired HibernateTransactionManager transactionManager; @Test @Transactional public void testFindDummyB() throws DAOException { Long idDummyB = 2L; Session session = getCurrentHibernateSession(); List lst = session.createCriteria(DummyB.class) .add(Restrictions.eq("id", idDummyB)).list(); assertTrue(lst.size() > 0); } private Session getCurrentHibernateSession() { return this.transactionManager.getSessionFactory().getCurrentSession(); } } 

My libraries:

  • org.hibernate: core hibernation: jar: 4.2.17.Final: assemble
  • org.hibernate.common: sleeping-Wikimedia Commons annotations: jar: 4.0.2. Final: compile
  • org.hibernate.javax.persistence: hibernation-JPA-2.0-API: bank: 1.0.1.Final: collect
  • org.hibernate: hibernate validator: jar: 4.3.2.Final: provided

Other things I've already tried:

  • Adding the hiberante @LazyToOne method to getDummyA () has no effect.

    @LazyToOne (LazyToOneOption.PROXY) @ManyToOne (fetch = FetchType.LAZY, optional = true) @JoinColumn (name = "dummya_name", linkColumnName = "name") @LazyToOne (LazyToOYOption)

  • Creating a foreign key from the DummyB table in dummyA (and the unique constraint in the dummya.name field) has no effect.

  • Adding @Column (unique = true) to the DummyA method getName () did not.
  • Set the option = true or false as suggested here .
  • Trying to force lazy loading to use setFetchMode in the criteria does not work, DummyA selection continues to be executed.

    List lst = session.createCriteria (DummyB.class) .add (Restrictions.eq ("id", idDummyB)). setFetchMode ("dummyA", FetchMode.SELECT) .list ();

I can’t find the point at which it refers to this behavior in the Hibernate docs, so I wonder if something is wrong in my annotations or I encountered a Hibernate error.

Can anyone tell?

UPDATED by md-dev request: To make this more clear:

Is this the expected behavior or is it a mistake? if this is the expected behavior, where is it documented?

Thanks.

+5
source share
4 answers

See the same behavior with Hibernate 5.0.4. @ManyToOne (with OneToMany ) and Lazy works fine if the join column is the primary key. If this is not the case, lazy load breaks and Hibernate readily retrieve all ManyToOne every time an object is created. This can be catastrophically slow if you do Criteria.list() for, say, 1000 entries. What started as a single query for 1000 records can be inserted into 5000 queries to look forward to a lot of @ManyToOne through individual selections.

Absolutely nothing that I was able to check / modify, explained it in any way, and I can reliably reproduce it.

The solution that I had to implement in my application that uses non-PK for connections was to simply garbage @ManyToOne / @OneToMany annotation @OneToMany and manually record collections (caching results with a transition variable). This is much more, but the performance is much higher, given that some of my objects have 5 or 6 @ManyToOne objects, and they were all eagerly triggered by separate Hibernate selections.

Unfortunately, I cannot reorganize my scheme to satisfy this quirk in Hibernate. I am running a project involving Heroku Connect and joining between tables when combining data from Salesforce.com using the "sfid" column in a table that is not a primary key. The primary key is a single value that is unique to writing to the Heroku Postgres database and cannot be used for joining, since no other tables in the database belong to this primary key.

I assume this is a bug in Hibernate; nothing that I read or could not change in any way affected this behavior, and, as I already mentioned, I can make the system work exactly as expected if the connection columns are primary keys.

+3
source

If someone still has problems with this, we got it to work as follows:

 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "dummya_name", referencedColumnName = "name", insertable = false, updatable = false), @LazyToOne(LazyToOneOption.NO_PROXY) public DummyA getDummyA() { if (fieldHandler != null) { return (DummyA) fieldHandler.readObject(this, "dummyA", dummyA); } return dummyA; } public void setDummyA(DummyA dummyA) { if (fieldHandler != null) { this.dummyA = (DummyA ) fieldHandler.writeObject(this, "dummyA", this.dummyA, dummyA); return; } this.dummyA= dummyA; } @Override public void setFieldHandler(FieldHandler fieldHandler) { this.fieldHandler = fieldHandler; } @Override public FieldHandler getFieldHandler() { return fieldHandler; } 

Hibernate has been well explained with lazy loading for a one-time reverse workaround - how does it work?

+1
source

You cannot use the name as a join column because there is no unique restriction for this. Perhaps this leads to displaying ManyToMany instead of ManyToOne. I have no idea if the hibernation mode agreed, but in the end it will end with something unexpected. In addition, I do not see a use case for this. I advise you to always use the long identifier as the primary key and automatically display this field. Such special handling is necessary only if you have an unorthodox utility or should be compatible with legacy db.

0
source

The @peach solution worked for me. A little was not mentioned much:

 @Entity public class DummyB implements Serializable { 

it should be

 @Entity public class DummyB implements Serializable, FieldHandled { 

and if you use @JsonIgnoreProperties you must add fieldHandler

 @JsonIgnoreProperties({"hibernateLazyInitializer", "handler", "fieldHandler"}) 
0
source

All Articles