This is the simply version of my application.
I have 2 entity.
Customer.java
package com.udacity.jdnd.course3.critter.user;
import java.util.List;
import java.util.ArrayList;
import com.udacity.jdnd.course3.critter.pet.Pet;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Entity
@NoArgsConstructor
@ToString
@Getter
@Setter
public class Customer {
public Customer(Customer customer, Pet pet) {
id = customer.getId();
name = customer.getName();
phoneNumber = customer.getPhoneNumber();
notes = customer.getNotes();
if (pet != null) {
pets = new ArrayList<>();
pets.add(pet);
}
}
public Customer(Customer customer, Long petId) {
id = customer.getId();
name = customer.getName();
phoneNumber = customer.getPhoneNumber();
notes = customer.getNotes();
if (petId != null) {
pets = new ArrayList<>();
pets.add(new Pet(petId));
}
}
public void addPet(long petId) {
if (pets == null) {
pets = new ArrayList<>();
}
pets.add(new Pet(petId));
}
@Id
@GeneratedValue
protected long id;
@Column(length = 100)
protected String name;
@Column(length = 50)
private String phoneNumber;
@Column(length = 500)
private String notes;
@OneToMany(mappedBy = "owner", fetch = FetchType.EAGER)
private List<Pet> pets;
}
Pet.java
package com.udacity.jdnd.course3.critter.pet;
import java.time.LocalDate;
import com.udacity.jdnd.course3.critter.user.Customer;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Entity
@NoArgsConstructor
@Getter
@Setter
public class Pet {
public Pet(long id) {
this.id = id;
}
@Id
@GeneratedValue
private long id;
private PetType type;
@Column(length = 100)
private String name;
private LocalDate birthDate;
@Column(length = 500)
private String notes;
@ManyToOne(fetch = FetchType.EAGER)
private Customer owner;
}
I have an API that find the user by their pet id. I used the below query to get it.
CustomerRepository.java
package com.udacity.jdnd.course3.critter.user;
import java.util.Optional;
import org.springframework.data.repository.CrudRepository;
public interface CustomerRepository extends CrudRepository<Customer, Long> {
Optional<Customer> findFirstByPetsId(long id);
}
The issue is when the application ran as normal and using MySQL as the database, the above api return both the customer and their pets. But when I ran the test and use H2 as the database, the query does not return the pets.
package com.udacity.jdnd.course3.critter;
import com.udacity.jdnd.course3.critter.pet.PetController;
import com.udacity.jdnd.course3.critter.pet.PetDTO;
import com.udacity.jdnd.course3.critter.pet.PetType;
import com.udacity.jdnd.course3.critter.user.*;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
@Transactional
@SpringBootTest(classes = CritterApplication.class)
public class CritterFunctionalTest {
@Autowired
private UserController userController;
@Autowired
private PetController petController;
@Test
public void testFindOwnerByPet() {
CustomerDTO customerDTO = createCustomerDTO();
CustomerDTO newCustomer = userController.saveCustomer(customerDTO);
PetDTO petDTO = createPetDTO();
petDTO.setOwnerId(newCustomer.getId());
PetDTO newPet = petController.save(petDTO);
CustomerDTO owner = userController.getOwnerByPet(newPet.getId());
Assertions.assertEquals(owner.getId(), newCustomer.getId());
Assertions.assertEquals(owner.getPetIds().get(0), newPet.getId());
}
private static CustomerDTO createCustomerDTO() {
CustomerDTO customerDTO = new CustomerDTO();
customerDTO.setName("TestEmployee");
customerDTO.setPhoneNumber("123-456-789");
return customerDTO;
}
private static PetDTO createPetDTO() {
PetDTO petDTO = new PetDTO();
petDTO.setName("TestPet");
petDTO.setType(PetType.CAT);
return petDTO;
}
}
I have try adding FetchType.Eager, write the query using criteria builder, but nothing works. The pets property is always null.
I did add log to the application.properties, and it seem that when query with MySQL there is an extra query that was called but I still don't know why it only called when using MySQL.
Hibernate:
select
p1_0.owner_id,
p1_0.id,
p1_0.birth_date,
p1_0.name,
p1_0.notes,
p1_0.type
from
pet p1_0
where
p1_0.owner_id=?
@JoinColumn
annotation atPet
entity? It should be overowner
filed (with@ManyToOne
annotation) - Read more: baeldung.com/jpa-joincolumn-vs-mappedby#joincolumn PS. PS. Why Dto extension? It is used to another objects, used to transmit data between different applications or parts of the same application (could have less fields and any logic / annotations etc. ). Entities without Dto ext. are basic element in ORM and fully reflect the table in the database - Read more: baeldung.com/java-entity-vs-dto@GeneratedValue
is implemented.