Search code examples
spring-bootjpatransactions

Q: Transactional Code why does this work so well?


Hello my professionals I have a simple question here that I would like to beg to solve this..

this is an Entity of Member

@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
/*@ToString(of = {"id", "username", "age"})*/
public class Member {

    @Id
    /*@GeneratedValue(strategy = GenerationType.IDENTITY)*/
    @Column(name = "member_id")
    private Long id;

    private String username;

    private int age;

    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "member")
    private List<Team> teams;

    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL,  mappedBy = "member")
    private List<Coach> coachs;

}

And this is an Entity of Coach

@Entity
@AllArgsConstructor
@Getter
@Builder
@Setter
@NoArgsConstructor
@ToString(of = {"id","name","career"})
public class Coach {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name= "coach_id")
    private Long id;

    @Column
    private String name;

    @Column
    private String career;

    @ManyToOne(fetch = FetchType.LAZY,cascade = ALL)
    @JoinColumn(name = "member_id")
    private Member member;

    @OneToOne(fetch = FetchType.LAZY,cascade = ALL)
    @JoinColumn(name = "team_id")
    private Team team;

}

and This is Controller Code

    @GetMapping("/member")
public void createUser(){
     Member m = memberService.createMember();
     Coach c = m.getCoachs().get(0);
     log.info(c.getName());
}

and This is Service Code

private final MemberRepository memberRepository;

    @Transactional
    public Member createMember(){
        return memberRepository.findMemberById(3L);
    }

and the last this is RepositoryCode

Member findMemberById(Long id);

So my question is that when i printed out Coach's name at the controller on console

it printed out so well.

but what I know the Transaction is over from the service So the persistence container is closed that means coach name can't be imported cause it's LAZY loading and persistence container is closed but it was printed out well

I want to know the reason why ...

here are the console results Thanks !!

[2022-01-10 23:27:46.835] [http-nio-9000-exec-2] [] INFO  o.a.c.c.C.[.[.[/] - Initializing Spring DispatcherServlet 'dispatcherServlet' 
[2022-01-10 23:27:46.835] [http-nio-9000-exec-2] [] INFO  o.s.w.s.DispatcherServlet - Initializing Servlet 'dispatcherServlet' 
[2022-01-10 23:27:46.855] [http-nio-9000-exec-2] [] INFO  o.s.w.s.DispatcherServlet - Completed initialization in 19 ms 
Hibernate: 
    /* select
        generatedAlias0 
    from
        Member as generatedAlias0 
    where
        generatedAlias0.id=:param0 */ select
            member0_.member_id as member_i1_1_,
            member0_.age as age2_1_,
            member0_.username as username3_1_ 
        from
            member member0_ 
        where
            member0_.member_id=?
[2022-01-10 23:27:47.007] [http-nio-9000-exec-2] [4c0222d3] INFO  p6spy - #1641824867007 | took 15ms | statement | connection 1| url jdbc:mariadb://patrick-lab.cjeq2ffynlc2.ap-northeast-2.rds.amazonaws.com:3306/patricklab?characterEncoding=UTF-8&serverTimezone=UTC
/* select generatedAlias0 from Member as generatedAlias0 where generatedAlias0.id=:param0 */ select member0_.member_id as member_i1_1_, member0_.age as age2_1_, member0_.username as username3_1_ from member member0_ where member0_.member_id=?
/* select generatedAlias0 from Member as generatedAlias0 where generatedAlias0.id=:param0 */ select member0_.member_id as member_i1_1_, member0_.age as age2_1_, member0_.username as username3_1_ from member member0_ where member0_.member_id=3; 
[2022-01-10 23:27:47.170] [http-nio-9000-exec-2] [4c0222d3] INFO  p6spy - #1641824867170 | took 12ms | commit | connection 1| url jdbc:mariadb://patrick-lab.cjeq2ffynlc2.ap-northeast-2.rds.amazonaws.com:3306/patricklab?characterEncoding=UTF-8&serverTimezone=UTC

; 
Hibernate: 
    select
        coachs0_.member_id as member_i4_0_0_,
        coachs0_.coach_id as coach_id1_0_0_,
        coachs0_.coach_id as coach_id1_0_1_,
        coachs0_.career as career2_0_1_,
        coachs0_.member_id as member_i4_0_1_,
        coachs0_.name as name3_0_1_,
        coachs0_.team_id as team_id5_0_1_ 
    from
        coach coachs0_ 
    where
        coachs0_.member_id=?
[2022-01-10 23:27:47.200] [http-nio-9000-exec-2] [4c0222d3] INFO  p6spy - #1641824867200 | took 12ms | statement | connection 1| url jdbc:mariadb://patrick-lab.cjeq2ffynlc2.ap-northeast-2.rds.amazonaws.com:3306/patricklab?characterEncoding=UTF-8&serverTimezone=UTC
select coachs0_.member_id as member_i4_0_0_, coachs0_.coach_id as coach_id1_0_0_, coachs0_.coach_id as coach_id1_0_1_, coachs0_.career as career2_0_1_, coachs0_.member_id as member_i4_0_1_, coachs0_.name as name3_0_1_, coachs0_.team_id as team_id5_0_1_ from coach coachs0_ where coachs0_.member_id=?
select coachs0_.member_id as member_i4_0_0_, coachs0_.coach_id as coach_id1_0_0_, coachs0_.coach_id as coach_id1_0_1_, coachs0_.career as career2_0_1_, coachs0_.member_id as member_i4_0_1_, coachs0_.name as name3_0_1_, coachs0_.team_id as team_id5_0_1_ from coach coachs0_ where coachs0_.member_id=3; 
[2022-01-10 23:27:47.213] [http-nio-9000-exec-2] [4c0222d3] INFO  m.p.l.m.c.MemberController - Coach1 

Solution

  • I believe it is because you are using the spring-boot default setting which the spring.jpa.open-in-view is set to true .

    This property enables OpenSessionInView pattern which you can simply think that a transaction will be opened automatically for you at the very first beginning when processing any HTTP request (e.g. in the Servlet Filter etc). Because of this , a transaction is actually already open before your service method executes and it is still active after your service method completes. Hence you will not experience any LazyInitializationException even after you access non-initialized properties outside the service method as the transaction is still active.

    There is a strong debate about whether or not spring-boot should enable it by default in the past . You can refer this for more details if you are interested. I personally would recommend to turn it off.