ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Mybatis:설정하기
    JAVA 2021. 7. 27. 16:23

    Mybatis

    참고사이트

    https://www.baeldung.com/mybatis

    이제 DAO 와 같이 DB 연결하는 Mapper 를 사용해 더 쉽게 연결하자!
    DAO는 JDBC API 였고, 이제 Mybatis API 를 설치한다. JDBC 를 위한 ConnectionPool API 도 필요 없다.
    DAO - 인터페이스를 만들고 그걸 구현한 클래스에 @Repository 를 걸어 @Autowired 시 인터페이스형으로 선언하여 사용함
    Mapper - 인터페이스에 아예 @Mapper 를 걸면 @Autowired 시 알아서 상속받은 클래스가 있는것 처럼 선언되어 사용할 수 있다.
    (구현하는 클래스 필요가 없다.)
    DAO vs Mapper : 여러개를 한번에 저장시 DAO 는 중간에 실패해도 초반건 들어가버린다. Mapper는 전체를 롤백한다.
    (ex. board는 저장했는데 attach가 이상하다고 실패했을 때 전체를 실행 시키지 않는 롤백을 시켜야 좋다)

    Mybatis 설정하기! (아주중요!!!)

    >> pom.xml

    		<!-- mybatis 를 위한 API 3개 -->
    		<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
    		<dependency>
    		    <groupId>org.mybatis</groupId>
    		    <artifactId>mybatis</artifactId>
    		    <version>3.4.1</version>
    		</dependency>
    		<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
    		<dependency>
    		    <groupId>org.mybatis</groupId>
    		    <artifactId>mybatis-spring</artifactId>
    		    <version>1.3.0</version>
    		</dependency>
    		<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
    		<dependency>
    		    <groupId>org.springframework</groupId>
    		    <artifactId>spring-jdbc</artifactId>
    		    <version>5.0.2.RELEASE</version>
    		</dependency>
    
    		<!-- db connectionpool 중 아래 2개는 필요없다. -->
    		<dependency>
    			<groupId>org.apache.commons</groupId>
    			<artifactId>commons-dbcp2</artifactId>
    			<version>2.7.0</version>
    		</dependency>
    // 		<dependency>
    //			<groupId>org.apache.commons</groupId>
    //			<artifactId>commons-pool2</artifactId>
    //			<version>2.8.0</version>
    //		</dependency>
    //		<dependency>
    //			<groupId>commons-logging</groupId>
    //			<artifactId>commons-logging</artifactId>
    //			<version>1.2</version>
    //		</dependency>

    >> web.xml

    	<servlet>
    		<servlet-name>dispatcher</servlet-name>
    		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    		<init-param>
    			<param-name>contextClass</param-name>
    			<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
    		</init-param>
    		<init-param>
    			<param-name>contextConfigLocation</param-name>
    			<param-value>
    				config.MvcConfig
    				config.BeanConfig
    			</param-value>
    		</init-param>
    	</servlet>

    web.xml 에 DispatcherServlet 이

    설정된 init-param 의 @Configuration 클래스들을 읽어 빈등록 한 것들을 반영해준다!


    >> MybatisConfig.java

    package config;
    
    import javax.sql.DataSource;
    
    import org.apache.commons.dbcp2.BasicDataSource;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.mybatis.spring.SqlSessionTemplate;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    @MapperScan(basePackages = {"com.stone"})
    public class MyBatisConfig {
    	
    	@Bean
    	public DataSource dataSource() {
    		String driverClassName = "com.mysql.cj.jdbc.Driver";
    		String url = "jdbc:mysql://localhost:3306/mvc2?useUnicode=true";
    		String userName = "root";
    		String password = "1234";
    		BasicDataSource dataSource = new BasicDataSource();
    		dataSource.setDriverClassName(driverClassName);
    		dataSource.setUrl(url);
    		dataSource.setUsername(userName);
    		dataSource.setPassword(password);
    		
    		return dataSource;
    	}
    	
    	@Bean
    	public SqlSessionFactory sqlSessionFactory() throws Exception {
    		SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
    		sqlSessionFactoryBean.setDataSource(dataSource());
    		return sqlSessionFactoryBean.getObject();
    	}
    	
    	@Bean
    	public SqlSessionTemplate sqlSessionTemplate() throws Exception {
    		return new SqlSessionTemplate(sqlSessionFactory());
    	}
    }
    @MapperScan(basePackages = {"com.stone"}) : 패키지 아래에 mybatis 인 객체인 @Mapper 애들 스캔
    @ComponentScan(basePackages = "com.stone") : 스프링객체인 @Controller 과 @Repository 인 애들 스캔, 자동으로 Bean등록
    BeanConfig 에 @Configuration 을 달아 따로 @Bean 을 잡는 이유 ( 생성자에 셋팅을 해야 할 수 있으니까!)
    @Bean 등록이 많은 이유: 아래의 생성자에 설정을 해야하며 설정시 쓰는 객체를 개인의 판단에 따라 여러가지 사용 가능하기 때문이다.
    아래의 3개 Bean 등록은 필수다!!
    1. DataSource : Connection 해준다. 그 중 BasicDataSource 객체를 선택했다.
    대표적으로 DriverManagerDataSource() 와 BasicDataSource() 골라쓸 수 있다.
    하지만 전자의 경우 계속 Connection 을 새로 생성하며 후자는 connection pooling 이라서 후자를 선택한다!
    (10개정도 만들어놓고 필요할때 꺼내쓰는방식)/connection pool 을 이제 안써도 됨!
    2. SqlSessionFactory : Connection 을 가져와 연결한 sqlFactory 를 만들어준다. 그 중 SqlSessionFactoryBean 객체를 선택했다.
    3. SqlSessionTemplate : sql문(select,insert등) 을 사용하게 하는 SqlSessionTemplate 객체를 Bean으로 올려 생성한다.

    >> MvcConfig.java (@Import 로 MyBatisConfig 추가)

    @Import(value= {MyBatisConfig.class})
    @Configuration
    @EnableWebMvc
    @ComponentScan(basePackages = "com.stone")
    public class MvcConfig implements WebMvcConfigurer {
    
    	@Override
    	public void configureViewResolvers(ViewResolverRegistry registry) {
    		registry.jsp("/WEB-INF/views/",".jsp");
    	
    	}
    	
    	@Override
    	public void addResourceHandlers(ResourceHandlerRegistry registry) {
    		// addResourceHandler = 모든 요청명 풀네임, addResourceLocations = 요청명 중 폴더명
    		registry.addResourceHandler("/img/**").addResourceLocations("/img/");
    		registry.addResourceHandler("/upload/**/**").addResourceLocations("/upload/");
    		registry.addResourceHandler("/js/*.js").addResourceLocations("/js/");
    	}
    	
    }

    예시

    @Select("select no,name from member where id=#{id} and password=#{password}")
    HashMap<String, Object> isIn(@Param("id")String id, String password)

    Member (회원)

    >> IMemberMapper

    package com.stone.mvc.dataservice_member;
    
    import java.util.List;
    
    import org.apache.ibatis.annotations.Mapper;
    import org.apache.ibatis.annotations.Select;
    import com.stone.mvc.common_member.Member;
    
    @Mapper
    public interface IMemberMapper {
    
    	@Select("select * from member;")
    	List<Member> selectAll();
    	
    	@Select("select * from member where no=#{no}")
    	Member findByNo(@Param("no") int 회원번호);
    
    	@Insert("insert into member(name,post,address,detailaddress,tel,"
    	+"email,id,password,profile)"
    	+"values(#{name},#{post},#{address},#{detailaddress},"
    	+"#{tel},#{email},#{id},#{password},#{profile})")
    	void save(Member member);
    	
    	@Select("select no,name from member where id=#{id} and password=#{password}")
    //no=1 , name=나길동 이렇게 HashMap 으로 컬럼명과 컬럼값이 들어간다.
    //만약 하나의 튜플이 아닌 여러개의 튜플이 나온다면 List<HashMap<String,String>> 으로 해주면 된다.
    	HashMap<String, String> isIn(String id, String password);
    	
    	@Select("select if(count(*)=1,1,0) from member where id=#{id}")
    	boolean isIn2(String id);//숫자가0 은 false로 반환, 0외의 모든 수는 true 임을 기억! DB에는 true false 가 없음
    }
    @Mapper : JDBC 에서는 @Repository 였던 것처럼 DB연동하는 클래스라고 선언
    (이제 MyBatisConfig 로 설정한 환경의 DB연결 설정이 알아서 셋팅된다.)
    select 문으로 db에 접근을 선언. 아래 매서드에 알아서 열이름=멤버변수이름을 매핑해서 넣어준다.
    @Param("#{no}이름과같은이름") : sql 문의 값에 매칭되는 매개변수 선언(변수명이 같다면 생략가능)

    >> MemberDAO ( DAO 의 일을 Mapper 로 돌리자 + Mapper 쪽에서 HashMap으로 바꿨으니 여기서도 반영하기 )

    @Repository
    public class MemberDAO implements IMemberDAO {
    	@Autowired
    	IMemberMapper memberMapper;
    	
    	// 회원 저장하기
    	public void save(Member member) {
    		memberMapper.save(member);
    	}
    
    	// 존재하는 회원인지(로그인시 사용) 있으면 번호와 성명반환
    	public HashMap<String,Object> isin(String id, String password) {
    		return memberMapper.isIn(id, password);
    	}
    
    	// 존재하는 아이디인지(회원가입시 아이디중복체크로 사용)
    	public boolean isin(String id) {
    		return memberMapper.isIn2(id);
    	}
    }

    >> 회원등록컨트롤 (Mapper 쪽에서 HashMap으로 바꿨으니 여기서도 반영하기)

    HashMap<String, Object> NoAndName = memberDAO.isin(member.getId(), member.getPassword());
    session.setAttribute("no", NoAndName.get("no"));
    session.setAttribute("name", NoAndName.get("name"));

    Board (게시물)

    >> IAttachMapper.java

    package com.stone.mvc.dataservice_board;
    
    import java.util.List;
    
    import org.apache.ibatis.annotations.Insert;
    import org.apache.ibatis.annotations.Mapper;
    import org.apache.ibatis.annotations.Param;
    import org.apache.ibatis.annotations.Result;
    import org.apache.ibatis.annotations.Results;
    import org.apache.ibatis.annotations.Select;
    
    import com.stone.mvc.common_board.Attach;
    
    @Mapper
    public interface IAttachMapper {
    	
    	@Insert("insert into attach(name,size,board_no) values(#{name},#{size},#{board.no})")
    	void save(Attach 첨부);
    	
    	@Insert({
    		"<script>",
    		"insert into attach(name,size,board_no) values",
    		"<foreach collection='attachs' item='attach' index='index' separator=','>",
    			"(#{attach.name},#{attach.size},#{attach.board.no})",
    		"</foreach>",
    		"</script>"
    	})
    	void saveAttachs(@Param(value="attachs")List<Attach> attachs);
    	
    	@Select("select * from attach where board_no=#{board_no}")
    	List<Attach> selectByBoardNo(@Param("board_no") int 게시물번호);
    
    	@Select("select * from attach where no=#{no}")
    	@Results(value = {
    			@Result(property="no", column="no"),
    			@Result(property="name", column="name"),
    			@Result(property="size", column="size"),
    			@Result(property="board.no", column="board_no")})
    	Attach findByNo(@Param("no") int 첨부번호);
    }
    1. board.no : Attach 의 board 참조변수의 변수인 no 을 가져온다.
    2. 반복insert문 사용하기 : {} 로 감싸야함. 각 문장의 구분자는 '+' 가 아닌 ',' 이다.
    <script> : 반복되는 문장들이 바로바로 insert 되지 않고 한번에 모았다 가도록 잡아놓는다.
    <foreach> : 향상된 for문
    - collection : 매개변수에서 가져오는 배열형태
    - item : collection 에서 가져온 배열을 하나씩 빼서 넣기
    - index : 반복수
    - separator : 반복되는 문장 끝에오는 구분자
    3. <ResultMap>(xml) = @Results(config)
    @Results
    - value : 여러 @Result 설정
    @Result : property 와 column 을 매핑시켜줌
    - property : 객체의 멤버변수명
    - column : 테이블 컬럼명 //as 로 별칭만들 경우 그 별칭!(어디까지나 추출된 테이블의 컬럼명이다.)

    >> IBoardMapper

    @Mapper
    public interface IBoardMapper {
    	@Insert("insert into board(title,contents,writer) values(#{title},#{contents},#{writer.no})")
    	@SelectKey(statement="select last_insert_id()", keyProperty="no", before=false, resultType=int.class)
    	int saveOnlyBoard(Board board);//attach 가 저장이 안됨. insert 할 때의 one to many 관계
    
    	@Select("select * from board where no=#{no}")
    	@Results(value= {
    			@Result(property="no",column="no"),
    			@Result(property="title",column="title"),
    			@Result(property="contents",column="contents"),
    			@Result(property="wdate",column="wdate"),
    			@Result(property="writer",column="writer",one=@One(select="com.stone.mvc.dataservice_member.IMemberMapper.findByNo")),
    			@Result(property="views",column="views"),
    			@Result(property="attachs",javaType=List.class,column="no",many=@Many(select="com.stone.mvc.dataservice_board.IAttachMapper.selectByBoardNo"))
    	})
    	Board findByNo(int no);
    	
    	@Update("update board set views=views+1 where no=#{no}")
    	void updateView(int no);
    	
    	@Select("select * from board")
    	@Results(value= {
    			@Result(property="no",column="no"),
    			@Result(property="title",column="title"),
    			@Result(property="contents",column="contents"),
    			@Result(property="wdate",column="wdate"),
    			@Result(property="writer",column="writer",one=@One(select="com.stone.mvc.dataservice_member.IMemberMapper.findByNo")),
    			@Result(property="views",column="views"),
    			@Result(property="attachs",javaType=List.class,column="no",many=@Many(select="com.stone.mvc.dataservice_board.IAttachMapper.selectByBoardNo"))
    			})
    	List<Board> selectAll();
    1. saveOnlyBoard()
    @SelectKey : 방금 insert 한 튜플의 key 값 가져오기
    - statement : key 값 가져오는 sql 문
    - keyProperty : key 값 컬럼이름, return 도 되고 board에 자동으로 이 이름의 멤버변수에 get된다.
    ( DAO 에서 따로 int no 로 받아 getNo(no) 하지 않아도 된다. )
    - resultType : 나오는 key 값의 변수형
    2. findByNo()
    1) 게시물상세보기도 조회수update 문도 함께 날려야하지만 sql문은 여러번 날릴 수 없으므로 따로 만들어야 한다.
    (DAO 에서 두 sql 문을 실행하면 된다. )

    2) one to one 관계 : 게시물상세보기에 게시물 작성자 즉, Member 도 와야한다
    @One : 자신(one도 되고 many도 될수 있음) to one 관계
    column 값을 property에 바로 매핑하지 않고,
    패키지명.클래스명.메서드명의 sql문에 column 값을 where에 넣어 결과값을 property에 매핑

    3) @Many : 자신(one도 되고 many도 될수 있음) to many 관계
    위와 동일
    javaType : 여러개니까 List<Attach> 인 멤버변수에 List형으로 넣어준다고 표시
    3. selectAll()
    자신이 단일 객체던 List 형이던 위의 findByNo() 와 똑같이 매핑시켜 하나씩 List에 넣어준다.

    >> BoardDAO ( DAO 의 일을 Mapper 로 돌리자 )

    @Repository
    public class BoardDAO implements IBoardDAO {
    	
    	@Autowired
    	private IBoardMapper boardMapper;
    	
    	@Autowired
    	private IAttachMapper attachMapper;
    	
    	//게시물 저장하기.
    	public int save(Board board) {
    		boardMapper.saveOnlyBoard(board);
    		attachMapper.saveAttachs(board.getAttachs());//for문 안쓰고 sql문으로 한번에 저장이 가능해졌다(script-foreach문)
    		
    		return board.getNo();
    	}
    		
    	// 게시물의 번호로 게시물의 상세내역 보기(+ 조회수 증가여부)
    	public Board findByNo(int no, boolean addView) {
    		if(addView) {boardMapper.updateView(no);}
    		return boardMapper.findByNo(no);
    	}
    
    
    	// 게시물전체 가져오기
    	public List<Board> selectAll() {
    		return boardMapper.selectAll();
    	}
    }

    >> ICommentMapper

    @Mapper
    public interface ICommentMapper {
    	@Insert("insert into comment(contents,writer,board_no) values(#{contents},#{writer.no},#{board.no}")
    	void save(Comment comment);
    	
    	@Select("select * from comment where board_no=#{no} order by wdate desc limit 0,#{size}")
    	@Results(value= {
    			@Result(property="no",column="no"),
    			@Result(property="contents",column="contents"),
    			@Result(property="wdate",column="wdate"),
    			@Result(property="writer.no",column="writer"),
    			@Result(property="board.no",column="board_no")
    	})
    	List<Comment> selectByBoardNo(int no, int size);
    	
    	@Select("select count(*) from comment where board_no=#{no}")
    	int totalSizeByBoardNo(int no);
    }
    1. board 에 달린 모든 Comment 객체와 댓글총수 구하는 메서드도 분리해야 한다.(sql문이 2개 였으니까)
    //굳이 board 객체를 다 채울 필요 없으니 no 만 셋팅했다.
    (만약 board 객체 다채울라면 one to one(댓글기준관계) 로 board의 메서드 끌어와야한다.)

    >> CommentDAO ( DAO 의 일을 Mapper 로 돌리자 )

    @Repository
    public class CommentDAO implements ICommentDAO {
    	@Autowired ICommentMapper commentMapper;
    
    	@Override
    	public void save(Comment comment) {
    		commentMapper.save(comment);
    		
    	}
    
    	@Override
    	public List<Comment> selectByBoardNo(int no, int size, RefInteger totalSize) {
    		totalSize.value = commentMapper.totalSizeByBoardNo(no);
    		return commentMapper.selectByBoardNo(no, size);
    	}
    }

    'JAVA' 카테고리의 다른 글

    3Tier: 예외처리,@Qualifier  (0) 2021.07.27
    Spring:interceptor,3Tier(Presentation,Business,Dataservice)  (0) 2021.07.27
    BLOB 이미지파일업로드  (0) 2021.07.27
    Spring:댓글더보기(ajax.ver)  (0) 2021.07.26
    Spring:댓글(ajax.ver)  (0) 2021.07.26

    댓글

Designed by Tistory.