Spring Framework-15

유호준·2021년 4월 3일
0

Spring Framework

목록 보기
16/21

✂️이번엔 글을 삭제할 때 파일도 삭제하도록 수정해보자!

외래키로 연관된 데이터를 삭제하는 방법은 테이블에 ON DELETE CASCADE를 주면 됩니다. 하지만 여기서는 @Transactional을 사용하기 위해 다른 방법으로 합니다.


FileMapper 인터페이스와 xml수정

post_id로 조회와 수정을 할 수 있도록 수정합니다.

public interface FileMapper {
    public void save(FileVO fileVO);
    public List<FileVO> findByPostId(int postId);
    public FileVO findById(int id);
    public void deleteByPostId(int postId);
}
<mapper namespace="ac.kr.smu.mapper.FileMapper">
    <insert id="save">
        INSERT INTO file(name,uuid,upload_path,post_id)
        VALUES(#{name},#{uuid},#{uploadPath},#{postId})
    </insert>
    <select id="findById" resultType="FileVO">
        SELECT * FROM file WHERE id = #{id}
    </select>
    <delete id="deleteByPostId">
        DELETE FROM file WHERE post_id = #{postId}
    </delete>
    <select id="findByPostId" resultType="FileVO">
        SELECT * FROM file WHERE post_id = #{postId}
    </select>
</mapper>

FileService 수정

post_id로 삭제할 수 있도록 수정합니다. 업로드된 파일DB에 저장된 파일의 정보를 삭제합니다.

public interface FileService {
    public List<FileVO> saveAll(int postId,List<MultipartFile> files);
    public FileSystemResource getFileSystemResource(int id);
    public void deleteByPostId(int postId);
}
@Override
public void deleteByPostId(int postId) {
        List<FileVO> fileList = fileMapper.findByPostId(postId);
    
        fileMapper.deleteByPostId(postId);
        fileList.stream().forEach(f ->{
            File file = new File(f.getPath());
            file.delete();
        });
}

PostService 수정

글을 삭제할 때 연관된 파일도 같이 삭제할 수 있도록 수정합니다. 이때 오류가 발생하면 반영이 되면 안되므로 @Transactional Annotation을 추가합니다.

@Override
@Transactional
public void delete(int id) {
        fileService.deleteByPostId(id);
        postMapper.delete(id);
}

@Transactional프록시 객체를 생성하여 실행하던 도중 runtime error가 발생하면 rollback을 할 수 있도록 하는 Anntoation입니다. 이를 사용하려면 설정을 해주어야 하는데 이 설정은 앞에서 하였습니다.

글과 연관된 파일은 같이 삭제되어야해 PostService에서FileSerivce를 참조해 삭제하는 식으로 구현하였습니다. Controller계층에서는 @Transactional Annotation이 수행되지 않습니다.

Service에서 DAO1:1 관계이어야 합니다. Service에서 다른 Service는 참조 가능하나 그 계층이 명확히 나뉘어있어야 합니다.


테스트




한번 RuntimeException을 발생시켜 @Transactional이 제대로 되는지 확인해보겠습니다.

SQL과 파라미터를 확인하기 위해 log4jdbc.log4j2 라이브러리를 설치하고 설정한 상태입니다. 아래와 같이 설정하면 실행되는 SQL과 파라미터들을 확인할 수 있습니다.

pom.xml

<!-- https://mvnrepository.com/artifact/org.bgee.log4jdbc-log4j2/log4jdbc-log4j2-jdbc4.1 -->
        <dependency>
            <groupId>org.bgee.log4jdbc-log4j2</groupId>
            <artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>
            <version>1.16</version>
        </dependency>

log4jdbc.log4j2.properties

webapp 바깥에 있는 resources폴더에 생성합니다.

log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator

log4j.xml

<!-- log4jdbc-log4j2 -->
    <logger name="jdbc.sqlonly">
        <level value="debug"/>
    </logger>
    <logger name="jdbc.sqltiming">
        <level value="info"/>
    </logger>
    <logger name="jdbc.audit">
        <level value="warn"/>
    </logger>
    <logger name="jdbc.resultset">
        <level value="error"/>
    </logger>
    <logger name="jdbc.resultsettable">
        <level value="warn"/>
    </logger>
    <logger name="jdbc.connection">
        <level value="info"/>
    </logger>

RootConfig 클래스

@Bean
public ComboPooledDataSource comboPooledDataSource(){
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        try{
            dataSource.setDriverClass("net.sf.log4jdbc.sql.jdbcapi.DriverSpy");
            dataSource.setJdbcUrl("jdbc:log4jdbc:mysql://localhost:3306/spring?allowMultiQueries=true");
            dataSource.setUser("spring");
            dataSource.setPassword("1111");
            dataSource.setCheckoutTimeout(3000);
        }catch (Exception e){e.printStackTrace();}     
        return dataSource;
}
@Override
@Transactional
public void deleteByPostId(int postId) {
        List<FileVO> fileList = fileMapper.findByPostId(postId);

        fileMapper.deleteByPostId(postId);
        fileList.stream().forEach(f ->{
            File file = new File(f.getPath());
            file.delete();
        });
        throw new RuntimeException();
}




다음과 같이 DELETE SQL이 수행되었지만 데이터가 삭제되지 않은 것을 확인할 수 있습니다.

파일이 삭제되는 것을 막는 것은 아닙니다.

0개의 댓글