[spring] 스프링 JSON통신, Mybatis

7 분 소요

참고 및 사이트 출저 :
https://khj93.tistory.com/entry/MyBatis-MyBatis%EB%9E%80-%EA%B0%9C%EB%85%90-%EB%B0%8F-%ED%95%B5%EC%8B%AC-%EC%A0%95%EB%A6%AC

https://www.youtube.com/watch?v=4YOk7oLGTKI&t=916s

스프링 JSON 통신

  • JSON 통신 하기전에 JSON 데이터 주고받을 데이터 자료구조 객체 클래스 생성해야됨
  • 예를들어 { name: value, address: value } JSON주고 받을려면 클래스 따로 만들어 줘야됨.
  • JSON 자료형 클래스로 DB 작업하는 기능을 갖는 인터페이스를 따로 생성함. (Mapper 클래스)
  • 그 후에 스프링 컨트롤러에서 JSON 자료클래스, JSON DB작업기능 인터페이스 두개를 이용해서 HTTP 메소드 작성
  • 마지막으로 DB 접근하도록 DB 설정 정보를 담는 applocation.properties 작성 (Study 프로젝트에서는 application-loc.yml에 DB 정보가 있음)



(1). 일단 주고받을 JSON 자료 클래스 생성함


import loombook.Data;

//주고받을 JSON 클래스는 @Data 어노테이션 붙여줘야함. 
@Data
public class Company{
    private int id;
    private String name;
    private String address;

}


(2). 해당 JSON 자료 클래스로 DB 쿼리 작업하는 기능의 API 를 당담하는 인터페이스 생성 (Mapper 클래스 생성)

@Mapper
public interface ComapnyMapper {

    /* 
    @SQL 사용자 지정 이름
    리턴타입 API이름(매개변수 있을시 매개변수)
    */




    /*
    @Options(useGeneratedKey=true, keyPropery="키이름")
    JSON 통신 DB쿼리의 결과값을 0, 1 로 받는 경우보다 입력한 JSON 그 자체로 반환 받는 경우가 많음. 그 경우에는 name과 address 는 받을 수 있는데 id 같은 경우에는 쿼리 던질지고 튜플이 생성될때 DB에서 자동으로 생성되는 기본키값이라 json에서 id 는 반환을 못받음 -> 기본키 id를 조회해서 반환할때 사용

    DB의 자동으로 생성되는 컬럼값인 기본키 id 값이 Company 클래스의 id 변수에 맵핑됨

    */


    @Insert("INSERT INTO company {company_name, company_address) VALUES(#{company.name}, #{company.address}}")
    @Options(useGeneratekeys=true, keyProperty="id")
    int insert(@Parm{"company"} Company company)
    /*
    company 라는 Json 자료형 클래스를 매개변수로 받아서, 그 이름은 compnay 로 맵핑되어 위 어노테이션 @Insert 에 Mapping 된 이름으로 사용됨
    */





    /*
    @Result -> Java의 변수 명과, DB의 컬럼 이름이 다를시에 Java의 변수 명과 DB의 컬럼 이름을 맵핑 시켜줌. 같은 경우에는 따로 설정을 안해줘도 자동으로 JAVA변수랑 DB 컬럼이름이랑 맵핑됨

    맵핑관계에 id 를 붙여서 다른 메소드에서도 id 이름만으로 재활용가능 "CompanyMap"

    property="자바변수 이름", column="DB컬럼 이름"

    */
    @Select("SELECT * FROM company")
    @Result({id = "CompanyMap" , value{
        @Result(property="name", column="company_name")
        @Result(property="address", column="compnay_address")
    })
    List<Company> getAll();
    // getAll() API 메소드 -> 데이터베이스 테이블에 있는 모든 컬럼값을 List<Company> 으로 반환함

    /*
    [ {
        "id" : 3,
        "name" : "현대자동차"
        "address" : "서울시 강남구 대치2동"
    },
    {
        "id" : 4,
        "name" : "삼성전자"
        "address" : "서울시 강남구 대치1동"
    }
    
    ]
    
    */
    
    @Select("SELECT * FROM company WHERE id=#{id}")
    @ResultMAp{"CompanyMap"}
    Company getByID(@Parm("id") int id);


}


(3). 스프링 Controller 에서 JSON 자료 클래스, JSON DB API메서드 클래스를 이용해 HTTP 메서드 작성


@RestController
@RequestMapping("/company")
public class CompanyController {


    /* 의존성 주입할때 쉽게 해줌. 원래라면 의존성 주입할려면 @Configuration 어노테이션이 붙은 클래스에 @Bean 메소드를 작성해야되는데, Autowried 사용하면 안해도됨 */
    @Autowired
    private CompanyMapper companyMapper;


    /*
    @HTTP메서드 이름(url 주소)
    public 반환자료형 (매개변수){
        return Mapper클래스.호출할 메소드;

    }
    */

    @PostMapping("")
    public int Post(@RequestBody Company company) {
        return companyMapper.insert(company)
    }
    // Post는 입력값이 필요하므로 매개변수로 @RequestBody 기입후 자료형 입력 필수
    // Post맨으로 POST 보내고, Response로 1 반환시 정상적으로 SQL 실행확인 가능


    /*
    @PostMapping("")
    public Company Post(@RequestBody Company company) {
        companyMapper.insert(company)
        return company;
    }
    */
    //결과값을 JSON 통신한 JSON 객체로 받음. id 는 DB에서 insert 작업 후에 자동으로 생성되는 컬럼값이여서 맵퍼클래스에서 @Option 어노테이션 설정을 해줘야함
    


    @GetMapping("")
    public List<Company> getAll(){
        return companyMapper.getAll();
    }

    @getMapping("/{id}")
    public List<Company> getByID(@PathVariable("id") int id){
        return companyMapper.getByID(id);
    }
    /* pathVariable -> 예를들어 id 5를 조회하고싶어서 
    /company/5 url 으로 send시에 url에 있는 5 를 매개변수로 넣어서 쿼리함

    4번 id 얻고싶을시에 /company/4 url 으로 send 하면됨

    */ 



}


(4). DB 정보를 담고있는 application.properties 작성

spring.datasource.url = (DB url 작성)
spring.datasource.username = (DB name 작성)
spring.datasource.password = (DB password 작성)



Mybatis

  • 프로그램 코드와 SQL 을 분리할 수 있게 해줌
  • 그 전에는 메소드등을 사용해서 코드안에 메소드(“SQL문”); 형식으로 사용했음.
  • 코드안에 SQL 삽입시에 코드양이 방대해지면 수정이 힘들어짐



Mybatis 구조


mybatis_strt



Mybatis 주요 구성요소가 Database 엑세스 하는 순서


mybatis2

  • 응용프로그램 시작시 수행되는 프로세스
    (Mybatis 설정파일 읽고, SqlSeesionFactory 생성함)

(1) 응용 프로그램이 SqlSessionFactoryBuilder를 위해 SqlSessionFactory를 빌드하도록 요청한다.
(2) SqlSessionFactoryBuilder는 SqlSessionFactory를 생성하기 위한 MyBatis 구성 파일을 읽는다.
(3) SqlSessionFactoryBuilder는 MyBatis 구성 파일의 정의에 따라 SqlSessionFactory를 생성한다.



  • 클라이언트의 각 요청에 대해 수행되는 프로세스
    (SSF 빌더를 사용해 SSF에서 Sql Session 생성 -> 어플리케이션에 갖다줌 -> 응용프로그램이 SqlSession 에서 Mapper Interface 구현객체 가져옴 -> 응용프로그램이 맵퍼 인터페이스 메서드 호출 -> SqlSeesion 호출 후 SqlSession에게 SQL 실행요청 -> SqlSession이 Mapping파일에서 SQL가져와서 SQL 실행 )

(4) 클라이언트가 응용 프로그램에 대한 프로세스를 요청한다.
(5) 응용 프로그램은 SqlSessionFactoryBuilder를 사용하여 빌드된 SqlSessionFactory에서 SqlSession을 가져온다.
(6) SqlSessionFactory는 SqlSession을 생성하고 이를 애플리케이션에 반환한다.
(7) 응용 프로그램이 SqlSession에서 매퍼 인터페이스의 구현 개체를 가져온다.
(8) 응용 프로그램이 매퍼 인터페이스 메서드를 호출한다.
(9) 매퍼 인터페이스의 구현 개체가 SqlSession 메서드를 호출하고 SQL 실행을 요청한다.
(10) SqlSession은 매핑 파일에서 실행할 SQL을 가져와 SQL을 실행한다.



MyBatis-Spring의 컴포넌트 구조


mybatis_spring

  • 응용프로그램 시작시 수행되는 프로세스
    (SSF Bean이 SSF 빌더에게 SSF 빌드하도록 요청 -> SSF 빌더는 Config File 읽어서 SSF 생성 -> 생성된 SSF는 스프링 DI 컨테이너에 저장됨 -> MF Bean이 Sql session Template (맵퍼) 객체 생성함, 스프링 DI 컨테이너에 저장됨 -> )

(1) SqlSessionFactoryBean은 SqlSessionFactoryBuilder를 위해 SqlSessionFactory를 빌드하도록 요청한다.
(2) SessionFactoryBuilder는 SqlSessionFactory 생성을 위해 MyBatis 구성 파일을 읽는다.
(3) SqlSessionFactoryBuilder는 MyBatis 구성 파일의 정의에 따라 SqlSessionFactory를 생성한다. 따라서 생성된 SqlSessionFactory는 Spring DI 컨테이너에 의해 저장된다.
(4) MapperFactoryBean은 안전한 SqlSession(SqlSessionTemplate) 및 스레드 안전 매퍼 개체(Mapper 인터페이스의 프록시 객체)를 생성한다. 따라서 생성되는 매퍼 객체는 스프링 DI 컨테이너에 의해 저장되며 서비스 클래스 등에 DI가 적용된다. 매퍼 개체는 안전한 SqlSession(SqlSessionTemplate)을 사용하여 스레드 안전 구현을 제공한다.



  • 클라이언트의 각 요청에 대해 수행되는 프로세스
    (어플리케이션이 DI 컨테이너에 주입된 맵퍼객체 호출 -> 맵퍼객체가 SS 호출 -> SS 는 프록시 및 안전한 SS메서드 호출 -> SS는 트랜잭션에 할당된 Mybatis3 SS를 사용함 (SS없을시 SSF 에 요청) -> SSF는 SS 반환함 -> SS가 트랙잭션에 할당됨(트랙잭션에 할당된 경우 새 SS 생성 안하고 동일한 SS 사용) -> On 메서드 사용하고 SQL 실행요청 -> SS는 맵핑파일에서 실행할 SQL 가지고 와서 SQL 실행 )

(5) 클라이언트가 응용 프로그램에 대한 프로세스를 요청한다.
(6) 애플리케이션(서비스)은 DI 컨테이너에서 주입한 매퍼 개체(매퍼 인터페이스를 구현하는 프록시 개체)의 방법을 호출한다.
(7) 매퍼 객체는 호출된 메소드에 해당하는 SqlSession (SqlSessionTemplate ) 메서드를 호출한다.
(8) SqlSession (SqlSessionTemplate )은 프록시 사용 및 안전한 SqlSession 메서드를 호출한다.
(9) 프록시 사용 및 스레드 안전 SqlSession은 트랜잭션에 할당된 MyBatis3 표준 SqlSession을 사용한다.
트랜잭션에 할당된 SqlSession이 존재하지 않는 경우 SqlSessionFactory 메서드를 호출하여 표준 MyBatis3의 SqlSession을 가져온다.
(10) SqlSessionFactory는 MyBatis3 표준 SqlSession을 반환한다. 반환된 MyBatis3 표준 SqlSession이 트랜잭션에 할당되기 때문에 동일한 트랜잭션 내에 있는 경우 새 SqlSession을 생성하지 않고 동일한 SqlSession을 사용합니다.on 메서드를 호출하고 SQL 실행을 요청한다.
(11) MyBatis3 표준 SqlSession은 매핑 파일에서 실행할 SQL을 가져와 실행한다.



Spring - DTO , VO , Bean , POJO , ORM

  1. DTO(Data Trancefer Object)
    • 계층간에 데이터 교환을 위해 사용하는 객체
    • 데이터를 담을 private 변수와 그 변수를 조작할 수 있는 Getter, Setter 메서드로 구성됨
  2. VO(Value Object)
    • DTO와 비슷하나, **내부 속성값을 변경 할 수 없는(Immutable), Read-Only 의 속성을 갖고있는 객체**
    • 변경없이 값으로 취급할 객체
  3. Bean
    • Spring의 IoC Container(=DI Container)를 통해 관리(생성, 제어)되는 객체
  4. POJO(Plan Old Java Object)
    • 특정 인터페이스나 클래스를 상속하지 않고, 순수하게 Getter, Setter로만 구성된(어디에도 종속되지 않은) 자바 객체
    • Spring MVC를 걷어내도 POJO들은 정상적으로 동작되어야 한다.
    • POJO와 설정 정보(Configuration metadata)를 Spring IoC Container에 주입시키면 Bean으로 등록, 사용이 가능하다.
      *허나, Spring에서는 Bean 생성을 위해 Annotation 사용을 강제하므로, POJO라 부르기에는 의미적으로 맞지 않다는 의견도 있다.
  5. ORM(Object Relational mapping)
    • Object - 객체지향 언어, Relational - 관계형 데이터베이스의 데이터
    • 관계형 데이터베이스에서 조회한 데이터를 Java 객체로 변환하여 리턴해 주고, Java 객체를 관계형 데이터베이스에 저장해 주는 라이브러리 혹은 기술
    • Mybatis는 JDBC로 처리하는 상당 부분의 코드와 파라미터 설정 및 결과 매핑을 대신해준다. 하지만 직접 쿼리를 작성하여 명시하여야 하기 때문에 ORM으로 보기 힘들다.



Mybatis-Spring의 주요 컴포넌트 역할

  • MyBatis 설정파일 (Study 프로젝트에서 mybatis-config.xml) -> VO 객체의 정보를 설정한다.

  • Mapping 파일(Study 프로젝트에서 Board.xml) -> SQL문과 OR mapping을 설정한다.

  • SpringBean 설정파일(Bean.xml) -> SSF Bean 을 Bean 등록할때 DataSource 정보와 MyBatis Config 파일정보, Mapping 파일의 정보를 함께 설정한다. SS Template을 Bean 으로 등록한다.


Mapping 파일 작성 예시)

<?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="사용중인 맵퍼 로컬경로">   예) kr.co.test.study.api.mapper.BoardMapper -->

<mapper namespace="userNS">
    
    <select id="SelectUserById" parameterType="string" resultType="User">
        select * from users where userid=#{value}
    </select>


    <!-- 
    <sql태그 id="Mapper 인터페이스의 메소드 이름들" parmeterTpye="자료형" resultType="결과로 받을 자료형 클래스 패쓰 경로"> 
        sql문 (동적으로 java 변수 사용시에는 #{자바변수} 형식으로 사용)
    </sql태그>
    -->

    <!-- ResultType 에 Model 즉, VO 객체 클래스에서 설정한 @Alias("이 값") 값을 넣으면됨. 즉 별칭을 말하는거임. Model - VO객체에서 설정한 Alias 값을 입력시에 그 객체와 맵핑되어서 그 객체의 각각의 변수에 값이 들어감 -->


    <insert id="insertUser" parameterType="User">
        insert into users values(#{userId}, #{name}, #{gender}, #{city} )
    </insert>

</mapper>



자주쓰는 클래스 패쓰 경로는 mybatis-config.xml 에서 별칭 저장 가능
(class 단위, pacakger 단위 둘중에 하나로 선택)

mybatis-config.xml

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

<configuration>
	
	<settings>
		<setting name="cacheEnabled" value="false" />
		<setting name="useGeneratedKeys" value="true" />
		<setting name="defaultExecutorType" value="REUSE" />
		<setting name="aggressiveLazyLoading" value="false" />
		<setting name="mapUnderscoreToCamelCase" value="true" />

		<setting name="callSettersOnNulls" value="true"/>
		<setting name="jdbcTypeForNull" value="NULL" />
		<setting name="returnInstanceForEmptyRow" value="true"/>
	</settings>

    <!-- 클래스 단위로 alias 설정할때, 이경우는 VO객체 클래스에 @Alias 어노테이션 안붙여도됨-->
    <typeAlias alias="Author" type="domain.blog.Author"/>


	<!-- 패키지 단위로 alias 설정할때 -->
	<typeAliases>
		<package name="kr.co.test.study.api.model"/>
		<package name="kr.co.test.study.api.model.study"/>
		<package name="kr.co.test.study.api.model.common"/>
    </typeAliases>
    
</configuration>


MyBatis 사용 요약

  • (1),(2),(3) 까지는 동일하고, 다만 SQL 작성하는것을 interface 에다가 하는 것이 아닌 Mapping 파일에다가 작성.
  • Mapping 파일과 데이터 객체 클래스 동기화 (객체 클래스에는 Alias 이노테이션 사용 및 맵핑파일에는 resultType 속성 기입)