0

Java 8 Spring Boot 2.0.5

I have following configuration which I am sure is correct. During debugging I can get values from collection of tools which is lazy loaded in UserInformations. I read that debugging is doing additional fetch to give user ability to get values from database, but simple System.out.print in my endpoint should fail. I want to lazy load the collection in @OneToMany because now is loading eager and if I will have more data getting single UserInformation will be to long. @OneToMany is by default lazy but in my example is working as eager and I don't know why to repair it to fetch lazy.

This are my classes:

@Getter
@Setter
@NoArgsConstructor
public class ElectricTool {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @NotBlank
    @NotEmpty
    @Size(max = 64)
    private String name;

    @ManyToOne
    private UserInformation userInformation;

    public ElectricTool(String name) {
        this.name = name;
    }
}



@Getter
@Setter
@Entity
public class UserInformation {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @NotEmpty
    @NotBlank
    @Size(max = 32)
    private String firstName;

    @OneToMany(mappedBy = "userInformation")
    private Set<ElectricTool> electricTools;

    public UserInformation() {
        this.electricTools = new HashSet<>();
    }

    public UserInformation(String firstName) {
        this.firstName = firstName;
        this.electricTools = new HashSet<>();
    }
}

@Repository
public interface UserInformationRepository extends
JpaRepository<UserInformation, Long> {}

@Repository
public interface ElectricToolRepository extends
JpaRepository<ElectricTool, Long> {}


@RestController
@RequestMapping(value = "/lazy")
public class LazyController {

    private UserInformationRepository userInformationRepository;
    private ElectricToolRepository electricToolRepository;

    public LazyController(UserInformationRepository userInformationRepository, ElectricToolRepository electricToolRepository) {
    this.userInformationRepository = userInformationRepository;
    this.electricToolRepository = electricToolRepository;
}

@GetMapping
public void checkLazy() {
    userInformationRepository.findAll().get(0).getElectricTools().stream().findFirst().get();
   }
}

Configuration

spring.datasource:
    url: jdbc:postgresql://localhost:5432/lazy
    username: postgres
    password: admin
spring.jpa:
    show-sql: true
    hibernate.ddl-auto: create-drop
    database-platform: org.hibernate.dialect.PostgreSQLDialect
    properties.hibernate.jdbc.lob.non_contextual_creation: true

Result of System.out.print: This should be lazy loading exception

Result of debugging: While debugging i have access to electric tool but it should throw lazy loading exception

When I turn on showing of SQL and change method in controller to directly get electricTool from userInformation it return the object but should throw an exception and the sql look like this

select userinform0_.id as id1_1_, userinform0_.first_name as first_na2_1_ from user_information userinform0_
select electricto0_.user_information_id as user_inf3_0_0_, electricto0_.id as id1_0_0_, electricto0_.id as id1_0_1_, electricto0_.name as name2_0_1_, electricto0_.user_information_id as user_inf3_0_1_ from electric_tool electricto0_ where electricto0_.user_information_id=?
select userinform0_.id as id1_1_, userinform0_.first_name as first_na2_1_ from user_information userinform0_

When you asked me why i think i should get lazy fetch exception during asking about element in collection annotated by @OneToMany i just want to show you the same configuration as above but in second project which work correctly, but lets look at code

@Entity
@Getter
@Setter
public class UserInformation {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String firstName;
    @OneToMany(mappedBy = "userInformation")
    private Set<Address> addresses;

    public UserInformation() {
        this.addresses = new HashSet<>();
    }

    public UserInformation(String firstName) {
        this.firstName = firstName;
        this.addresses = new HashSet<>();
    }
}


@Entity
@Getter
@Setter
@NoArgsConstructor
public class Address {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String city;
    @ManyToOne
    private UserInformation userInformation;

    public Address(String city) {
        this.city = city;
    }
}


@Repository
public interface UserInformationRepository extends JpaRepository<UserInformation, Long> {
}

@Repository
public interface AddressRepository extends JpaRepository<Address, Long> {
}

Configuration

spring.datasource:
    url: jdbc:postgresql://localhost:5432/specification
    username: postgres
    password: admin
spring.jpa:
    show-sql: true
    hibernate.ddl-auto: create-drop
    database-platform: org.hibernate.dialect.PostgreSQLDialect
    properties.hibernate.jdbc.lob.non_contextual_creation: true

And when i try to execute that piece of code:

userInformationRepository.findAll()

Result

I get LazyInitializationException and that is behavior which i want to get in the first example. So where is the mistake in first example because of findAll method give me all nested relations in debugger

Additional when i do the same execution as in first example i get LazyInitializationException

userInformationRepository.findAll().get(0).getAddresses().stream().findFirst().get();

failed to lazily initialize a collection of role: com.specification.specification.entity.borrow.UserInformation.addresses, could not initialize proxy - no Session
Sebastian
  • 92
  • 2
  • 14
  • How do you know its eager in your case and not lazy? Log your queries using `hibernate.show_sql` and see if any query is fired when you reach the statement `userInformationRepository.findAll().get(0).getElectricTools()`. – Abdullah Khan Nov 21 '18 at 09:44
  • See here: https://stackoverflow.com/questions/30549489/what-is-this-spring-jpa-open-in-view-true-property-in-spring-boot The session may held open until the web request completes. You may need to disable that to trigger the exception. – Alan Hay Nov 21 '18 at 10:50
  • Spring Boot by default enables the `OpenEntityManagerInViewInterceptor` so you will net get an exception UNLESS you explicitly disable it. – M. Deinum Nov 21 '18 at 11:44

2 Answers2

0

It will print the address/reference of the class only. if you want to print values then override toString method like below.

@Override
public String toString() 
{
    return new ToStringBuilder(this)
      .append("name", name)
      .append("etc", etc)
      .toString(); 
}

Also add cascade type and fetch type in onetomany mapping.

@OneToMany(mappedBy = "userInformation",cascade=CascadeType.ALL,fetch=FetchType.EAGER)
Alien
  • 15,141
  • 6
  • 37
  • 57
  • I don't explain correctly what I want to do, my fault. I change post because I have configuration to lazy load as you see but in case of @OneToMany fetch is eager because I can get access to electric tool but in that configuration I should not get electric tool object instead I should get exception and I need to repair it to get that exception but I don't have any idea how to do it – Sebastian Nov 21 '18 at 09:42
0

Problem solved as @Abdullah Khan said, sql logs correctly show that all of these example works but debugger of Intellij fetchs necessary relations for objects, topic to close. Thanks guys for help :)

Sebastian
  • 92
  • 2
  • 14