Root IoC 컨테이너, Mybatis - 1017,18

Yung·2022년 10월 17일
0

Java223bitcamp

목록 보기
22/26

084. Root IoC 컨테이너 사용법

  • Root IoC 컨테이너를 설정하는 방법
  • 여러 개의 프론트 컨트롤러를 설정하는 방법
  • 프론트 컨트롤러 별로 필터를 설정하는 방법

설정이 중요하다.
설정이 끝나면 그다음부터 쉽다.
설정까지 가는 과정을 정확하게 알고있어야한다.

<<ServletContextLoaderListener>>ContextLoaderListener

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/context/ContextLoaderListener.html

ContextLoaderListener
웹 어플리케이션이 시작될때 여러 프론트컨트롤러가 공통으로 사용할 객체를 준비한다.

ContextLoaderListener는 Spring IoC 컨테이너를 소유하고있고,
Spring IoC 컨테이너는 RootConfig를 참고해서 객체를 준비
RootConfig는 DataSource, TransactionManger, DAO, Servie 객체를 설정한다.

:: ContextLoaderListener 객체생성은 -> ?
:: Spring IoC 컨테이너 객체생성은 -> ?
:: RootConfig를 생성하면

WebApplicationInitializer 구동 과정

웹어플리케이션이 시작되면, ServletContextListener보다 SpringServletContainerInitializer먼저 시작되고 WebApplicationInitializer를 찾아 OnStarter()를 호출한다.

1) Servlet 컨테이너 시작
2) 웹 어플리케이션을 시작
3) SpringServletContainerInitializer.onStartup()
4) WebApplicationInitializer 인터페이스의 규칙에 따라서 만든 구현체의 onStartup()가 호출된다.

서블릿컨테이너에 등록되어있는 서블릿들중에서 프론트컨틀롤러의 서비스를 호출되는데 그 URL에맞는 페이지 컨트롤러를 호출하는데.. 그럼 Serivce, DAO,

1단계 -Root IoC 컨테이너의 java config를 정의한다.

  • com.bitcamp.board.config.RootConfig 클래스 생성
package com.bitcamp.board.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.FilterType;

@ComponentScan(value = "com.bitcamp.board",
    excludeFilters = @Filter(type = FilterType.REGEX, pattern = "com.bitcamp.board.controller.*"))
// com.bitcamp.board 패키지를 다 찾아서 애노테이션이 붙은 클래스찾아 객체를 생성하는데,
// "com.bitcamp.board.controller.*" 패키지는 제외한다.
// type = FilterType.REGEX : 정규표현식
// "com.bitcamp.board.controller.*"를 패턴으로 만드려면 타입을 FilterType.REGEX로 정해줘야 한다.
public class RootConfig {

  public RootConfig() {
    System.out.println("RootConfig() 생성자 호출됨!");
  }
}
  • com.bitcamp.board.config.DatabaseConfig 클래스 생성
package com.bitcamp.board.config;

import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.transaction.PlatformTransactionManager;

public class DatabaseConfig {

  public DatabaseConfig() {
    System.out.println("DatabaseConfig() 생성자 호출됨!");
  }

  // 객체가 리턴한 값을 transactionManager라는 이름으로 저장한다.
  // @Bean 애노테이션을 붙일 때 객체 이름을 지정하면
  // 그 이름으로 리턴 값을 컨테이너에 보관한다.
  // 이름을 지정하지 않으면 메서드 이름으로 보관한다.
  @Bean
  public PlatformTransactionManager transactionManager(DataSource ds) {
    // Spring IoC 컨테이너는 이 메서드를 호출하기 전에 
    // 이 메서드가 원하는 파라미터 값인 DataSource를 먼저 생성한다.
    // => createDataSource() 메서드를 먼저 호출한다.
    System.out.println("createTransactionManager() 호출됨!");
    return new DataSourceTransactionManager(ds);
  }

  // DataSource를 생성하는 메서드를 호출하라고 표시한다.
  // 메서드가 리턴한 객체는 @Bean 애노테이션에 지정된 이름으로 컨테이너에 보관될 것이다.
  @Bean
  public DataSource dataSource() {
    System.out.println("DataSource 객체 생성!");
    DriverManagerDataSource ds = new DriverManagerDataSource();
    ds.setDriverClassName("org.mariadb.jdbc.Driver");
    ds.setUrl("jdbc:mariadb://localhost:3306/studydb");
    ds.setUsername("study");
    ds.setPassword("1111");
    return ds;
  }
}
  • com.bitcamp.board.config.AppWebApplicationInitializer 클래스 변경
package com.bitcamp.board.config;

import javax.servlet.Filter;
import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletRegistration.Dynamic;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import com.bitcamp.board.filter.LoginCheckFilter;

public class AppWebApplicationInitializer
    extends AbstractAnnotationConfigDispatcherServletInitializer {

  @Override
  protected Class<?>[] getRootConfigClasses() {
    return new Class<?>[] {RootConfig.class, DatabaseConfig.class};
  }

  @Override
  protected String getServletName() {
    return "app";
  }

  @Override
  protected Class<?>[] getServletConfigClasses() {
    return new Class<?>[] {AppWebConfig.class};
  }

  @Override
  protected String[] getServletMappings() {
    return new String[] {"/app/*"};
  }

  @Override
  protected Filter[] getServletFilters() {
    return new Filter[] {new CharacterEncodingFilter("UTF-8"), new LoginCheckFilter()};
  }

  @Override
  protected void customizeRegistration(Dynamic registration) {
    registration.setMultipartConfig(new MultipartConfigElement(System.getProperty("java.io.tmpdir"), // 클라이언트가 보낸 파일을 임시 저장할 때 사용할 디렉토리
        1024 * 1024 * 5, // 한 파일의 최대 크기
        1024 * 1024 * 10, // 첨부 파일을 포함한 전체 요청 데이터의 최대 크기
        1024 * 1024 // 첨부 파일을 데이터를 일시적으로 보관할 버퍼 크기
    ));
  }
}

RootConfig는 DAO, Service 설정
DatabaseConfig는 DataSource, TransactionManger 설정

084. Root IoC 컨테이너 사용

(/app/*) DispatcherServlet은 Spring IoC 컨테이너를 소유하고 있고,
Spring IoC 컨테이너는 AppWebConfig를 참고한다.
AppWebConfig는 Controller, 기타 웹 관련 컴포넌트를 생성시킨다.

(/admin/*) DispatcherServlet AdminWebConfig

2단계 - '/app/*' 요청 처리 프론트 컨트롤러를 설정한다.

  • com.bitcamp.board.config.AppWebConfig 클래스 생성
package com.bitcamp.board.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.FilterType;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

@ComponentScan(value = "com.bitcamp.board.controller",
    excludeFilters = @Filter(type = FilterType.REGEX,
        pattern = "com.bitcamp.board.controller.admin.*"))
public class AppWebConfig {

  public AppWebConfig() {
    System.out.println("AppWebConfig() 생성자 호출됨!");
  }

  // multipart/form-data 형식으로 보내온 요청 데이터를
  // 도메인 객체로 받는 일을 할 도우미 객체를 등록한다. 
  // 이 객체가 등록된 경우 multipart/form-data를 도메인 객체로 받을 수 있다.
  @Bean
  public MultipartResolver multipartResolver() {
    return new StandardServletMultipartResolver();
  }

  // Spring WebMVC의 기본 ViewResolver를 교체한다.
  @Bean
  public ViewResolver viewResolver() {
    InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
    viewResolver.setViewClass(JstlView.class); // 주어진 URL을 처리할 객체 => JSP 실행시킨다.
    viewResolver.setPrefix("/WEB-INF/jsp/"); //
    viewResolver.setSuffix(".jsp");

    return viewResolver;
  }
}
  • com.bitcamp.board.config.AppWebApplicationInitializer 클래스 변경
package com.bitcamp.board.config;

import javax.servlet.Filter;
import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletRegistration.Dynamic;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import com.bitcamp.board.filter.LoginCheckFilter;

public class AppWebApplicationInitializer
    extends AbstractAnnotationConfigDispatcherServletInitializer {

  @Override
  protected Class<?>[] getRootConfigClasses() {
    return new Class<?>[] {RootConfig.class, DatabaseConfig.class};
  }

  @Override
  protected String getServletName() {
    return "app";
  }

  @Override
  protected Class<?>[] getServletConfigClasses() {
    return new Class<?>[] {AppWebConfig.class};
  }

  @Override
  protected String[] getServletMappings() {
    return new String[] {"/app/*"};
  }

  @Override
  protected Filter[] getServletFilters() {
    return new Filter[] {new CharacterEncodingFilter("UTF-8"), new LoginCheckFilter()};
  }

  @Override
  protected void customizeRegistration(Dynamic registration) {
    registration.setMultipartConfig(new MultipartConfigElement(System.getProperty("java.io.tmpdir"), // 클라이언트가 보낸 파일을 임시 저장할 때 사용할 디렉토리
        1024 * 1024 * 5, // 한 파일의 최대 크기
        1024 * 1024 * 10, // 첨부 파일을 포함한 전체 요청 데이터의 최대 크기
        1024 * 1024 // 첨부 파일을 데이터를 일시적으로 보관할 버퍼 크기
    ));
  }
}

3단계 - '/admin/*' 요청 처리 프론트 컨트롤러를 설정한다.

  • com.bitcamp.board.config.AdminWebConfig 클래스 생성
package com.bitcamp.board.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

@ComponentScan(value = "com.bitcamp.board.controller.admin")
public class AdminAppWebConfig {

  public AdminAppWebConfig() {
    System.out.println("adminAppWebConfig() 생성자 호출됨!");
  }

  // Spring WebMVC의 기본 ViewResolver를 교체한다.
  @Bean
  public ViewResolver viewResolver() {
    InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
    viewResolver.setViewClass(JstlView.class); // 주어진 URL을 처리할 객체 => JSP 실행시킨다.
    viewResolver.setPrefix("/WEB-INF/jsp/"); //
    viewResolver.setSuffix(".jsp");
    return viewResolver;
  }
}
  • com.bitcamp.board.config.AdminWebApplicationInitializer 클래스 생성
package com.bitcamp.board.config;

import javax.servlet.Filter;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import com.bitcamp.board.filter.AdminCheckFilter;
import com.bitcamp.board.filter.LoginCheckFilter;

public class AdminWebApplicationInitializer
    extends AbstractAnnotationConfigDispatcherServletInitializer {

  @Override
  protected Class<?>[] getRootConfigClasses() {
    return null; // Root IoC의 설정은 다른 쪽에서 했기때문에 여기서 설정하지 않음.
    // Root IoC의 설정 공유함.
  }

  @Override
  protected String getServletName() {
    return "admin";
  }

  @Override
  protected Class<?>[] getServletConfigClasses() {
    return new Class<?>[] {AdminAppWebConfig.class};
  }

  @Override
  protected String[] getServletMappings() {
    return new String[] {"/admin/*"};
  }

  @Override
  protected Filter[] getServletFilters() {
    return new Filter[] {new CharacterEncodingFilter("UTF-8"), new LoginCheckFilter(),
        new AdminCheckFilter()};
  }
}
  • /webapp/welcome.jsp 변경
    <li><a href="${contextPath}/admin/member/list">회원</a></li>

085. Mybatis SQL Mapper 도입

반복적이고, 단순한 JDBC 프로그래밍을 제거한다.
SQL을 별도 파일로 분리 -> SQL을 다루기가 더 쉽다.

도입 전
BoardDao

JDBC Code + SQL

도입 후
BoardDao

MyBatis(JDBC Code를 사용하기 쉽게 캡슐화를 시킨것)
캡슐화 : 메서드와 클래스로 묶는다.

SQL 코드(자바 소스파일에서 SQL 코드를 분리)

Mybatis를 도입한 후의 DAO 구동

Mybatis 사용법

1단계 - Mybatis SQL Mapper 라이브러리를 프로젝트에 추가한다.

  • search.maven.org 에서 'mybatis' 검색한다.
    • mybatis 라이브러리 파일
  • search.maven.org 에서 'mybatis-spring' 검색한다.
    • mybatis를 spring과 연동할 때 사용되는 라이브러리 파일
  • 라이브러리를 빌드 스크립트 파일(build.gradle)에 설정한다.
  • 이클립스 IDE용 설정 파일을 갱신한다.
  • 프로젝트를 갱신한다.
  • 라이브러리가 추가된 것을 확인한다.
    // SQL Mapper 라이브러리
    implementation 'org.mybatis:mybatis:3.5.11'
    
    // Mybatis를 스프링 프레임워크에서 관리할 수 있도록 도와주는 클래스들 
    implementation 'org.mybatis:mybatis-spring:2.0.7'

2단계 - SqlSessionFactory 객체를 준비한다.

  • com.bitcamp.board.config.MybatisConfig 클래스 생성
  • sqlSessionFactory() 메서드 추가
package com.bitcamp.board.config;

import javax.sql.DataSource;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;

public class MybatisConfig {

  public MybatisConfig() {
    System.out.println("MybatisConfig() 생성자 호출됨!");
  }

  @Bean
  public SqlSessionFactory sqlSessionFactory(DataSource ds, ApplicationContext iocContainer)
      throws Exception {
    System.out.println("SqlSessionFactory() 생성자 호출됨!");

    // Mybatis의 Log4j2 기능 활성화 시키기
    LogFactory.useLog4J2Logging();

    //SqlSessionFactory를 만들어 줄 객체를 준비한다.
    SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();

    // Mybatis가 사용할 DB 커넥션풀을 설정한다.
    factoryBean.setDataSource(ds);

    // Mybatis가 실행할 SQL 문이 들어있는 파일의 위치를 설정한다.
    factoryBean.setMapperLocations(
        iocContainer.getResources("classpath:com/bitcamp/board/mapper/*Mapper.xml"));
    // classpath: -> 
    return factoryBean.getObject();
  }
}
  • com.bitcamp.board.config.AppWebApplicationInitializer 클래스 변경
    • getRootConfigClasses() 메서드 리턴 값 변경
      MybatisConfig.class 추가
public class AppWebApplicationInitializer
    extends AbstractAnnotationConfigDispatcherServletInitializer {

  @Override
  protected Class<?>[] getRootConfigClasses() {
    return new Class<?>[] {RootConfig.class, DatabaseConfig.class, MybatisConfig.class};
  }

SqlSession 과 SQL Mapper

  • sqlSession.selectList("MemberDao.findAll")

"MemberDao"는 MemberDaoMapper.xml의 mapper namespace를 가르킨다.
"findAll"은 select id="findAll"을 가르킨다.

sqlSession.selectList가 리턴하는 값은 List<Member> List타입의 Member를 리턴한다.

1개의 레코드값(row)를 1개의 Member 인스턴스에 저장한다.
Member 인스턴스에 저장할때 no컬럼의 값을 setNo()을 호출하여 저장한다.

컬럼의 이름과 Member 인스턴스의 이름이 같아야 한다.(프로퍼티와 같아야한다.)
컬럼 이름에 해당하는 프로퍼티에 값을 담기 때문에 컬럼에 별명을 부여해서 일치 시켜야한다.

nonameemailcreateDate
100aa@test.com2021-10-18
200bb@test.com2021-10-18
300cc@test.com2021-10-18

MemberDaoMapper.xml

<mapper namespace="MemberDao">
  <select id="findAll" resultType="com.bitcamp.board.domain.Member">
	select
    	mno as no,
    	name
    	email,
    	cdt as createDate
    from
    	app_member
    order by
    	name
  </select>
  • sqlSession.selectOne("MemberDao.findByEmailPassword")
<mapper namespace="MemberDao">
  <select id="findByEmailPassword" resultType="com.bitcamp.board.domain.Member">
	select
    	mno as no,
    	name
    	email,
    	cdt as createDate
    from
    	app_member
    where
    	email=#{email} and 
    	pwd=sha2(#{password},256)
  </select>
nonameemailcreateDate
100aa@test.com2022-10-18
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("",email);
paramMap.put("",password);
keyvalue
emaila
password1111

sqlSession.selectOne("MemberDao.findByEmailPassword", paramMap)
sqlSession.selectOne는 null 또는 Member 객체를 리턴한다.
null: select 결과가 없을때 null 리턴
Member 객체 : select 결과가 한개라면 Member 객체를 리턴
예외 : select 결과가 여러개라면 예외가 발생

paramMap.get("email") --> email=#{email}
paramMap.get("password") --> email=#{password}

  • sqlSession.selectOne("MemberDao.findByNo")

int no값을 넣으면 자바에서 auto-boxing기능을 이용해 Integer.valueOf(no) 넣어준다.
sqlSession.selectOne("MemberDao.findByNo", Integer.valueOf(no));
sqlSession.selectOne("MemberDao.findByNo", no);

<mapper namespace="MemberDao">
  <select id="findByEmailPassword" resultType="com.bitcamp.board.domain.Member">
	select
    	mno as no,
    	name
    	email,
    	cdt as createDate
    from
    	app_member
    where
    	mno=#{value}
  </select>

mno=#{value} 메서드를 통해 넘어올 값이 Wrapper 객체거나 String 객체일 경우 이름으 아무거나 써도 상관없다.

  • sqlSession.insert

3단계 - Mybatis를 사용하는 DAO를 정의한다.

  • com.bitcamp.board.dao.MybatisMemberDao 클래스 생성
  • com/bitcamp/board/mapper/MemberDaoMapper.xml 파일 생성

insert

import java.sql.PreparedStatement;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.bitcamp.board.domain.Member;

@Repository
public class MybatisMemberDao implements MemberDao {

  @Autowired
  DataSource ds;
  @Autowired
  SqlSessionFactory sqlSessionFactory;

  @Override
  public int insert(Member member) throws Exception {
    try (SqlSession sqlSession = sqlSessionFactory.openSession();) {
      return sqlSession.insert("MemberDao.insert", member);
    }
  }
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="MemberDao">

  <insert id="insert"
    parameterType="com.bitcamp.board.domain.Member">
    insert into app_member(name,email,pwd)
    values(#{name},#{email},sha2(#{password},256))
  </insert>

findByNo

  @Override
  public Member findByNo(int no) throws Exception {
    try (SqlSession sqlSession = sqlSessionFactory.openSession();) {
      return sqlSession.selectOne("MemberDao.findByNo", no);
    }
  }
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="MemberDao">
  <select id="findByNo" resultType="com.bitcamp.board.domain.Member">
    select
      mno no,
      name,
      email,
      cdt createDate
    from
      app_member
    where
      mno=#{no}
  </select>

findAll

  @Override
  public List<Member> findAll() throws Exception {
    try (SqlSession sqlSession = sqlSessionFactory.openSession();) {
      return sqlSession.selectList("MemberDao.findAll");
    }
  }
  <select id="findAll" resultType="com.bitcamp.board.domain.Member">
    select
      mno no,
      name,
      email 
    from 
     app_member 
    order by
     name
  </select>

findByEmailPassword

  @Override
  public Member findByEmailPassword(String email, String password) throws Exception {
    try (SqlSession sqlSession = sqlSessionFactory.openSession();) {

      Map<String, Object> paramMap = new HashMap<>();
      paramMap.put("email", email);
      paramMap.put("password", password);
      return sqlSession.selectOne("MemberDao.findByEmailPassword", // SQL 문의 ID
          paramMap // SQL 문의 in-parameter(#{})에 들어 갈 값을 담고 있는 객체
      );
    }
  }
  <select id="findByEmailPassword" resultType="com.bitcamp.board.domain.Member">
    select
      mno no,
      name,
      email,
      cdt createDate
    from
      app_member
    where
      email=#{email} and pwd=sha2(#{password},256)
  </select>
</mapper>

update

  @Override
  public int update(Member member) throws Exception {
    try (SqlSession sqlSession = sqlSessionFactory.openSession();) {
      return sqlSession.update("MemberDao.update", member);
    }
  }
  <update id="update" parameterType="com.bitcamp.board.domain.Member">
    update
      app_member
    set 
      name=#{name}, 
      email=#{email}, 
      pwd=sha2(#{password},256) 
    where
      mno=#{no}
  </update>

delete

  @Override
  public int delete(int no) throws Exception {
    try (SqlSession sqlSession = sqlSessionFactory.openSession();) {
      return sqlSession.delete("MemberDao.delete", no);
    }
  }
  <delete id="delete">
    delete
    from
      app_member
    where
      mno=#{value}
  </delete>
  • com.bitcamp.board.dao.MybatisBoardDao 클래스 생성

DAO와 테이블

테이블 조인과 SQL Mapper

MariaDB [studydb]> select
    -> b.bno,
    -> b.title,
    -> b.cdt,
    -> b.vw_cnt,
    ->   m.mno,
    ->   m.name
    -> from
    -> app_board b
    -> join app_member m on b.mno = m.mno
    -> order by bno desc;
+-----+---------------+---------------------+--------+-----+-------+
| bno | title         | cdt                 | vw_cnt | mno | name  |
+-----+---------------+---------------------+--------+-----+-------+
|  82 | yyy           | 2022-10-18 17:12:25 |      0 |   1 | user1 |
|  81 | yyy           | 2022-10-18 17:10:34 |      0 |   1 | user1 |
|  80 | asdfa         | 2022-10-18 17:08:25 |      0 |   1 | user1 |
|  78 | yyyy          | 2022-10-18 17:00:40 |      0 |   6 | admin |
|  75 | test          | 2022-10-18 16:26:43 |      0 |   6 | admin |
|  74 | test          | 2022-10-18 16:26:36 |      0 |   6 | admin |
|  73 | asdfasdf      | 2022-10-18 14:56:49 |      0 |   6 | admin |
|  70 | asdfasdf      | 2022-10-14 15:39:15 |      0 |   6 | admin |
|  67 | asetaset      | 2022-10-14 14:23:25 |      0 |   6 | admin |
|  35 | test          | 2022-10-06 11:22:59 |      0 |   3 | user3 |
|  33 | TEST1         | 2022-10-05 17:45:49 |      0 |   6 | admin |
|  32 | TEST          | 2022-10-05 17:45:15 |      0 |   6 | admin |
|  31 | bbbbcc        | 2022-10-05 12:36:04 |      0 |   1 | user1 |
|  25 | test1asdfasdf | 2022-10-04 14:44:12 |      0 |   1 | user1 |
|  22 | asdfasdf      | 2022-10-04 11:01:02 |      0 |   1 | user1 |
|  21 | asdfasdf      | 2022-10-04 11:00:19 |      0 |   1 | user1 |
|  20 |               | 2022-10-04 10:59:31 |      0 |   1 | user1 |
|  19 |               | 2022-10-04 10:59:08 |      0 |   1 | user1 |
|  18 | test1         | 2022-10-04 10:28:19 |      0 |   1 | user1 |
|  17 | asdfas        | 2022-10-03 17:05:53 |      0 |   1 | user1 |
|  16 | 제목6         | 2022-10-02 15:45:23 |      0 |   4 | user4 |
|  15 | 제목5         | 2022-10-02 15:45:23 |      0 |   2 | user2 |
|  14 | 제목4         | 2022-10-02 15:45:23 |      0 |   2 | user2 |
|  13 | 제목3         | 2022-10-02 15:45:23 |      0 |   1 | user1 |
|  12 | 제목2         | 2022-10-02 15:45:23 |      0 |   1 | user1 |
|  11 | 제목1         | 2022-10-02 15:45:23 |      0 |   1 | user1 |
+-----+---------------+---------------------+--------+-----+-------+
26 rows in set (0.003 sec)

게시글 상세 정보 가져오기 : 첨부 파일 목록 포함(방법1)

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mariadb.jdbc.Statement;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.bitcamp.board.domain.AttachedFile;
import com.bitcamp.board.domain.Board;

@Repository
public class MybatisBoardDao implements BoardDao {

  @Autowired
  DataSource ds;
  @Autowired
  SqlSessionFactory sqlSessionFactory;
  
  @Override
  public Board findByNo1(int no) throws Exception {
    try (SqlSession sqlSession = sqlSessionFactory.openSession();) {

      Board board = sqlSession.selectOne("BoardDao.findByNo", no);

      // 게시글 첨부파일 가져오기
      List<AttachedFile> attachedFiles =
          sqlSession.selectList("BoardDao.findAttachedFilesByBoard", no);
      board.setAttachedFiles(attachedFiles);
      return board;
    }
  }

DefaultBoardService

  @Override
  public Board get(int no) throws Exception {
     // 방법1: 
     return boardDao.findByNo(no);
  }
<mapper namespace="BoardDao">

  <resultMap type="board" id="boardMap">
    <id column="bno" property="no"/> 
    <result column="title" property="title"/>
    <result column="cont" property="content"/>
    <result column="cdt" property="createdDate"/>
    <result column="vw_cnt" property="viewCount"/>
    
    <association property="writer" javaType="member">
      <id column="mno" property="no"/>
      <result column="name" property="name"/>  
    </association>
  </resultMap>
  
  <resultMap type="AttachedFile" id="attachedFileMap">
    <id column="bfno" property="no" />
    <result column="filepath" property="filepath" />
    <result column="bno" property="boardNo" />
  </resultMap>
  
  <select id="findByNo" resultMap="boardMap">
  select
    b.bno,
    b.title,
    b.cont,
    b.cdt,
    b.vw_cnt,
    m.mno,
    m.name
  from
    app_board b
    join app_member m on b.mno = m.mno
  where 
    b.bno=#{value}
  </select>
  
  <select id="findAttachedFilesByBoard" resultMap="attachedFileMap">
  select
    bfno,
    filepath, 
    bno 
  from 
    app_board_file 
  where 
    bno =#{value}
  </select>

게시글 상세 정보 가져오기 : 첨부 파일 목록 포함(방법2)

MybatisBoardDao

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mariadb.jdbc.Statement;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.bitcamp.board.domain.AttachedFile;
import com.bitcamp.board.domain.Board;

@Repository
public class MybatisBoardDao implements BoardDao {

  @Autowired
  DataSource ds;
  @Autowired
  SqlSessionFactory sqlSessionFactory;
  
  // 게시글 상세 정보 가져오기 : 첨부파일 목록 포함 방법2
  @Override
  public Board findByNo2(int no) throws Exception {
    try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
      return sqlSession.selectOne("BoardDao.findByNo", no);
    }
  }

DefaultBoardService

  @Override
  public Board get(int no) throws Exception {
    // 방법2:
    Board board = boardDao.findByNo2(no);
    List<AttachedFile> attachedFiles = boardDao.findFilesByBoard(no);
    board.setAttachedFiles(attachedFiles);
    return board;
  }

게시글 상세 정보 가져오기 : 첨부 파일 목록 포함(방법3)

  <select id="findByNo3" resultMap="boardMap">
  select
    b.bno,
    b.title,
    b.cont,
    b.cdt,
    b.vw_cnt,
    m.mno,
    m.name,
    bf.bfno,
    bf.filepath
  from
    app_board b
    join app_member m on b.mno = m.mno
    left outer join app_board_file bf on b.bno = bf.bno
  where 
    b.bno=#{value}
  </select>
MariaDB [studydb]>   select
    ->     b.bno,
    ->     b.title,
    ->     b.cont,
    ->     b.cdt,
    ->     b.vw_cnt,
    ->     m.mno,
    ->     m.name,
    ->     bf.bfno,
    ->     bf.filepath
    ->   from
    ->     app_board b
    ->     join app_member m on b.mno = m.mno
    ->     left outer join app_board_file bf on b.bno = bf.bno
    ->   where
    ->     b.bno=67;
+-----+----------+----------+---------------------+--------+-----+-------+------+----------+
| bno | title    | cont     | cdt                 | vw_cnt | mno | name  | bfno | filepath |
+-----+----------+----------+---------------------+--------+-----+-------+------+----------+
|  67 | asetaset | asetaset | 2022-10-14 14:23:25 |      0 |   6 | admin | NULL | NULL     |
+-----+----------+----------+---------------------+--------+-----+-------+------+----------+
1 row in set (0.001 sec)
  // 게시글 상세 정보 가져오기 : 첨부파일 목록 포함 방법3
  @Override
  public Board findByNo3(int no) throws Exception {
    try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
      return sqlSession.selectOne("BoardDao.findByNo3", no);
    }
  }

0개의 댓글