18

I have @OneToMany relationship between FabricRoll and FabricDefect.

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @JoinColumn(name = "fabric_roll_id", referencedColumnName = "fabric_roll_id") private Set<FabricDefect> fabricDefects = new HashSet<>(); 

The problem is when I get FabricRoll by JpaRepository function

findAll()

the associate FabricDefect is also loaded.

I want to load only FabricRoll and FabricDefect should load when calling the function getFabricDefect()

FabricRollServiceImpl class

@Component public class FabricRollServiceImpl implements IFabricRollService{ @Autowired FabricRollRepository fabricRollRepository; @Transactional(propagation = Propagation.REQUIRED) @Override public List<FabricRoll> getAllFabricRoll() { FabricRoll fabricRoll1 = new FabricRoll(); fabricRoll1.setBatchNo("34344"); fabricRoll1.setLotNo("425"); fabricRoll1.setPoNo("42"); fabricRoll1.setRollLength(2343); fabricRoll1.setRollNo("356"); fabricRoll1.setRollWidth(60); fabricRoll1.setStyleNo("354"); FabricDefect fabricDefect = new FabricDefect(); fabricDefect.setDefectNote("note"); fabricDefect.setDefectPoint(3); fabricDefect.setSegment(3); fabricDefect.setYard(42); Set<FabricDefect> fabricDefects = new HashSet<>(); fabricDefects.add(fabricDefect); fabricRoll1.setFabricDefects(fabricDefects); addFabricRoll(fabricRoll1); FabricRoll fabricRoll = null; return fabricRollRepository.findAll(); } @Override public void addFabricRoll(FabricRoll fabricRoll) { fabricRollRepository.save(fabricRoll); } 

}

Break point: enter image description here

Console: enter image description here

4
  • This is OneToMany, why you uesed JoinColumn ? Commented Oct 23, 2017 at 7:59
  • show what you have in FabricDefect class Commented Oct 23, 2017 at 9:35
  • if you get rid of the @Transactional annotation you will see that you'll get LazyInitializationException which means that the collection is not loaded. Commented Oct 23, 2017 at 14:10
  • here the answer for same question stackoverflow.com/a/42584774/5289288 Commented Dec 8, 2017 at 19:28

5 Answers 5

2

It seems to be a debugging artifact.

At debugging time, because the transaction is still open, the watched lazy loaded entity properties will be loaded at the breakpoint evaluation time.

To check the "production" behavior you should insert a em.detach statement just before the breakpoint or use logging (as suggested by Manza) and check em.getEntityManagerFactory().getPersistenceUnitUtil().isLoaded(fabricRoll1.fabricDefects()) returns false on the detached entity.

(remember to inject EntityManager for example by declaring @PersistenceContext private EntityManager em;)

Sign up to request clarification or add additional context in comments.

Comments

0

You don't need to use @JoinColumn, and you don't need to instantiate fabricDefects

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) private Set<FabricDefect> fabricDefects ; 

See more in this question.

Comments

0

FabricDefect class:

@ManyToOne @JoinColumn(name = "fabric_roll_id") private FabricRoll roll; 

FabricRoll class:

@OneToMany(mappedBy = "roll") private Set<FabricDefect> fabricDefects; 

Collections are by default loaded lazily, JPA will query the db only when the method getFabricDefects will be called. You can see it by yourself enabling logging.

Comments

0

I found solution in this tutorial.

You have to modify FabricRoll OneToMany map as below:

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "fabricRoll") private Set<FabricDefect> fabricDefects; 

FabricDefect ManyToOne as below (remember to remove fabric_roll_id field if you included it in your entity):

@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "fabric_roll_id") private FabricRoll fabricRoll; 

And you don't need to add @Transactional(propagation = Propagation.REQUIRED) before getAllFabricRoll() function.

Comments

0

The issue occurs because, despite `fetch = FetchType.LAZY`, something in your application is accessing `getFabricDefects()`, triggering Hibernate to load the `FabricDefect` collection. Common causes include JSON serialization (e.g., Jackson in a REST API), Lombok annotations like `@Data` or `@ToString`, or logging/debugging that inadvertently calls the getter. Here's how to fix it and ensure `FabricDefect` is only loaded when explicitly requested.

### Why Lazy Loading Isn't Working

1. Serialization: - When returning `List<FabricRoll>` from a REST controller, Jackson calls `getFabricDefects()` to serialize the collection, fetching the data if the session is open or throwing a `LazyInitializationException` if closed. 2. Lombok: - If `FabricRoll` uses `@Data` or `@ToString`, the generated `toString()` or getters may be called during logging (e.g., `System.out.println(fabricRoll)`) or debugging, loading `fabricDefects`. 3. Transactional Context: - In a `@Transactional` method, accessing `fabricDefects` (even unintentionally) fetches the data because the Hibernate session is open. 

### Solutions

#### 1. Prevent Serialization Issues:

Use `@JsonIgnore` to exclude `fabricDefects` from JSON serialization or configure Jackson to handle lazy-loaded collections.

 @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @JoinColumn(name = "fabric_roll_id", referencedColumnName = "fabricRollId") @JsonIgnore private Set<FabricDefect> fabricDefects = new HashSet<>() 

#### 2. Avoid Lombok Pitfalls

If using Lombok, avoid `@Data` as it generates `toString()`, `equals()`, and `hashCode()`, which may access `fabricDefects`. Use `@Getter`, `@Setter`, and exclude `fabricDefects` from `toString()`:

@ToString(exclude = "fabricDefects") 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.