0

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=?
5
  • Where is @JoinColumn annotation at Pet entity? It should be over owner 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 Commented Jul 8 at 16:30
  • I would turn on SQL statements debugging and see what the difference is and work backwards from there.
    – K.Nicholas
    Commented Jul 8 at 16:32
  • @K.Nicholas, I edited the question with my findings, though I still don't understand why the extra query exists when using MySQL. Commented Jul 8 at 16:46
  • Don't "know" exactly either but that query is usually used to determine whether a row exists. There may be a difference in how @GeneratedValue is implemented.
    – K.Nicholas
    Commented Jul 8 at 16:50
  • How can you query with this method, findFirstByPetsId?? Do you make inner join with pets automatically? I think your query is not correct.
    – Eric
    Commented Jul 10 at 12:54

0

Browse other questions tagged or ask your own question.