fixed lazy loading issues

also renamed some local variables in Unit tests
This commit is contained in:
Mic 2013-01-30 11:20:22 +08:00
parent 56c7671939
commit f44e383462
7 changed files with 52 additions and 54 deletions

View file

@ -9,6 +9,7 @@ import java.util.Set;
import javax.persistence.CascadeType; import javax.persistence.CascadeType;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn; import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne; import javax.persistence.ManyToOne;
import javax.persistence.OneToMany; import javax.persistence.OneToMany;
@ -41,7 +42,7 @@ public class Pet extends NamedEntity {
@JoinColumn(name = "owner_id") @JoinColumn(name = "owner_id")
private Owner owner; private Owner owner;
@OneToMany(cascade=CascadeType.ALL, mappedBy="pet") @OneToMany(cascade=CascadeType.ALL, mappedBy="pet", fetch=FetchType.EAGER)
private Set<Visit> visits; private Set<Visit> visits;

View file

@ -120,16 +120,16 @@ public class JdbcOwnerRepositoryImpl implements OwnerRepository {
@Transactional @Transactional
public void save(Owner owner) throws DataAccessException { public void save(Owner owner) throws DataAccessException {
BeanPropertySqlParameterSource parameterSource = new BeanPropertySqlParameterSource(owner);
if (owner.isNew()) { if (owner.isNew()) {
Number newKey = this.insertOwner.executeAndReturnKey( Number newKey = this.insertOwner.executeAndReturnKey(parameterSource);
new BeanPropertySqlParameterSource(owner));
owner.setId(newKey.intValue()); owner.setId(newKey.intValue());
} }
else { else {
this.namedParameterJdbcTemplate.update( this.namedParameterJdbcTemplate.update(
"UPDATE owners SET first_name=:firstName, last_name=:lastName, address=:address, " + "UPDATE owners SET first_name=:firstName, last_name=:lastName, address=:address, " +
"city=:city, telephone=:telephone WHERE id=:id", "city=:city, telephone=:telephone WHERE id=:id",
new BeanPropertySqlParameterSource(owner)); parameterSource);
} }
} }

View file

@ -6,6 +6,7 @@ import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext; import javax.persistence.PersistenceContext;
import javax.persistence.Query; import javax.persistence.Query;
import org.hibernate.Hibernate;
import org.springframework.samples.petclinic.Owner; import org.springframework.samples.petclinic.Owner;
import org.springframework.samples.petclinic.repository.OwnerRepository; import org.springframework.samples.petclinic.repository.OwnerRepository;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
@ -32,13 +33,19 @@ public class JpaOwnerRepositoryImpl implements OwnerRepository {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public Collection<Owner> findByLastName(String lastName) { public Collection<Owner> findByLastName(String lastName) {
Query query = this.em.createQuery("SELECT owner FROM Owner owner WHERE owner.lastName LIKE :lastName"); // using 'join fetch' because a single query should load both owners and pets
// using 'left join fetch' because it might happen that an owner does not have pets yet
Query query = this.em.createQuery("SELECT owner FROM Owner owner left join fetch owner.pets WHERE owner.lastName LIKE :lastName");
query.setParameter("lastName", lastName + "%"); query.setParameter("lastName", lastName + "%");
return query.getResultList(); return query.getResultList();
} }
public Owner findById(int id) { public Owner findById(int id) {
return this.em.find(Owner.class, id); // using 'join fetch' because a single query should load both owners and pets
// using 'left join fetch' because it might happen that an owner does not have pets yet
Query query = this.em.createQuery("SELECT owner FROM Owner owner left join fetch owner.pets WHERE owner.id =:id");
query.setParameter("id", id);
return (Owner) query.getSingleResult();
} }

View file

@ -29,7 +29,7 @@ public class JpaVetRepositoryImpl implements VetRepository {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public Collection<Vet> findAll() { public Collection<Vet> findAll() {
return this.em.createQuery("SELECT vet FROM Vet vet ORDER BY vet.lastName, vet.firstName").getResultList(); return this.em.createQuery("SELECT vet FROM Vet vet join fetch vet.specialties ORDER BY vet.lastName, vet.firstName").getResultList();
} }
} }

View file

@ -97,22 +97,13 @@ public abstract class AbstractOwnerRepositoryTests {
} }
@Test @Transactional @Test @Transactional
public void findOwner() { public void findSingleOwner() {
Owner o1 = this.ownerRepository.findById(1); Owner owner1 = this.ownerRepository.findById(1);
assertTrue(o1.getLastName().startsWith("Franklin")); assertTrue(owner1.getLastName().startsWith("Franklin"));
Owner o10 = this.ownerRepository.findById(10); Owner owner10 = this.ownerRepository.findById(10);
assertEquals("Carlos", o10.getFirstName()); assertEquals("Carlos", owner10.getFirstName());
// XXX: Add programmatic support for ending transactions with the assertEquals(owner1.getPets().size(), 1);
// TestContext Framework.
// Check lazy loading, by ending the transaction:
// endTransaction();
// Now Owners are "disconnected" from the data store.
// We might need to touch this collection if we switched to lazy loading
// in mapping files, but this test would pick this up.
o1.getPets();
} }
@Test @Transactional @Test @Transactional

View file

@ -97,51 +97,51 @@ public abstract class AbstractPetRepositoryTests {
public void getPetTypes() { public void getPetTypes() {
Collection<PetType> petTypes = this.petRepository.findPetTypes(); Collection<PetType> petTypes = this.petRepository.findPetTypes();
PetType t1 = EntityUtils.getById(petTypes, PetType.class, 1); PetType petType1 = EntityUtils.getById(petTypes, PetType.class, 1);
assertEquals("cat", t1.getName()); assertEquals("cat", petType1.getName());
PetType t4 = EntityUtils.getById(petTypes, PetType.class, 4); PetType petType4 = EntityUtils.getById(petTypes, PetType.class, 4);
assertEquals("snake", t4.getName()); assertEquals("snake", petType4.getName());
} }
@Test @Test
public void findPet() { public void findPet() {
Collection<PetType> types = this.petRepository.findPetTypes(); Collection<PetType> types = this.petRepository.findPetTypes();
Pet p7 = this.petRepository.findById(7); Pet pet7 = this.petRepository.findById(7);
assertTrue(p7.getName().startsWith("Samantha")); assertTrue(pet7.getName().startsWith("Samantha"));
assertEquals(EntityUtils.getById(types, PetType.class, 1).getId(), p7.getType().getId()); assertEquals(EntityUtils.getById(types, PetType.class, 1).getId(), pet7.getType().getId());
assertEquals("Jean", p7.getOwner().getFirstName()); assertEquals("Jean", pet7.getOwner().getFirstName());
Pet p6 = this.petRepository.findById(6); Pet pet6 = this.petRepository.findById(6);
assertEquals("George", p6.getName()); assertEquals("George", pet6.getName());
assertEquals(EntityUtils.getById(types, PetType.class, 4).getId(), p6.getType().getId()); assertEquals(EntityUtils.getById(types, PetType.class, 4).getId(), pet6.getType().getId());
assertEquals("Peter", p6.getOwner().getFirstName()); assertEquals("Peter", pet6.getOwner().getFirstName());
} }
@Test @Transactional @Test @Transactional
public void insertPet() { public void insertPet() {
Owner o6 = this.ownerRepository.findById(6); Owner owner6 = this.ownerRepository.findById(6);
int found = o6.getPets().size(); int found = owner6.getPets().size();
Pet pet = new Pet(); Pet pet = new Pet();
pet.setName("bowser"); pet.setName("bowser");
Collection<PetType> types = this.petRepository.findPetTypes(); Collection<PetType> types = this.petRepository.findPetTypes();
pet.setType(EntityUtils.getById(types, PetType.class, 2)); pet.setType(EntityUtils.getById(types, PetType.class, 2));
pet.setBirthDate(new DateTime()); pet.setBirthDate(new DateTime());
o6.addPet(pet); owner6.addPet(pet);
assertEquals(found + 1, o6.getPets().size()); assertEquals(found + 1, owner6.getPets().size());
// both storePet and storeOwner are necessary to cover all ORM tools // both storePet and storeOwner are necessary to cover all ORM tools
this.petRepository.save(pet); this.petRepository.save(pet);
this.ownerRepository.save(o6); this.ownerRepository.save(owner6);
o6 = this.ownerRepository.findById(6); owner6 = this.ownerRepository.findById(6);
assertEquals(found + 1, o6.getPets().size()); assertEquals(found + 1, owner6.getPets().size());
} }
@Test @Transactional @Test @Transactional
public void updatePet() throws Exception { public void updatePet() throws Exception {
Pet p7 = this.petRepository.findById(7); Pet pet7 = this.petRepository.findById(7);
String old = p7.getName(); String old = pet7.getName();
p7.setName(old + "X"); pet7.setName(old + "X");
this.petRepository.save(p7); this.petRepository.save(pet7);
p7 = this.petRepository.findById(7); pet7 = this.petRepository.findById(7);
assertEquals(old + "X", p7.getName()); assertEquals(old + "X", pet7.getName());
} }
} }

View file

@ -90,17 +90,16 @@ public abstract class AbstractVisitRepositoryTests {
@Test @Transactional @Test @Transactional
public void insertVisit() { public void insertVisit() {
Pet p7 = this.petRepository.findById(7); Pet pet7 = this.petRepository.findById(7);
int found = p7.getVisits().size(); int found = pet7.getVisits().size();
Visit visit = new Visit(); Visit visit = new Visit();
p7.addVisit(visit); pet7.addVisit(visit);
visit.setDescription("test"); visit.setDescription("test");
// both storeVisit and storePet are necessary to cover all ORM tools // both storeVisit and storePet are necessary to cover all ORM tools
this.visitRepository.save(visit); this.visitRepository.save(visit);
this.petRepository.save(p7); this.petRepository.save(pet7);
// assertTrue(!visit.isNew()); -- NOT TRUE FOR TOPLINK (before commit) pet7 = this.petRepository.findById(7);
p7 = this.petRepository.findById(7); assertEquals(found + 1, pet7.getVisits().size());
assertEquals(found + 1, p7.getVisits().size());
} }
} }