问题
I am attempting to query a person by id
and in my schema below i have a @OneToMany
reference between Address
and Person
and a ManyToOne
reference between person and School. I use a named query and query hints to get the best performing query for this simple task but looking at the stack trace (Relevant parts posted), EclipseLink generates three Select statements when all i want is the person object by id? Using eager fetching was killing performance and so my question is how can i simply create a query using JPQL that gets me a single person object by ID without referencing other classes?
@NamedQuery(
name="findPersonById",
query="SELECT p FROM Person as p WHERE p.id = :id",
hints={@QueryHint(name="eclipselink.batch.type", value="JOIN"),
@QueryHint(name="eclipselink.batch", value="p.address")
}
)
public class Person {
@Id
@TableGenerator(name = "TABLE_GEN", table = "PERSON_SEQUENCE_TABLE", pkColumnName = "SEQ_NAME", valueColumnName = "SEQ_COUNT", pkColumnValue = "PER_SEQ", allocationSize = 1, initialValue = 10000)
@GeneratedValue(strategy = GenerationType.TABLE, generator = "TABLE_GEN")
@Column(name = "personID")
private Long id;
@ManyToOne(cascade = CascadeType.PERSIST,fetch = FetchType.LAZY)
@JoinColumn(name = "addressID")
private Address address;
@ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
@JoinColumn(name = "schoolID")
private School school;
}
The Address Entity
public class Address {
@Id
@TableGenerator(name = "ADDRESS_TABLE_GEN", table = "ADDRESS_SEQUENCE_TABLE", pkColumnName = "SEQ_NAME", valueColumnName = "SEQ_COUNT", pkColumnValue = "ADDR_SEQ", allocationSize = 1, initialValue = 1)
@GeneratedValue(strategy = GenerationType.TABLE, generator = "ADDRESS_TABLE_GEN")
@Column(name = "addressID")
private Long id;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "address")
private Set<Person> persons = new HashSet<Person>();
Named Query implementation
public Person getPersonQueryBatch(Long id){
EntityManager entityManager = factory.createEntityManager();
Person person = null;
try {
List<Person> results = entityManager.createNamedQuery("findPersonById")
.setParameter("id", id)
.getResultList();
if(!results.isEmpty()){
// ignores multiple results
person = results.get(0);
}
} catch (NoResultException e) {
e.printStackTrace();
}
entityManager.close();
return person;
}
Stack Trace: The last bit where it searches for an addressID
that equals 1 is hanging
[EL Finest]: jpa: 2012-06-26 20:47:29.78--ServerSession(1259621282)--Thread(Thread[main,5,main])--Begin deploying Persistence Unit persistenceUnit; session file:/Users/warz07/Documents/workspace-sts-2.8.0.RELEASE/dugsimanager/target/classes/_persistenceUnit; state Deployed; factoryCount 2
[EL Finest]: jpa: 2012-06-26 20:47:29.781--ServerSession(1259621282)--Thread(Thread[main,5,main])--End deploying Persistence Unit persistenceUnit; session file:/Users/warz07/Documents/workspace-sts-2.8.0.RELEASE/dugsimanager/target/classes/_persistenceUnit; state Deployed; factoryCount 2
[EL Finer]: connection: 2012-06-26 20:47:29.787--ServerSession(1259621282)--Thread(Thread[main,5,main])--client acquired: 67158058
[EL Finer]: transaction: 2012-06-26 20:47:29.788--ClientSession(67158058)--Thread(Thread[main,5,main])--acquire unit of work: 1296566131
[EL Finest]: query: 2012-06-26 20:47:29.788--UnitOfWork(1296566131)--Thread(Thread[main,5,main])--Execute query ReadObjectQuery(name="findPersonById" referenceClass=Person sql="SELECT personID, TYPE, DATEADDED, FIRSTNAME, LASTNAME, MIDDLENAME, ACTIVE, BIRTHDAY, EMAILADDRESS, GENDER, IMAGEPATH, MARITAL, PRIMARYTELEPHONE, SECONDARYTELEPHONE, version, addressID, schoolID, ETHNICITY, HISPANIC, MAJOR, NATIVELANGUAGE, RELIGIOUSAFFILIATION, studentId FROM PERSON WHERE (personID = ?)")
[EL Finest]: connection: 2012-06-26 20:47:29.789--ServerSession(1259621282)--Connection(65769329)--Thread(Thread[main,5,main])--Connection acquired from connection pool [read].
[EL Finest]: connection: 2012-06-26 20:47:29.789--ServerSession(1259621282)--Thread(Thread[main,5,main])--reconnecting to external connection pool
[EL Fine]: sql: 2012-06-26 20:47:29.79--ServerSession(1259621282)--Connection(1578517945)--Thread(Thread[main,5,main])--SELECT personID, TYPE, DATEADDED, FIRSTNAME, LASTNAME, MIDDLENAME, ACTIVE, BIRTHDAY, EMAILADDRESS, GENDER, IMAGEPATH, MARITAL, PRIMARYTELEPHONE, SECONDARYTELEPHONE, version, addressID, schoolID, ETHNICITY, HISPANIC, MAJOR, NATIVELANGUAGE, RELIGIOUSAFFILIATION, studentId FROM PERSON WHERE (personID = ?)
bind => [10000]
[EL Finest]: connection: 2012-06-26 20:47:29.798--ServerSession(1259621282)--Connection(65769329)--Thread(Thread[main,5,main])--Connection released to connection pool [read].
2012-06-26 20:47:29,802 [main] DEBUG org.springframework.beans.factory.annotation.InjectionMetadata - Processing injected method of bean 'org.bixin.dugsi.domain.Student': PersistenceElement for transient javax.persistence.EntityManager org.bixin.dugsi.domain.Person.entityManager
2012-06-26 20:47:29,803 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'entityManagerFactory'
[EL Finest]: query: 2012-06-26 20:47:29.821--ServerSession(1259621282)--Thread(Thread[main,5,main])--Execute query ReadAllQuery(name="address" referenceClass=Address sql="SELECT DISTINCT t0.addressID, t0.CITY, t0.COUNTRY, t0.STATE_US, t0.STREETADDRESS, t0.STREETADDRESS2, t0.version, t0.ZIPCODE FROM ADDRESS t0, PERSON t1 WHERE ((t0.addressID = t1.addressID) AND (t1.personID = ?))")
[EL Finest]: connection: 2012-06-26 20:47:29.821--ServerSession(1259621282)--Connection(420965983)--Thread(Thread[main,5,main])--Connection acquired from connection pool [read].
[EL Finest]: connection: 2012-06-26 20:47:29.821--ServerSession(1259621282)--Thread(Thread[main,5,main])--reconnecting to external connection pool
[EL Fine]: sql: 2012-06-26 20:47:29.822--ServerSession(1259621282)--Connection(1364143063)--Thread(Thread[main,5,main])--SELECT DISTINCT t0.addressID, t0.CITY, t0.COUNTRY, t0.STATE_US, t0.STREETADDRESS, t0.STREETADDRESS2, t0.version, t0.ZIPCODE FROM ADDRESS t0, PERSON t1 WHERE ((t0.addressID = t1.addressID) AND (t1.personID = ?))
bind => [10000]
[EL Finest]: connection: 2012-06-26 20:47:29.825--ServerSession(1259621282)--Connection(420965983)--Thread(Thread[main,5,main])--Connection released to connection pool [read].
2012-06-26 20:47:29,826 [main] DEBUG org.springframework.beans.factory.annotation.InjectionMetadata - Processing injected method of bean 'org.bixin.dugsi.domain.Address': PersistenceElement for transient javax.persistence.EntityManager org.bixin.dugsi.domain.Address.entityManager
2012-06-26 20:47:29,827 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'entityManagerFactory'
2012-06-26 20:47:29,830 [main] DEBUG org.springframework.beans.factory.annotation.InjectionMetadata - Processing injected method of bean 'org.bixin.dugsi.domain.Student': PersistenceElement for transient javax.persistence.EntityManager org.bixin.dugsi.domain.Person.entityManager
2012-06-26 20:47:29,831 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'entityManagerFactory'
[EL Finest]: transaction: 2012-06-26 20:47:29.832--UnitOfWork(1296566131)--Thread(Thread[main,5,main])--[EL Finest]: query: 2012-06-26 20:47:29.844--ServerSession(1259621282)--Thread(Thread[main,5,main])--Execute query ReadAllQuery(name="persons" referenceClass=Person )
[EL Finest]: connection: 2012-06-26 20:47:29.845--ServerSession(1259621282)--Connection(36219749)--Thread(Thread[main,5,main])--Connection acquired from connection pool [read].
[EL Finest]: connection: 2012-06-26 20:47:29.845--ServerSession(1259621282)--Thread(Thread[main,5,main])--reconnecting to external connection pool
[EL Fine]: sql: 2012-06-26 20:47:29.845--ServerSession(1259621282)--Connection(1007449342)--Thread(Thread[main,5,main])--SELECT personID, TYPE, DATEADDED, FIRSTNAME, LASTNAME, MIDDLENAME, ACTIVE, BIRTHDAY, EMAILADDRESS, GENDER, IMAGEPATH, MARITAL, PRIMARYTELEPHONE, SECONDARYTELEPHONE, version, addressID, schoolID, ETHNICITY, HISPANIC, MAJOR, NATIVELANGUAGE, RELIGIOUSAFFILIATION, studentId FROM PERSON WHERE (addressID = ?)
bind => [1]
Test case that runs the query
public void testSavingPersonSchool(){
PersonService personService = new PersonService();
System.out.println("here\n");
Person cPerson = personService.getPersonQueryBatch(Long.valueOf("10000"));
System.out.println("not here\n");
School school = new School();
Address address = new Address();
address.setStreetAddress("eer street");
address.setCity("hiokins");
address.setZipCode("34343");
address.setState_us("MN");
address.setCountry("usa");
school.setName("Maui");
school.setDescription("Thinking of dropping a summer class?");
school.setAddress(address);
school.setPrimaryPhone("3242342342");
school.setAdmissionsPhone("3242342342");
school.setAdmissionsEmailAddress("ads@d.com");
school.setActive(true);
cPerson.setSchool(school);
school.persist();
Student.java
package org.bixin.dugsi.domain;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.DiscriminatorValue;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.OneToMany;
import javax.persistence.PostPersist;
import javax.validation.constraints.NotNull;
import org.eclipse.persistence.annotations.BatchFetch;
import org.eclipse.persistence.annotations.BatchFetchType;
import org.eclipse.persistence.annotations.JoinFetch;
import org.eclipse.persistence.annotations.JoinFetchType;
import org.springframework.roo.addon.javabean.RooJavaBean;
import org.springframework.roo.addon.jpa.activerecord.RooJpaActiveRecord;
import org.springframework.roo.addon.tostring.RooToString;
@RooJavaBean
@RooToString
@RooJpaActiveRecord
@DiscriminatorValue("S")
public class Student extends Person {
@Basic
@Column(name = "studentId")
private String studentIdentifier;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "student", fetch = FetchType.LAZY)
private Set<Registration> registrations = new HashSet<Registration>();
@PostPersist
public void generateCode() {
studentIdentifier = ("S-000-");
}
}
回答1:
from these lines:
2012-06-26 20:47:29,802 [main] DEBUG org.springframework.beans.factory.annotation.InjectionMetadata - Processing injected method of bean 'org.bixin.dugsi.domain.Student': PersistenceElement for transient javax.persistence.EntityManager org.bixin.dugsi.domain.Person.entityManager
It seems that you are doing some injection on the entity classes. Need to see the full source for the entities to be sure though. Nevertheless, if you do that.. well.. don't :) Creating spring proxies or doing DI on every entity is an overkill, also will almost surely defeat the whole lazy initialization, because Spring will access the ManyToOne collections while doing DI, triggering db load for those entities as well.
As a sidenote: your entities shouldn't contain business logic, thus there's no reason to do any dependency injection to them.
回答2:
With the fetch set to LAZY access to the Person should not trigger queries to the address or school. Do you have weaving enabled (using agent or Java EE/Spring weaving)? You may need to enable dynamic weaving or use static weaving.
It may be something else is triggering the relationships, you could put a breakpoint, or dump the stack trace (using a SessionEvent) in the EclipseLink Session query execution.
Your batch fetch on the address does not make sense, batch fetching is for when the query returns many results, but you are finding by id. You could use a join fetch (in the JPQL or through the join fetch hint).
I'm not sure how this could hang, break the process to get the stack trace to see what it is waiting on.
来源:https://stackoverflow.com/questions/11218379/eclipselink-lazy-loading-hangs-on-reference-class