인프런 커뮤니티 질문&답변

dev_dev_dev님의 프로필 이미지

작성한 질문수

스프링 시큐리티

8) 계층 권한 적용하기- RoleHierarchy

질문드립니다.

작성

·

206

1

아무것도 등록이 되어있지 않아서 DB를 보니 ROLE_HIERARCHY 부분에 아무런 데이터 정보도 들어있지 않다는 것을 알게 되었습니다. 

SetupDataLoader.java에서 

과 같이 계층 권한을 설정해주었는데 

처럼 부모객체가 등록되어있지 않은 것으로 나옵니다. 

해당 코드 내에서 어떤 문제가 있는 것일까요?

답변 4

2

정수원님의 프로필 이미지
정수원
지식공유자

네 맞습니다

업데이트는 영속성 객체인 경우 변경을 감지하여 save 하지 않아도 자동 DB 에 반영이 됩니다

단 조건은 마지막에 commit 이나 flush 와 같은 API 가 실행되어야 합니다

스프링에서는 메서드에 @Transactional 을 선언하면 트랜잭션이 종료되는 시점에 커밋을 하게 되고 이때 업데이트 쿼리가 실제 DB 에 반영이 됩니다

즉 commit 이나 flush 와 같은 api 가 실행됨을 의미합니다

스프링 JpaRepository 의 save 메서드는 @Transactional 가 기본적으로 선언이 되어 있습니다

그러나 save 메서드 이후  set 을 설정하게 되면 이미 commit 은 종료가 되었고 별도로 commit 이나 flush 등을 실행하지 않을 경우 자동변경감지를 감지를 통한 업데이트 쿼리가 실행되지 않고 결국 DB 에 반영되지 않습니다

스프링 Jpa 가 아닌 일반 hibernate 로 구문을 작성하는 경우 EntityManager 가 트랜잭션을 선언한 다음 엔터티 객체를 persist 하거나 set 구문을 작성하고 마지막에 Commit 을 실행하는 것과 같은 이치입니다

즉 DB 반영은 commit 시점이기 때문입니다

그래서 Set 구문 이전이 아닌 이후에 save 를 해야만 트랜잭션이 일어나고 commit 이 되어 자동변경감지를 통한 업데이트 쿼리가 실행되게 됩니다

1

dev_dev_dev님의 프로필 이미지
dev_dev_dev
질문자

코드입니다.

0

dev_dev_dev님의 프로필 이미지
dev_dev_dev
질문자

흠.. 해결했긴 했는데 뭔가 찝찝한 상황입니다.

아래와같이 save문구를 써서 해결했는데요. 제가 알기로 jpa에서는 변경감지기능이 있어 set만 해줘도 업데이트가 되는것으로 알고 있는데 저렇게 명시적으로 save를 해줘야 데이터값이 제대로 들어가는 상황입니다. 

강의 관련 질문은 아니지만 혹시 왜 저렇게 되는지 이유를 알려주실 수 있나요?

0

정수원님의 프로필 이미지
정수원
지식공유자

코드 상으로는 특별한 문제가 없어 보입니다.

깃헙에 있는 소스 그대로 실행했다면 PARNET_NAME 에 값이 저장되는 것이 맞습니다.

RoleHierachy Entity 클래스와 맞는지 확인 부탁드립니다.

@Entity
@Table(name="ROLE_HIERARCHY")
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@Builder
public class RoleHierarchy implements Serializable {

@Id
@GeneratedValue
private Long id;

@Column(name = "child_name")
private String childName;

@ManyToOne(cascade = {CascadeType.ALL},fetch = FetchType.LAZY)
@JoinColumn(name = "parent_name", referencedColumnName = "child_name")
private RoleHierarchy parentName;

@OneToMany(mappedBy = "parentName", cascade={CascadeType.ALL})
private Set<RoleHierarchy> roleHierarchy = new HashSet<RoleHierarchy>();
}

해당 강의 주제가 계층형권한 구현이라 사실 DB 에 수동으로 데이터를 입력하고 테스트해 보셔도 됩니다.

즉 권한 정보 데이터가 부모/자식 관계로 연쇄적으로 설정되어 있을 경우 스프링 시큐리티에서 계층 권한이 적용되는지 확인해 보는것이 핵심입니다.

그래서 데이터 계층 기술을 JPA 을 사용했지만 JDBC 나 Mybatis 같은 기술을 사용해도 무방합니다

다만 현재 시큐리티 강의에 국한되다 보니 JPA 에 대한 자세한 기술적인 설명은 강의 범위를 벗어나는 만큼 양해 부탁드립니다.

엔터티를 보시면 childName 과 parentName 이 셀프조인으로 설정되어 있습니다.

엔터티 연관관계는 childName 관점에서 외래키는 parentName 가 됩니다.

자식권한 정보를 먼저 저장한다음 부모권한 정보를 업데이트 하고 있습니다.

RoleHierarchy childRoleHierarchy = roleHierarchyRepository.save(roleHierarchy);
childRoleHierarchy.setParentName(parentRoleHierarchy);

그래서 콘솔을 보니 아래와 같이 실행이 되고 있습니다.

즉 처음 입력시 child_name 이 입력되고 나서 다시 parent_name 이 업데이트 되고 있습니다.

Hibernate: 

    insert 

    into

        role_hierarchy

        (child_name, parent_name, id) 

    values

        (?, ?, ?)

Hibernate: 

    update

        role_hierarchy 

    set

        child_name=?,

        parent_name=? 

    where

        id=?

일단 깃헙에 있는 소스를 수정하지 않고 실행해 보시기 바랍니다.

application.properties 는 다음과 같이 설정해 보시고 실행해 보시기 바랍니다.

spring.datasource.url=jdbc:postgresql://localhost:5432/springboot
spring.datasource.username=postgres
spring.datasource.password=pass

spring.jpa.hibernate.ddl-auto=create
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true

spring.thymeleaf.cache=false

spring.devtools.livereload.enabled=true
spring.devtools.restart.enabled=true

spring.main.allow-bean-definition-overriding=true

#spring.profiles.active=pointcut