2025-04-24 mabatis 연쇄 delete, snippet

성현규·2025년 4월 24일
post-thumbnail

📌 1. 스니펫(Snippet)

  • 코드 조각을 미리 정의해 두고, 단축어를 입력하면 자동으로 완성되는 기능
  • vs코드 코드 조각에 (java.jason)커스텀 설정가능
  • $1 = 거기서 커서 깜빡깜빡
  • ${TM_FILENAME_BASE} : 거기서 파일이름을 넣어줌
  • ${} 안에 정규 표현식 사용 가능
  • prefix: 단축어이다. 원래 접두사인데 “앞에 입력하는 키워드”로 스니펫을 불러오기 때문에 이름을 이렇게 붙였다.

📌 1-1. model 클래스 자동 생성 (my_model)

  • $1, $2, $3... 은 스니펫 커서 포커스 순서를 정하는 번호이다.
  • regDate: 데이터가 처음 생성된 날짜: DB 테이블과 연결되는 모델 클래스에서 기록의 생성/수정 시점을 저장하기 위해 자주 사용
  • editDate: 데이터가 마지막으로 수정된 날짜: DB 테이블과 연결되는 모델 클래스에서 기록의 생성/수정 시점을 저장하기 위해 자주 사용
  • listCount: 전체 데이터 개수
  • offset: 시작 위치: 몇번째부터 보여줄지: 페이징(페이지 개수 매김)할때 사용
  • "package ${1:kr.hyungyu}.mappers;" 로 기본값을 수정하였다.
"Model class initialize": {
        "prefix": "my_model",
        "body": [
            "package $1.models;",
            "",
            "import lombok.Data;",
            "import lombok.Getter;",
            "import lombok.Setter;",
            "",
            "@Data",
            "public class ${TM_FILENAME_BASE} {",
            "    private int id;",
            "    ",
            "    // TODO : Add more fields",
            "    $2",
            "",
            "    private String regDate;",
            "    private String editDate;",
            "",
            "    @Getter",
            "    @Setter",
            "    private static int listCount = 0;",
            "",
            "    @Getter",
            "    @Setter",
            "    private static int offset = 0;",
            "}"
        ],
        "description": "모델 클래스 기본 구성"
    },

📌 1-2. mapper 인터페이스 자동 생성 (my_mapper)

  • ${1:[프로그램 패키지_이름]}: 스니펫 커서의 첫 번째 위치 ($1), 사용자가 직접 패키지 이름을 입력하게 만들고, 기본 제안값은 프로그램 패키지_이름이다.

  • 슬래시(/)는 정규식을 감싸는 구분자

  • |: "또는" (OR)의 의미야.

  • (...): 괄호는 그룹(group)을 만든다는 뜻이다.

  • (?<=[a-z0-9])([A-Z]): (?<=...)는 앞에 있는 문자가 이런 조건을 만족해야 한다는 뜻이다.

  • ${n:/downcase}: n번째 그룹 → 소문자로

  • ${n}: n번째 그룹 참조

  • ${n:+}: n번째 그룹이 존재하면 추가

  • (Mapper): mapper라는 글자를 의미

  • (^[A-Z]): 클래스 이름의 첫 글자 (대문자), ^가 괄호 밖에 있으므로 첫번째 글자라는 뜻이다.

  • :/downcase: 첫 글자 소문자로

  • {3:+_}{3:/downcase}: 중간 대문자 앞에 _ 붙이고 소문자로 변환

  • g: 문자열 전체에서 일치하는 모든 부분 변환

  • (Mapper)|(^[A-Z])|(?<=[a-z0-9])([A-Z]): mapper가 붙거나 대문자이거나 대문자 앞에 소문자나 숫자가 오는 경우를 의미하면 앞에서 부터 탐지하여 하나라도 발견되면 탐지 되었다고 판단한다.

  • /2:/downcase{2:/downcase}{3:+}${3:/downcase}/ : 위의 식에서 초반이 대문자이면 소문자로 바꾸고 3번째 경우가 있으면 를 붙인다음에 대상을 소문자로 바꾼다.

    "MyBatis Mapper Generator": {
        "prefix": "my_mapper",
        "body": [
            "package ${1:[프로그램 패키지_이름]}.mappers;",
            "",
            "import java.util.List;",
            "",
            "import org.apache.ibatis.annotations.Delete;",
            "import org.apache.ibatis.annotations.Insert;",
            "import org.apache.ibatis.annotations.Mapper;",
            "import org.apache.ibatis.annotations.Options;",
            "import org.apache.ibatis.annotations.Result;",
            "import org.apache.ibatis.annotations.ResultMap;",
            "import org.apache.ibatis.annotations.Results;",
            "import org.apache.ibatis.annotations.Select;",
            "import org.apache.ibatis.annotations.Update;",
            "",
            "import ${1:[프로그램 패키지_이름]}.models.${TM_FILENAME_BASE/(Mapper)//};",
            "",
            "@Mapper",
            "public interface ${TM_FILENAME_BASE} {",
            "    @Insert(\"INSERT INTO ${TM_FILENAME_BASE/(Mapper)|(^[A-Z])|(?<=[a-z0-9])([A-Z])/${2:/downcase}${3:+_}${3:/downcase}/g} (\" +",
            "           \"    ✅✅✅    \" +",
            "           \") VALUES (\" +",
            "           \"    ✅✅✅    \" +",
            "           \")\")",
            "    @Options(useGeneratedKeys = true, keyProperty = \"✅\", keyColumn = \"✅\")",
            "    public int insert(${TM_FILENAME_BASE/(Mapper)//} input);",
            "",
            "    @Update(\"UPDATE ${TM_FILENAME_BASE/(Mapper)|(^[A-Z])|(?<=[a-z0-9])([A-Z])/${2:/downcase}${3:+_}${3:/downcase}/g} SET \" +",
            "           \"    ✅✅✅    \" +",
            "           \"WHERE ✅✅✅\")",
            "    public int update(${TM_FILENAME_BASE/(Mapper)//} input);",
            "",
            "    @Delete(\"DELETE FROM ${TM_FILENAME_BASE/(Mapper)|(^[A-Z])|(?<=[a-z0-9])([A-Z])/${2:/downcase}${3:+_}${3:/downcase}/g} WHERE ✅✅✅\")",
            "    public int delete(${TM_FILENAME_BASE/(Mapper)//} input);",
            "",
            "    @Select(\"SELECT * FROM ${TM_FILENAME_BASE/(Mapper)|(^[A-Z])|(?<=[a-z0-9])([A-Z])/${2:/downcase}${3:+_}${3:/downcase}/g} \" +",
            "           \"WHERE ✅✅✅\")",
            "    @Results(id=\"resultMap\", value={",
            "        @Result(property=\"✅\", column=\"✅\"),",
            "        @Result(property=\"✅\", column=\"✅\"),",
            "        @Result(property=\"✅\", column=\"✅\")",
            "    })",
            "    public ${TM_FILENAME_BASE/(Mapper)//} selectOne(${TM_FILENAME_BASE/(Mapper)//} input);",
            "",
            "    @Select(\"<script>\"+",
            "           \"SELECT ✅✅✅ FROM ${TM_FILENAME_BASE/(Mapper)|(^[A-Z])|(?<=[a-z0-9])([A-Z])/${2:/downcase}${3:+_}${3:/downcase}/g}\" +",
            "           \"<where>\" +",
            "           \"    <if test=\\\"✅ != null and ✅ != ''\\\">...</if>\" +",
            "           \"</where>\" +",
            "           \"</script>\")",
            "    @ResultMap(\"resultMap\")",
            "    public List<${TM_FILENAME_BASE/(Mapper)//}> selectList(${TM_FILENAME_BASE/(Mapper)//} input);",
            "",
            "    @Select(\"<script>\" +",
            "           \"SELECT COUNT(*) FROM ${TM_FILENAME_BASE/(Mapper)|(^[A-Z])|(?<=[a-z0-9])([A-Z])/${2:/downcase}${3:+_}${3:/downcase}/g}\" +",
            "           \"<where>\" +",
            "           \"    <if test=\\\"✅ != null and ✅ != ''\\\">...</if>\" +",
            "           \"</where>\" +",
            "           \"</script>\")",
            "    public int selectCount(${TM_FILENAME_BASE/(Mapper)//} input);",
            "}",
            ""
        ],
        "description": "MyBatis Mapper Generator"
    },

📌 1-3. service층 인터페이스 자동생성

  • ${TMFILENAME_BASE/찾을패턴/치환할_내용/}: 정규식 치환패턴
  • ${TM_FILENAME_BASE/(Service)//}: 파일 네임에서 base만 두고 service를 공백으로 치환하라는 뜻
    "Service Layer Generator": {
        "prefix": "my_service",
        "body": [
            "package ${1:[프로그램 패키지_이름]}.services;",
            "",
            "import java.util.List;",
            "",
            "import ${1:[프로그램 패키지_이름]}.models.${TM_FILENAME_BASE/(Service)//};",
            "",
            "public interface ${TM_FILENAME_BASE} {",
            "    public List<${TM_FILENAME_BASE/(Service)//}> getList(${TM_FILENAME_BASE/(Service)//} params) throws Exception;",
            "",
            "    public ${TM_FILENAME_BASE/(Service)//} getItem(${TM_FILENAME_BASE/(Service)//} params) throws Exception;",
            "",
            "    public ${TM_FILENAME_BASE/(Service)//} addItem(${TM_FILENAME_BASE/(Service)//} params) throws Exception;",
            "",
            "    public ${TM_FILENAME_BASE/(Service)//} editItem(${TM_FILENAME_BASE/(Service)//} params) throws Exception;",
            "",
            "    public int deleteItem(${TM_FILENAME_BASE/(Service)//} params) throws Exception;",
            "",
            "    public int getCount(${TM_FILENAME_BASE/(Service)//} params) throws Exception;",
            "}",
            ""
        ],
        "description": "Service Layer Generator",
    },

📌 1-4. service층 인터페이스 구현체 자동생성

  • /^(.*)ServiceImpl$/: "UserServiceImpl"이라는 문자열에서 앞부분 "User"를 잡아서 그룹 1로 분리
  • ^: 문자열 시작
  • (.*): 전체 문자열 중에서 아무 문자든 0개 이상 → 그룹 1
  • ServiceImpl: 정확히 이 글자와 일치해야 함
  • $: 문자열 끝
  • 괄호가 쳐진 부분만 "그룹"이 된다! -> 즉 /^(.*)ServiceImpl$/이면 ServiceImpl앞에 부분의 모든 문자만 한 그룹이 된다.
  • :/camelcase 첫 글자는 소문자, 이후 단어의 앞글자는 대문자 유지: "UserBoard" → "userBoard"
    "Service Layer Implement Generator": {
        "prefix": "my_service_impl",
        "body": [
            "package ${2:[프로그램 패키지_이름]}.services.impl;",
            "",
            "import java.util.List;",
            "",
            "import org.springframework.beans.factory.annotation.Autowired;",
            "import org.springframework.stereotype.Service;",
            "",
            "import ${2:[프로그램_패키지_이름]}.mappers.${TM_FILENAME_BASE/(ServiceImpl)//}Mapper;",
            "import ${2:[프로그램_패키지_이름]}.models.${TM_FILENAME_BASE/(ServiceImpl)//};",
            "import ${2:[프로그램_패키지_이름]}.services.${TM_FILENAME_BASE/(ServiceImpl)//}Service;",
            "import lombok.extern.slf4j.Slf4j;",
            "",
            "@Slf4j",
            "@Service",
            "public class ${TM_FILENAME_BASE} implements ${TM_FILENAME_BASE/(Impl)//} {",
            "",
            "    @Autowired",
            "    private ${TM_FILENAME_BASE/(ServiceImpl)//}Mapper ${TM_FILENAME_BASE/^(.*)ServiceImpl$/${1:/camelcase}/}Mapper;",
            "",
            "    @Override",
            "    public ${TM_FILENAME_BASE/(ServiceImpl)//} addItem(${TM_FILENAME_BASE/(ServiceImpl)//} input) throws Exception {",
            "        int rows = ${TM_FILENAME_BASE/^(.*)ServiceImpl$/${1:/camelcase}/}Mapper.insert(input);",
            "",
            "        if (rows == 0) {",
            "            throw new Exception(\"저장된 ${TM_FILENAME_BASE/(ServiceImpl)//} 데이터가 없습니다.\");",
            "        }",
            "",
            "        return ${TM_FILENAME_BASE/^(.*)ServiceImpl$/${1:/camelcase}/}Mapper.selectOne(input);",
            "    }",
            "",
            "    @Override",
            "    public ${TM_FILENAME_BASE/(ServiceImpl)//} editItem(${TM_FILENAME_BASE/(ServiceImpl)//} input) throws Exception {",
            "        int rows = ${TM_FILENAME_BASE/^(.*)ServiceImpl$/${1:/camelcase}/}Mapper.update(input);",
            "",
            "        if (rows == 0) {",
            "            throw new Exception(\"수정된 ${TM_FILENAME_BASE/(ServiceImpl)//} 데이터가 없습니다.\");",
            "        }",
            "",
            "        return ${TM_FILENAME_BASE/^(.*)ServiceImpl$/${1:/camelcase}/}Mapper.selectOne(input);",
            "    }",
            "",
            "    @Override",
            "    public int deleteItem(${TM_FILENAME_BASE/(ServiceImpl)//} input) throws Exception {",
            "        int rows = ${TM_FILENAME_BASE/^(.*)ServiceImpl$/${1:/camelcase}/}Mapper.delete(input);",
            "",
            "        if (rows == 0) {",
            "            throw new Exception(\"${TM_FILENAME_BASE/(ServiceImpl)//} 삭제된 데이터가 없습니다.\");",
            "        }",
            "",
            "        return rows;",
            "    }",
            "",
            "    @Override",
            "    public ${TM_FILENAME_BASE/(ServiceImpl)//} getItem(${TM_FILENAME_BASE/(ServiceImpl)//} input) throws Exception {",
            "        ${TM_FILENAME_BASE/(ServiceImpl)//} output = ${TM_FILENAME_BASE/^(.*)ServiceImpl$/${1:/camelcase}/}Mapper.selectOne(input);",
            "",
            "        if (output == null) {",
            "            throw new Exception(\"${TM_FILENAME_BASE/(ServiceImpl)//} 조회된 데이터가 없습니다.\");",
            "        }",
            "",
            "        return output;",
            "    }",
            "",
            "    @Override",
            "    public List<${TM_FILENAME_BASE/(ServiceImpl)//}> getList(${TM_FILENAME_BASE/(ServiceImpl)//} input) throws Exception {",
            "        return ${TM_FILENAME_BASE/^(.*)ServiceImpl$/${1:/camelcase}/}Mapper.selectList(input);",
            "    }",
            "",
            "    @Override",
            "    public int getCount(${TM_FILENAME_BASE/(ServiceImpl)//} input) throws Exception {",
            "        return ${TM_FILENAME_BASE/^(.*)ServiceImpl$/${1:/camelcase}/}Mapper.selectCount(input);",
            "    }",
            "}"
        ],
        "description": "Service Layer Implement Generator"
    }

2. professorservice 구축

  • delete가 관건이다. 우선 외래키를 조사하여 지워야할 테이블들을 파악하고 그 각각의 매퍼에 그 테이블에서 어떤 거를 지워야 할지를 정한 다음 다 만들고 순서에 맞게 실행하면 된다.
  • 즉 우선 해야할 것은 각 테이블에서 지워야할 것들을 일단 다 정의하여 순서는 고려하지 않고 sql문을 짜는 것이다.
  • 순서를 맞추는 것은 구현시에 해당 서비스에서 고민한다.
  • 중요한 것은 mapper에서 지워지는 주체는(delete from) 해당 매퍼가 다루는 클래스이다. 그 테이블을 해결하려고 그 매퍼 클래스에서 지우고 있기 때문이다.
  • model과 service 인터페이스는 단순하고 겹치는 내용이 많아서 포함하지 않겠다.
  • 빌드하면서 겪은 에러들은 select문이 여러개 겹쳤을때 괄호 문제가 있었고 model정의할때 필드값에 오타를 냈던 상황이 있다.

📌 2-1. 지우기 위한 enrollmentsMapper

  • enrollments가 professorId를 가지고 있지 않아 enrollments객체에 지우고자하는 professorId를 주입하고 파라미터로 사용하는 식으로는 해결하지 못한다.
  • 하여 professorId를 가지고 있는 student에서 지우고자 하는 교수의 아이디를 받아 대신 파라미터로 사용한다.
  • 서브쿼리를 중복으로 활용해서 도달하고자 하는 목표치를 구현해내었다.
/** 특정 교수가 지도교수인 학생이 수강하는 내역 삭제, 특정 교수의 담당 과목의 수강신청 내역 삭제
     *  
     * @param input - 삭제하려는 학과 번호가 저장된 student 클래스의 객체
     * @return - 삭제된 데이터의 수
     */
    @Delete("DELETE FROM enrollments WHERE student_id IN (select id from students where professor_id = #{professorId}) "
           + "or subject_id in (select id from subjects where professor_id in (select id from students where professor_id = #{professorId}))" ) 
public int deleteByStudentIdForProfessor(Student input);

📌 2-2. 지우기 위한 subjectMapper

  • subject는 교수 아이디를 포함하고 있으므로 바로 진행할 수 있었다.
    /**
     * 특정 교수가 가르치는 과목 일괄 삭제
     * @param subject - 학과 번호를 저장하고 있는 subject 객체 
     * @return - 삭제된 데이터의 수
     */
    @Delete ("DELETE FROM subjects WHERE professor_id = #{professorId}")
    public int deleteByProfessorIdForProfessor(Subject subject);

📌 2-3. 지우기 위한 studentMapper

/**
     *  특정 교수가 지도교수인 학생 삭제, 특정 교수가 가르치는 과목을 수강하는 학생 삭제
     *  학생이 멤버변수로 삭제하고자 하는 교수의 정보를 들고 있어야함.
     * @param input - 학과 번호를 저장하고 있는 student 객체
     * @return - 삭제된 데이터의 수
     */
    
     @Delete("DELETE FROM students WHERE professor_id = #{professorId} OR "
     + "id IN (SELECT student_id FROM enrollments WHERE subject_id in (select id from subjects where professor_id = #{professorId}))")
     public int deleteByProfessorId(Student input);

📌 2-4. professorMapper

@Mapper
public interface ProfessorMapper {
    @Insert("INSERT INTO professors (" +
           "id, name, user_id, position, sal, hiredate, comm, email, phone, photo_url, status, department_id " +
           ") VALUES (" +
           "#{id}, #{name}, #{userId}, #{position}, #{sal}, #{hiredate}, #{comm}, #{email}, #{phone}, #{photoUrl}, #{status}, #{departmentId} " +
           ")")
    @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
    public int insert(Professor input);

    @Update("UPDATE professors SET " +
           " name = #{name}, user_id = #{userId}, position = #{position}, sal =  #{sal}, hiredate = #{hiredate}, comm = #{comm}, email = #{email}, "
            + " phone = #{phone}, photo_url = #{photoUrl}, status = #{status}, department_id = #{departmentId} "
            + " where id = #{id}")
    public int update(Professor input);

    @Delete("DELETE FROM professors WHERE id = #{id}")
    public int delete(Professor input);

    @Select("SELECT id, name, user_id, position, sal, hiredate, comm, email, phone, photo_url, status, department_id FROM professors " +
           "WHERE id = #{id}")
           @Results(id = "resultMap", value = {
            @Result(property = "id", column = "id"),
            @Result(property = "name", column = "name"),
            @Result(property = "userId", column = "user_id"),
            @Result(property = "position", column = "position"),
            @Result(property = "sal", column = "sal"),
            @Result(property = "hiredate", column = "hiredate"),
            @Result(property = "comm", column = "comm"),
            @Result(property = "email", column = "email"),
            @Result(property = "phone", column = "phone"),
            @Result(property = "photoUrl", column = "photo_url"),
            @Result(property = "status", column = "status"),
            @Result(property = "departmentId", column = "department_id")
        })
    public Professor selectOne(Professor input);

    @Select("<script>"+
           "SELECT id, name, user_id, position, sal, hiredate, comm, email, phone, photo_url, status, department_id FROM professors" +
           "<where>" +
           "    <if test=\"id != null and id != ''\"> id = #{id} </if>" +
           "</where>" +
           "</script>")
    @ResultMap("resultMap")
    public List<Professor> selectList(Professor input);

    @Select("SELECT COUNT(*) FROM professors")
    public int selectCount(Professor input);

    /**
     *  특정 학과에 소속 되어 있는 교수 일괄 삭제
     * @param input - 학과 번호를 저장하고 있는 Professor 객체
     * @return - 삭제된 데이터 수
     */
    @Delete("DELETE FROM professors WHERE department_id = #{departmentId}")
    public int deleteByDepartmentId(Professor input);
}

📌 2-5. serviceImpl

  • delete 부분 유심히 보기
  • getId로 지우고자하는 교수의 아이디를 받아서 다른 객체의 멤버변수에 삽입한다음 그 객체를 활용하여 해결해나간다.
public class ProfessorServiceimpl implements ProfessorService {
    ProfessorMapper professorMapper = null;
    StudentMapper studentMapper = null;
    EnrollmentMapper enrollmentMapper = null;
    SubjectMapper subjectMapper = null;

    public ProfessorServiceimpl(SqlSession sqlSession){ 
        professorMapper = sqlSession.getMapper(ProfessorMapper.class);
        studentMapper = sqlSession.getMapper(StudentMapper.class);
        enrollmentMapper = sqlSession.getMapper(EnrollmentMapper.class);
        subjectMapper = sqlSession.getMapper(SubjectMapper.class);
    }

    @Override
    public Professor addItem(Professor params) throws ServiceNoResultException, Exception {
        if (professorMapper.insert(params) == 0){
            throw new ServiceNoResultException("정상적으로 처리되지 않았습니다.");
        }
        return professorMapper.selectOne(params);
    }

    @Override
    public Professor editItem(Professor params) throws ServiceNoResultException, Exception {
        if (professorMapper.update(params) == 0){
            throw new ServiceNoResultException("정상적으로 처리되지 않았습니다.");
        }
        return professorMapper.selectOne(params);
    }

    @Override
    public int getCount(Professor params) throws ServiceNoResultException, Exception {
        return professorMapper.selectCount(params);
    }

    @Override
    public Professor getItem(Professor params) throws ServiceNoResultException, Exception {
        Professor ouput = professorMapper.selectOne(params);
        if (ouput == null){
            throw new ServiceNoResultException("정상적으로 처리되지 않았습니다.");
        }
        return ouput;
    }

    @Override
    public List<Professor> getList(Professor params) throws ServiceNoResultException, Exception {
        return professorMapper.selectList(params);
    }

    @Override
    public int deleteItem(Professor params) throws ServiceNoResultException, Exception {
        int professorskey = params.getId();

        Student student1 = new Student();
        student1.setProfessorId(professorskey);

        Subject subject1 = new Subject();
        subject1.setProfessorId(professorskey);
        
        
        
        // 교수가 담당하는 과목의 수강신청 내역 삭제 , 교수의 지도학생이 듣는 수강신청 내역 삭제
        enrollmentMapper.deleteByStudentIdForProfessor(student1);
        // 교수가 담당하는 과목을 수강하는 학생이거나 교수의 지도학생 삭제
        studentMapper.deleteByProfessorId(student1);
        // 교수가 담당하는 과목 삭제
        subjectMapper.deleteByProfessorIdForProfessor(subject1);
        // 교수 삭제
        int output = professorMapper.delete(params);

        if (output == 0){
            throw new ServiceNoResultException("정상적으로 처리되지 않았습니다.");
        }

        return output;
    }
}

3. subjectService 구축

📌 3-1. subjectMapper

  • 일반적인 mapper이나 여전히 작성할때 칼럼명과 필드명을 잘 생각해야한다.
public interface SubjectMapper {
    @Insert("INSERT INTO subjects (id, name, credit, department_id, professor_id)"
            + " VALUES (#{id}, #{name}, #{credit}, #{departmentId}, #{professorId})" )
    @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
    public int insert(Subject input);

    @Update("UPDATE subjects SET " +
           "id = #{id}, name = #{name}, credit = #{credit}, department_id = #{departmentId}, professor_id = #{professorId} " +
           "WHERE id = #{id}")
    public int update(Subject input);

    @Delete("DELETE FROM subjects WHERE id = #{id}")
    public int delete(Subject input);

    
    @Results(id="resultMap", value={
        @Result(property="id", column="id"),
        @Result(property="name", column="name"),
        @Result(property="credit", column="credit"),
        @Result(property="department_id", column="departmentId"),
        @Result(property="professor_id", column="professorId")
    })
    @Select("SELECT * FROM subjects " +
        "WHERE id = #{id}")
    public Subject selectOne(Subject input);

    @Select(
           "SELECT id, name, credit, department_id, professor_id "
           + "from subjects")
    @ResultMap("resultMap")
    public List<Subject> selectList(Subject input);

    @Select(
           "SELECT COUNT(*) FROM subjects")
    public int selectCount(Subject input);

        /**
     * 특정 학과에서 개설된 과목이거나, 특정학과에 소속된 교수가 담당하는 '과목 일괄 삭제'
     * @param subject - 학과 번호를 저장하고 있는 subject 객체 
     * @return - 삭제된 데이터의 수
     */
    @Delete ("DELETE FROM subjects WHERE department_id = #{departmentId} OR professor_id IN "
    + " (SELECT id FROM professors WHERE department_id = #{departmentId})")
    public int deleteByProfessorId(Subject subject);

    /**
     * 특정 교수가 가르치는 과목 일괄 삭제
     * @param subject - 학과 번호를 저장하고 있는 subject 객체 
     * @return - 삭제된 데이터의 수
     */
    @Delete ("DELETE FROM subjects WHERE professor_id = #{professorId}")
    public int deleteByProfessorIdForProfessor(Subject subject);
}

📌 3-2. subjectServiceImpl

  • 삭제가 특별하므로 삭제부분을 주의깊게 본다.
public class SubjectServiceImpl implements SubjectService {
    private SubjectMapper subjectMapper = null;
    private EnrollmentMapper enrollmentMapper = null;

    public SubjectServiceImpl(SqlSession sqlSession){
        subjectMapper = sqlSession.getMapper(SubjectMapper.class);
        enrollmentMapper = sqlSession.getMapper(EnrollmentMapper.class);
    }

    @Override
    public Subject addItem(Subject params) throws ServiceNoResultException, Exception {
        if (subjectMapper.insert(params) == 0){
            throw new ServiceNoResultException("정상적으로 처리되지 않았습니다.");
        }
        return subjectMapper.selectOne(params);
    }

    @Override
    public Subject editItem(Subject params) throws ServiceNoResultException, Exception {
        if (subjectMapper.update(params) == 0){
            throw new ServiceNoResultException("정상적으로 처리되지 않았습니다.");
        }
        return subjectMapper.selectOne(params);
    }

    @Override
    public int getCount(Subject params) throws ServiceNoResultException, Exception {
        return subjectMapper.selectCount(params);
    }

    @Override
    public Subject getItem(Subject params) throws ServiceNoResultException, Exception {
        Subject output = subjectMapper.selectOne(params);
        if (output == null){
            throw new ServiceNoResultException("정상적으로 처리되지 않았습니다.");
        }
        return output;
    }

    @Override
    public List<Subject> getList(Subject params) throws ServiceNoResultException, Exception {
        return subjectMapper.selectList(params);
    }

    @Override
    public int deleteItem(Subject params) throws ServiceNoResultException, Exception {
        // 과목을 삭제하기 위해 우선 해당 과목의 수강내역 삭제
        Enrollment input = new Enrollment();
        input.setSubjectId(params.getId());

        enrollmentMapper.deleteBySubject(input);

        // 과목삭제
        int output = subjectMapper.delete(params);
        return output;
    }
    
}

📌 3-3. subject를 삭제하기 위한 enrollmentMapper

  • enrollment에 subject_id가 있으므로 스스로 해결한다.
/** 특정 과목에 해당하는 수강내역 삭제
     *  
     * @param input - 삭제하려는 과목 번호가 저장된 enrollment 클래스의 객체
     * @return - 삭제된 데이터의 수
     */
    @Delete("DELETE FROM enrollments WHERE subject_id = #{subjectId}") 
public int deleteBySubject(Enrollment enrollment);
profile
학생

0개의 댓글