JPA
Spring Data JPA - a dumb way to negate your lazy loading
I was playing with different options for joining tables, and kept scratching my head about why JPA was building so many dang SQL selects. Eventually, the lightbulb came on. I was logging my result sets so that I could verify I was getting all of my data. Lazy loading promises to not select on joined tables until you make a reference to them. Since I was referring to them in my Entity’s toString() method, I was accidentally cancelling out my lazy loading.
Derp.
Here’s the code…
Pet Owners @Controller
@RequestMapping( { "", "/", "/index", "/index.html" } )
public String listOwners(Model model)
{
log.info("\n >>>>> ownerSvc.findAll()");
Set<Owner> allOwners = ownerSvc.findAll();
log.info(allOwners.toString());
model.addAttribute( "owners", allOwners );
log.info("\n >>>>> ownerSvc.findByLastName()");
Owner owner = ownerSvc.findByLastName("Doolittle");
log.info(owner.toString());
model.addAttribute( "ownerEntity", owner);
log.info("\n >>>>> ownerSvc.findOwnerPetsByLastName()");
List<OwnerPetsDTO> ownerPets = ownerSvc.findOwnerPetsByLastName("Doolittle");
log.info(ownerPets.toString());
model.addAttribute( "ownerPetsDTO", ownerPets);
return "owners/index";
}
Owner @Service Impl
@Override
public Set<Owner> findAll()
{
return ownerRepo.findAll();
}
@Override
public Owner findByLastName(String lastName)
{
return ownerRepo.findByLastName(lastName);
}
@Override
public List<OwnerPetsDTO> findOwnerPetsByLastName(String lastName)
{
return ownerRepo.findOwnerPetsByLastName(lastName);
}
Owner Repository
@Repository
public interface OwnerRepository extends CrudRepository<Owner, Long>
{
Owner findByLastName(String lastName);
Set<Owner> findAll();
@Query( value = "SELECT new com.scotthensen.fooclinic.model.OwnerPetsDTO "
+ " ( o.firstName, "
+ " o.lastName, "
+ " o.address, "
+ " p.name ) "
+ "FROM Owner o, "
+ " Pet p "
+ "WHERE o.lastName = :lastName "
+ " AND o.id = p.owner "
)
List<OwnerPetsDTO> findOwnerPetsByLastName(@Param("lastName") String lastName);
}
Owner @Entity
@Getter
@Setter
@NoArgsConstructor
@Entity
@Table( name = "owners" )
public class Owner extends Person
{
@Builder
public Owner(Long id, String firstName, String lastName,
String address, String city, String telephone, Set<Pet> pets)
{
super(id, firstName, lastName);
this.address = address;
this.city = city;
this.telephone = telephone;
this.pets = pets;
}
@Column( name = "address" )
private String address;
@Column( name = "city" )
private String city;
@Column( name = "telephone" )
private String telephone;
@OneToMany( cascade = CascadeType.ALL, mappedBy = "owner", fetch = FetchType.LAZY )
private Set<Pet> pets = new HashSet<>();
@Override
public String toString() {
return "Owner [ \n"
+ " address=" + address + "\n"
+ " , city=" + city + "\n"
+ " , telephone=" + telephone + "\n"
+ " , pets=" + pets + "\n" // this will negate lazy loading
+ "]";
}
}
Pet @Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity
@Table( name = "pets" )
public class Pet extends BaseEntity
{
@Column( name = "name")
private String name;
@ManyToOne( fetch = FetchType.LAZY )
@JoinColumn( name = "type_id" )
private PetType petType;
@ManyToOne( fetch = FetchType.LAZY )
@JoinColumn( name = "owner_id" )
private Owner owner;
@Column( name = "birth_date")
private LocalDate birthDate;
@OneToMany( cascade = CascadeType.ALL, mappedBy = "pet", fetch = FetchType.LAZY )
private Set<Visit> visits = new HashSet<>();
@Override
public String toString() {
return "Pet ["
+ " name=" + name + "\n"
+ " , petType=" + petType + "\n" // this will negate lazy loading
+ " , birthDate=" + birthDate + "\n"
+ " , visits=" + visits + "\n" // this will negate lazy loading
+ "]";
}
}
Owner Pets DTO
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class OwnerPetsDTO
{
private String firstName;
private String lastName;
private String address;
private String petName;
@Override
public String toString() {
return "OwnerPetsDTO [ \n"
+ " firstName=" + firstName + "\n"
+ " , lastName=" + lastName + "\n"
+ " , address=" + address + "\n"
+ " , petName=" + petName + "\n"
+ "]";
}
}
Results (lazy loading, but logging the relationships with toString())
findAll
>>>>> ownerSvc.findAll()
Hibernate:
select
owner0_.id as id1_0_,
owner0_.first_name as first_na2_0_,
owner0_.last_name as last_nam3_0_,
owner0_.address as address4_0_,
owner0_.city as city5_0_,
owner0_.telephone as telephon6_0_
from
owners owner0_
Hibernate:
select
pets0_.owner_id as owner_id4_1_0_,
pets0_.id as id1_1_0_,
pets0_.id as id1_1_1_,
pets0_.birth_date as birth_da2_1_1_,
pets0_.name as name3_1_1_,
pets0_.owner_id as owner_id4_1_1_,
pets0_.type_id as type_id5_1_1_
from
pets pets0_
where
pets0_.owner_id=?
Hibernate:
select
pettype0_.id as id1_3_0_,
pettype0_.name as name2_3_0_
from
types pettype0_
where
pettype0_.id=?
Hibernate:
select
visits0_.pet_id as pet_id4_6_0_,
visits0_.id as id1_6_0_,
visits0_.id as id1_6_1_,
visits0_.date as date2_6_1_,
visits0_.description as descript3_6_1_,
visits0_.pet_id as pet_id4_6_1_
from
visits visits0_
where
visits0_.pet_id=?
Hibernate:
select
pets0_.owner_id as owner_id4_1_0_,
pets0_.id as id1_1_0_,
pets0_.id as id1_1_1_,
pets0_.birth_date as birth_da2_1_1_,
pets0_.name as name3_1_1_,
pets0_.owner_id as owner_id4_1_1_,
pets0_.type_id as type_id5_1_1_
from
pets pets0_
where
pets0_.owner_id=?
Hibernate:
select
pettype0_.id as id1_3_0_,
pettype0_.name as name2_3_0_
from
types pettype0_
where
pettype0_.id=?
Hibernate:
select
visits0_.pet_id as pet_id4_6_0_,
visits0_.id as id1_6_0_,
visits0_.id as id1_6_1_,
visits0_.date as date2_6_1_,
visits0_.description as descript3_6_1_,
visits0_.pet_id as pet_id4_6_1_
from
visits visits0_
where
visits0_.pet_id=?
Hibernate:
select
pets0_.owner_id as owner_id4_1_0_,
pets0_.id as id1_1_0_,
pets0_.id as id1_1_1_,
pets0_.birth_date as birth_da2_1_1_,
pets0_.name as name3_1_1_,
pets0_.owner_id as owner_id4_1_1_,
pets0_.type_id as type_id5_1_1_
from
pets pets0_
where
pets0_.owner_id=?
Hibernate:
select
visits0_.pet_id as pet_id4_6_0_,
visits0_.id as id1_6_0_,
visits0_.id as id1_6_1_,
visits0_.date as date2_6_1_,
visits0_.description as descript3_6_1_,
visits0_.pet_id as pet_id4_6_1_
from
visits visits0_
where
visits0_.pet_id=?
Hibernate:
select
visits0_.pet_id as pet_id4_6_0_,
visits0_.id as id1_6_0_,
visits0_.id as id1_6_1_,
visits0_.date as date2_6_1_,
visits0_.description as descript3_6_1_,
visits0_.pet_id as pet_id4_6_1_
from
visits visits0_
where
visits0_.pet_id=?
Hibernate:
select
visits0_.pet_id as pet_id4_6_0_,
visits0_.id as id1_6_0_,
visits0_.id as id1_6_1_,
visits0_.date as date2_6_1_,
visits0_.description as descript3_6_1_,
visits0_.pet_id as pet_id4_6_1_
from
visits visits0_
where
visits0_.pet_id=?
Hibernate:
select
visits0_.pet_id as pet_id4_6_0_,
visits0_.id as id1_6_0_,
visits0_.id as id1_6_1_,
visits0_.date as date2_6_1_,
visits0_.description as descript3_6_1_,
visits0_.pet_id as pet_id4_6_1_
from
visits visits0_
where
visits0_.pet_id=?
Hibernate:
select
visits0_.pet_id as pet_id4_6_0_,
visits0_.id as id1_6_0_,
visits0_.id as id1_6_1_,
visits0_.date as date2_6_1_,
visits0_.description as descript3_6_1_,
visits0_.pet_id as pet_id4_6_1_
from
visits visits0_
where
visits0_.pet_id=?
[Owner [
address=123 Foo St
, city=Tempe
, telephone=4808675309
, pets=[Pet [ name=Pooch
, petType=com.scotthensen.fooclinic.model.PetType@6df38ecb
, birthDate=2018-10-09
, visits=[]
]]
], Owner [
address=123 Foo St
, city=Tempe
, telephone=4808675309
, pets=[Pet [ name=Kitty
, petType=com.scotthensen.fooclinic.model.PetType@6f40601d
, birthDate=2018-10-09
, visits=[com.scotthensen.fooclinic.model.Visit@2acaf15a]
]]
], Owner [
address=1313 Mockingbird Ln
, city=Tempe
, telephone=4808675309
, pets=[Pet [ name=dog2
, petType=com.scotthensen.fooclinic.model.PetType@6df38ecb
, birthDate=null
, visits=[]
], Pet [ name=dog3
, petType=com.scotthensen.fooclinic.model.PetType@6df38ecb
, birthDate=null
, visits=[]
], Pet [ name=dog4
, petType=com.scotthensen.fooclinic.model.PetType@6df38ecb
, birthDate=null
, visits=[]
], Pet [ name=dog1
, petType=com.scotthensen.fooclinic.model.PetType@6df38ecb
, birthDate=null
, visits=[]
], Pet [ name=dog5
, petType=com.scotthensen.fooclinic.model.PetType@6df38ecb
, birthDate=null
, visits=[]
]]
]]
findByLastName
>>>>> ownerSvc.findByLastName()
Hibernate:
select
owner0_.id as id1_0_,
owner0_.first_name as first_na2_0_,
owner0_.last_name as last_nam3_0_,
owner0_.address as address4_0_,
owner0_.city as city5_0_,
owner0_.telephone as telephon6_0_
from
owners owner0_
where
owner0_.last_name=?
Owner [
address=1313 Mockingbird Ln
, city=Tempe
, telephone=4808675309
, pets=[Pet [ name=dog2
, petType=com.scotthensen.fooclinic.model.PetType@6df38ecb
, birthDate=null
, visits=[]
], Pet [ name=dog3
, petType=com.scotthensen.fooclinic.model.PetType@6df38ecb
, birthDate=null
, visits=[]
], Pet [ name=dog4
, petType=com.scotthensen.fooclinic.model.PetType@6df38ecb
, birthDate=null
, visits=[]
], Pet [ name=dog1
, petType=com.scotthensen.fooclinic.model.PetType@6df38ecb
, birthDate=null
, visits=[]
], Pet [ name=dog5
, petType=com.scotthensen.fooclinic.model.PetType@6df38ecb
, birthDate=null
, visits=[]
]]
]
findOwnerPetsByLastName
>>>>> ownerSvc.findOwnerPetsByLastName()
Hibernate:
select
owner0_.first_name as col_0_0_,
owner0_.last_name as col_1_0_,
owner0_.address as col_2_0_,
pet1_.name as col_3_0_
from
owners owner0_ cross
join
pets pet1_
where
owner0_.last_name=?
and owner0_.id=pet1_.owner_id
[OwnerPetsDTO [
firstName=Dr.
, lastName=Doolittle
, address=1313 Mockingbird Ln
, petName=dog2
], OwnerPetsDTO [
firstName=Dr.
, lastName=Doolittle
, address=1313 Mockingbird Ln
, petName=dog5
], OwnerPetsDTO [
firstName=Dr.
, lastName=Doolittle
, address=1313 Mockingbird Ln
, petName=dog1
], OwnerPetsDTO [
firstName=Dr.
, lastName=Doolittle
, address=1313 Mockingbird Ln
, petName=dog4
], OwnerPetsDTO [
firstName=Dr.
, lastName=Doolittle
, address=1313 Mockingbird Ln
, petName=dog3
]]
Fixing the Lazy Loading
Removed petType and visits from Pet toString()
@Override
public String toString() {
return "Pet ["
+ " name=" + name + "\n"
// + " , petType=" + petType + "\n" // this will negate lazy loading
+ " , birthDate=" + birthDate + "\n"
// + " , visits=" + visits + "\n" // this will negate lazy loading
+ "]";
}
Removed pets from Owner toString()
@Override
public String toString() {
return "Owner [ \n"
+ " address=" + address + "\n"
+ " , city=" + city + "\n"
+ " , telephone=" + telephone + "\n"
// + " , pets=" + pets + "\n" // this will negate lazy loading
+ "]";
}
Results (Lazy loading works when you don’t log your related tables.)
findAll
>>>>> ownerSvc.findAll()
Hibernate:
select
owner0_.id as id1_0_,
owner0_.first_name as first_na2_0_,
owner0_.last_name as last_nam3_0_,
owner0_.address as address4_0_,
owner0_.city as city5_0_,
owner0_.telephone as telephon6_0_
from
owners owner0_
[Owner [
address=123 Foo St
, city=Tempe
, telephone=4808675309
], Owner [
address=123 Foo St
, city=Tempe
, telephone=4808675309
], Owner [
address=1313 Mockingbird Ln
, city=Tempe
, telephone=4808675309
]]
findByLastName
>>>>> ownerSvc.findByLastName()
Hibernate:
select
owner0_.id as id1_0_,
owner0_.first_name as first_na2_0_,
owner0_.last_name as last_nam3_0_,
owner0_.address as address4_0_,
owner0_.city as city5_0_,
owner0_.telephone as telephon6_0_
from
owners owner0_
where
owner0_.last_name=?
Owner [
address=1313 Mockingbird Ln
, city=Tempe
, telephone=4808675309
]
findOwnerPetsByLastName
>>>>> ownerSvc.findOwnerPetsByLastName()
Hibernate:
select
owner0_.first_name as col_0_0_,
owner0_.last_name as col_1_0_,
owner0_.address as col_2_0_,
pet1_.name as col_3_0_
from
owners owner0_ cross
join
pets pet1_
where
owner0_.last_name=?
and owner0_.id=pet1_.owner_id
[OwnerPetsDTO [
firstName=Dr.
, lastName=Doolittle
, address=1313 Mockingbird Ln
, petName=dog1
], OwnerPetsDTO [
firstName=Dr.
, lastName=Doolittle
, address=1313 Mockingbird Ln
, petName=dog4
], OwnerPetsDTO [
firstName=Dr.
, lastName=Doolittle
, address=1313 Mockingbird Ln
, petName=dog5
], OwnerPetsDTO [
firstName=Dr.
, lastName=Doolittle
, address=1313 Mockingbird Ln
, petName=dog2
], OwnerPetsDTO [
firstName=Dr.
, lastName=Doolittle
, address=1313 Mockingbird Ln
, petName=dog3
]]