[AXBoot] QueryDSL, MyBatis

yesjm·2021년 4월 19일
0

QueryDSL
쿼리를 직접 만들지 않아도 db에 데이터를 관리할 수 있도록 함
오늘은 어떤식으로 QueryDSL을 활용하는지
insert, update, delete 등의 메소드 작업

오전 수업

QueryDSL

  • Swagger에 QueryDSL 추가하기

CompanyService.java 파일에 아래 코드 추가

    //QueryDSL
    public List<Company> getByQueryDsl(RequestParams<Company> requestParams){
        String company = requestParams.getString("company", "");
        String ceo = requestParams.getString("ceo","");
        String bizno = requestParams.getString("bizno","");

        BooleanBuilder builder = new BooleanBuilder();

        if (isNotEmpty(company)) {
            builder.and(qCompany.companyNm.eq(company));
        }
        if (isNotEmpty(ceo)) {
            builder.and(qCompany.ceo.eq(ceo));
        }
        if (isNotEmpty(bizno)){
            builder.and(qCompany.bizno.eq(bizno));
        }

        List<Company> companyList = select()
                .from(qCompany)
                .where(builder)
                .orderBy(qCompany.companyNm.asc())
                .fetch();

        return companyList;
    }
  • fetch() -> 조회

이 작업을 하고 나면 qCompany에 에러가 생긴다.

qCompany는 QueryDSL이 자동으로 생성해주는 명칭인데, QueryDSL을 사용하려면 작업하기전에 미리 해줘야 하는게 있다.
미리 컴파일된 QueryDSL에서 사용할 수 있는 파일을 만들어놔야 한다.

-> BaseService.java 파일에 아래 사진과 같이 추가 해준다.

compile을 해보면target/classes/edu/axboot/domain/company/QCompany.class 파일이 생성되어 있다.

clean을 하면 컴파일한 데이터가 사라져서 다시 에러가 발생하지만, compile을 다시 해주면 정상적으로 돌아온다.


CompanyController.java 파일에 아래 코드를 추가(GET)

    @RequestMapping(value = "/QueryDsl", method = RequestMethod.GET, produces = APPLICATION_JSON)
    public Responses.ListResponse list2(RequestParams<Company> requestParams) {
        List<Company> list = companyService.getByQueryDsl(requestParams);
        return Responses.ListResponse.of(list);
    }

CompanyService.java 파일에 아래 코드 추가

    public void saveByQueryDsl(List<Company> request) {
        for (Company company:request) {
            if (company.isCreated()) {
                save(company);
            } else if (company.isModified()) {
                update(qCompany)
                        .set(qCompany.companyNm, company.getCompanyNm())
                        .set(qCompany.ceo, company.getCeo())
                        .where(qCompany.id.eq(company.getId()))
                        .execute();
            } else if (company.isDeleted()) {
                delete(qCompany)
                        .where(qCompany.id.eq(company.getId()))
                        .execute();
            }
        }
    }

CompanyController.java 파일에 아래 코드를 추가(PUT)

    @RequestMapping(value = "/QueryDsl", method = RequestMethod.PUT, produces = APPLICATION_JSON)
    public ApiResponse save2(@RequestBody List<Company> request) {
        companyService.saveByQueryDsl(request);
        return ok();
    }

개발자 도구 - Swagger에 QueryDSL이 추가된 것을 볼 수 있다.


Transaction

swagger에서 QueryDSL을 이용해 db를 수정하려 하면(modified = true)

에러가 발생한다.
ERROR com.chequer.axboot.core.controllers.BaseController:errorLogging:97 Executing an update/delete query
javax.persistence.TransactionRequiredException

트랜잭션을 걸어주지 않았기 때문이다. CompanyService.java에 @Transactional을 추가해준다.

트랜잭션을 추가하고 다시 실행하면 정상적으로 데이터가 수정되는 것을 확인할 수 있다.

CompanyController.java 파일에 아래 코드를 추가하면 전체 조회가 아닌 입력한 파라미터 객체에 대한 값만 확인할 수 있게 된다.

    @RequestMapping(value = "/QueryDsl", method = RequestMethod.GET, produces = APPLICATION_JSON)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "company", value = "회사명", dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "ceo", value = "대표자", dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "bizno", value = "사업자번호", dataType = "String", paramType = "query"),
    })
    public Responses.ListResponse list2(RequestParams<Company> requestParams) {
        List<Company> list = companyService.getByQueryDsl(requestParams);
        return Responses.ListResponse.of(list);
    }


오후 수업

Swagger를 화면단과 연결하기

조회

company.jsp 파일의 코드를 아래 코드로 수정

        <div role="page-header">
            <ax:form name="searchView0">
                <ax:tbl clazz="ax-search-tbl" minWidth="500px">
                    <ax:tr>
                        <ax:td label='회사명' width="300px">
                            <input type="text" name="company" id="company" class="form-control" />
                        </ax:td>
                        <ax:td label='대표자' width="300px">
                            <input type="text" name="ceo" id="ceo" class="form-control" />
                        </ax:td>
                        <ax:td label='사업자번호' width="300px">
                            <input type="text" name="bizno" id="bizno" class="form-control" />
                        </ax:td>
                    </ax:tr>
                </ax:tbl>
            </ax:form>
            <div class="H10"></div>
        </div>

company.js 파일에서 PAGE_SEARCH 함수의 url을 오전에 만든 컨트롤러(/api/v1/company/QueryDsl)와 연결하고

아래 코드를 추가한다.

/**
 * searchView
 */
fnObj.searchView = axboot.viewExtend(axboot.searchView, {
    initView: function () {
        this.target = $(document["searchView0"]);
        this.target.attr("onsubmit", "return ACTIONS.dispatch(ACTIONS.PAGE_SEARCH);");
        this.company = $("#company");
        this.ceo = $("#ceo");
        this.bizno = $("#bizno");
    },
    getData: function () {
        return {
            pageNumber: this.pageNumber,
            pageSize: this.pageSize,
            company: this.company.val(),
            ceo: this.ceo.val(),
            bizno: this.bizno.val()
        }
    }
});

회사명을 입력하고 조회 버튼을 클릭하면 아래와 같은 결과가 나온다!

크롬의 개발자 도구(f12)에서 확인해보니 설정한 컨트롤러가 잘 작동하고 있다 ㅎ

저장 & 수정

company.js 파일에서 PAGE_SAVE 함수도 url: "/api/v1/company/QueryDsl",로 바꿔주면 데이터를 수정하거나 추가한 뒤 저장 버튼을 눌렀을 때 잘 작동하는 확인할 수 있다.

삭제

데이터 삭제를 위해서는 company.js의 getData함수의 delete this.deleted; return this.key; 를 삭제하고 return this.id;를 추가해주면 삭제 버튼이 제대로 동작한다.


MyBatis

  • Swagger에 MyBatis 추가하기

컨트롤러부터 만들고 서비스를 만든 후에 Mapper 생성하는 방식으로 진행할 예정
불필요한 기능들은 만들지 않기 위함이다. 향후에도 이런 방식이 습관이 될 수 있도록 하자

ModleExtractor에 생성되어 있는 기본 코드를 참고

Mapper.java파일과 같은 경로로 resources에 Mapper.xml을 올린다. 다른 곳에서 파일을 관리하는 이유는 Mapper.xml파일은 수정이 빈번하기 때문에 프로그램을 올렸다 내렸다를 반복하기 어려우니 파일로서 리소스 영역에 수정시에도 바로바로 반영될 수 있도록 하기 위함이다.

MyBatis는 명칭을 select, insert, delete, update를 그대로 가져가는 것이 좋다.

CompanyController.java에 아래 코드를 추가한다.

    @RequestMapping(value = "/MyBatis", method = RequestMethod.GET, produces = APPLICATION_JSON)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "company", value = "회사명", dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "ceo", value = "대표자", dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "bizno", value = "사업자번호", dataType = "String", paramType = "query"),
    })
    public Responses.ListResponse list3(RequestParams<Company> requestParams) {
        List<Company> list = companyService.selectBy(requestParams);
        return Responses.ListResponse.of(list);
    }

CompanyService.java에 아래 코드를 추가한다

    public List<Company> selectBy(RequestParams<Company> requestParams) {
            String company = requestParams.getString("company", "");
        String ceo = requestParams.getString("ceo","");
        String bizno = requestParams.getString("bizno","");
        }

src/main/java/edu/axboot/domain/company경로에 CompanyMapper.java 파일을 생성하고

ModelExtractor를 참고하여 코드를 수정한다.

public interface CompanyMapper extends MyBatisMapper {

}

CompanyService.java에 아래 코드를 추가한다.

        List<Company> companyList = this.companyMapper.selectBy(company);

        return companyList;

selectBy에 alt+Enter를 눌러 CompanyMapper.java에 메소드를 생성한다.

src/main/resources/edu/axboot/domain/company 경로에 CompanyMapper.xml 파일을 생성하고 ModelExtractor의 코드를 붙여넣는다.

  • 이때 CompanyM -> Company로 수정한다.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="edu.axboot.domain.company.CompanyMapper">

    <select id="selectBy" resultType="company" parameterType="company" statementType="PREPARED">
        SELECT
            ID AS id,
            COMPANY_NM AS companyNm,
            CEO AS ceo,
            BIZNO AS bizno,
            TEL AS tel,
            ZIP AS zip,
            ADDRESS AS address,
            ADDRESS_DETAIL AS addressDetail,
            EMAIL AS email,
            REMARK AS remark,
            USE_YN AS useYn
        FROM
            COMPANY_M
    </select>
</mapper>

그 결과 Swagger에 MyBatis가 추가된 것을 확인할 수 있다.

같은 방식으로 insert, update, delete를 추가하고
company.js 파일의 PAGE_SEARCH, PAGE_SAVE의 url을 "/api/v1/company/MyBatis"로 수정해주면 크롬의 개발자도구(f12)에서 확인이 가능하다.


소스코드

CompanyController.java

package edu.axboot.controllers;

import com.chequer.axboot.core.api.response.Responses;
import com.chequer.axboot.core.controllers.BaseController;
import com.chequer.axboot.core.parameter.RequestParams;
import com.wordnik.swagger.annotations.ApiImplicitParam;
import com.wordnik.swagger.annotations.ApiImplicitParams;
import org.springframework.stereotype.Controller;
import com.chequer.axboot.core.api.response.ApiResponse;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import edu.axboot.domain.company.Company;
import edu.axboot.domain.company.CompanyService;

import javax.inject.Inject;
import java.util.List;

@Controller
@RequestMapping(value = "/api/v1/company")
public class CompanyController extends BaseController {

    @Inject
    private CompanyService companyService;

    @RequestMapping(method = RequestMethod.GET, produces = APPLICATION_JSON)
    public Responses.ListResponse list(RequestParams<Company> requestParams) {
        List<Company> list = companyService.gets(requestParams);
        return Responses.ListResponse.of(list);
    }

    @RequestMapping(method = {RequestMethod.PUT}, produces = APPLICATION_JSON)
    public ApiResponse save(@RequestBody List<Company> request) {
        companyService.save(request);
        return ok();
    }

    @RequestMapping(value = "/QueryDsl", method = RequestMethod.GET, produces = APPLICATION_JSON)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "company", value = "회사명", dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "ceo", value = "대표자", dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "bizno", value = "사업자번호", dataType = "String", paramType = "query"),
    })
    public Responses.ListResponse list2(RequestParams<Company> requestParams) {
        List<Company> list = companyService.getByQueryDsl(requestParams);
        return Responses.ListResponse.of(list);
    }

    @RequestMapping(value = "/QueryDsl", method = RequestMethod.PUT, produces = APPLICATION_JSON)
    public ApiResponse save2(@RequestBody List<Company> request) {
        companyService.saveByQueryDsl(request);
        return ok();
    }

    @RequestMapping(value = "/MyBatis", method = RequestMethod.GET, produces = APPLICATION_JSON)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "company", value = "회사명", dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "ceo", value = "대표자", dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "bizno", value = "사업자번호", dataType = "String", paramType = "query"),
    })
    public Responses.ListResponse list3(RequestParams<Company> requestParams) {
        List<Company> list = companyService.selectBy(requestParams);
        return Responses.ListResponse.of(list);
    }

    @RequestMapping(value = "/MyBatis", method = RequestMethod.PUT, produces = APPLICATION_JSON)
    public ApiResponse save3(@RequestBody List<Company> request) {
        companyService.saveByMyBatis(request);
        return ok();
    }
}

CompanyService.java

package edu.axboot.domain.company;

import com.chequer.axboot.core.parameter.RequestParams;
import com.querydsl.core.BooleanBuilder;
import edu.axboot.domain.BaseService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.inject.Inject;
import java.util.List;

@Service
public class CompanyService extends BaseService<Company, Long> {

    private CompanyRepository companyRepository;

    @Inject
    private CompanyMapper companyMapper;

    @Inject
    public CompanyService(CompanyRepository companyRepository) {
        super(companyRepository);
        this.companyRepository = companyRepository;
    }

    public List<Company> gets(RequestParams<Company> requestParams) {
        return findAll();
    }

    //QueryDSL
    public List<Company> getByQueryDsl(RequestParams<Company> requestParams){
        String company = requestParams.getString("company", "");
        String ceo = requestParams.getString("ceo","");
        String bizno = requestParams.getString("bizno","");

        BooleanBuilder builder = new BooleanBuilder();

        if (isNotEmpty(company)) {
            builder.and(qCompany.companyNm.eq(company));
        }
        if (isNotEmpty(ceo)) {
            builder.and(qCompany.ceo.eq(ceo));
        }
        if (isNotEmpty(bizno)){
            builder.and(qCompany.bizno.eq(bizno));
        }

        List<Company> companyList = select()
                .from(qCompany)
                .where(builder)
                .orderBy(qCompany.companyNm.asc())
                .fetch();

        return companyList;
    }

    @Transactional
    public void saveByQueryDsl(List<Company> request) {
        for (Company company:request) {
            if (company.isCreated()) {
                save(company);
            } else if (company.isModified()) {
                update(qCompany)
                        .set(qCompany.companyNm, company.getCompanyNm())
                        .set(qCompany.ceo, company.getCeo())
                        .set(qCompany.bizno, company.getBizno())
                        .where(qCompany.id.eq(company.getId()))
                        .execute();
            } else if (company.isDeleted()) {
                delete(qCompany)
                        .where(qCompany.id.eq(company.getId()))
                        .execute();
            }
        }
    }

    //MyBatis
    public List<Company> selectBy(RequestParams<Company> requestParams) {

        Company company = new Company();
        company.setCompanyNm(requestParams.getString("company", ""));
        company.setCeo(requestParams.getString("ceo",""));
        company.setBizno(requestParams.getString("bizno",""));

        List<Company> companyList = this.companyMapper.selectBy(company);

        return companyList;
    }

    public void saveByMyBatis(List<Company> request) {
        for (Company company:request) {
            if (company.isCreated()) {
                this.companyMapper.insert(company);
            } else if (company.isModified()) {
                this.companyMapper.update(company);
            } else if (company.isDeleted()) {
                this.companyMapper.delete(company);
            }
        }
    }
}

CompanyMapper.java

package edu.axboot.domain.company;

import com.chequer.axboot.core.mybatis.MyBatisMapper;

import java.util.List;

public interface CompanyMapper extends MyBatisMapper {

    List<Company> selectBy(Company company);

    void insert(Company company);

    void update(Company company);

    void delete(Company company);
}

CompanyMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="edu.axboot.domain.company.CompanyMapper">

    <select id="selectBy" resultType="company" parameterType="company" statementType="PREPARED">
        SELECT
            ID AS id,
            COMPANY_NM AS companyNm,
            CEO AS ceo,
            BIZNO AS bizno,
            TEL AS tel,
            ZIP AS zip,
            ADDRESS AS address,
            ADDRESS_DETAIL AS addressDetail,
            EMAIL AS email,
            REMARK AS remark,
            USE_YN AS useYn
        FROM
            COMPANY_M
        <where>
            <if test="companyNm != null and companyNm != ''">
                AND COMPANY_NM = #{companyNm}
            </if>
            <if test="ceo != null and ceo != ''">
                AND CEO = #{ceo}
            </if>
            <if test="bizno != null and bizno != ''">
                AND BIZNO = #{bizno}
            </if>
        </where>
    </select>

    <insert id="insert" parameterType="company" statementType="PREPARED">
        INSERT INTO COMPANY_M (
            COMPANY_NM,
            CEO,
            BIZNO,
            TEL,
            ZIP,
            ADDRESS,
            ADDRESS_DETAIL,
            EMAIL,
            REMARK,
            USE_YN
        ) VALUES (
            #{companyNm},
            #{ceo},
            #{bizno},
            #{tel},
            #{zip},
            #{address},
            #{addressDetail},
            #{email},
            #{remark},
            #{useYn}
        )
    </insert>

    <update id="update" parameterType="company" statementType="PREPARED">
        UPDATE COMPANY_M
        SET
            COMPANY_NM = #{companyNm},
            CEO = #{ceo},
            BIZNO = #{bizno},
            TEL = #{tel},
            ZIP = #{zip},
            ADDRESS = #{address},
            ADDRESS_DETAIL = #{addressDetail},
            EMAIL = #{email},
            REMARK = #{remark},
            USE_YN = #{useYn}
        WHERE
            ID = #{id}
    </update>

    <delete id="delete" parameterType="company" statementType="PREPARED">
        DELETE FROM
            COMPANY_M
        WHERE
            ID = #{id}
    </delete>

</mapper>

변수명에 커서를 두고 shift + f6 : rename 단축키
리팩토링을 생활화하자!

profile
yesjm's second brain

0개의 댓글