Notice
Recent Posts
Recent Comments
Link
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

wintertreey 님의 블로그

DB 연동3/4_mybatis. lombok annotation. 본문

Spring

DB 연동3/4_mybatis. lombok annotation.

wintertreey 2024. 7. 14. 17:32

DB 연동방법 4가지


1. 클래스내에서 직접 sql문 기술
2. 스프링이 지원하는 jdbcdao support 사용
3. mybatis 프레임워크사용
4. jpa 사용 

 

MyBatis란?

 

 

MyBatis 프레임워크는 반복적인 JDBC 프로그래밍을 단순화하여, 불필요한 Boilerplate 코드를 제거하고, Java 소스코드에서 SQL 문을 분리하여 별도의 XML 파일로 저장하고, 이 둘을 서로 연결시켜주는 기능을 제공해준다.

 

MyBatis의 특징

Java 코드와 SQL 매핑

MyBatis를 사용하면, MyBatis 내부에서 그러한 Boilerplate 코드가 구현되어 있고, MyBatis에서 Java 메소드와 SQL 간에 매핑을 시켜서 개발자는 Java 메소드 선언과 SQL 문만 만들면 MyBatis가 자동으로 그 둘 간을 연결시켜 주게 된다. 

 

SQL 문장이 Java 코드 내에 들어가 있어서 수정하기도 힘들고, 보기도 힘들었는데, SQL 문을 별도로 Java 코드에서 분리해두어서 관리가 편하게 하였으며, 분리된 SQL 문을 MyBatis가 찾아서 실행해 주는 기능을 한다.

 


 

 


 
mybatis 연동 작업을 위해, pom.xml에 mybatis 소스코드를 추가한다.
이번에는 롬복도 추가했다. 
 
하단의 링크는 maven 작업시 소스를 얻기 위해 참고한 링크다. 
해당 사이트에서 모든 소스코드를 얻을 수 있다. 최고!
https://central.sonatype.com/artifact/org.projectlombok/lombok

pom.xml 일부분 캡쳐

 
이번 작업시에는 롬복 어노테이션도 사용했는데 이번 기회에 정리해보고 넘어가자.
 


Lombok

롬복 어노테이션 정리

@Getter : Getter를 자동으로 생성
@Setter : Setter를 자동으로 생성
@Data: getter, setter를 한번에 자동으로 생성.
> 보통은 이것만 쓴다. 그러나 잘 먹히지 않는경우도 있기에, 꼭 outline에서 확인해야한다. 
 
이번에 새로 알게된 어노테이션. 
@NoArgsConstructor : 매개 변수가 없는 기본 생성자를 생성
@AllArgsConstructor : 모든 필드를 포함한 생성자를 생성

해당 어노테이션작업을 통해, outline에서 기본생성자 SangpumDto(), 모든 파라미터를 포함한 생성자 SangpumDto(----) 가 생성되었음을 확인할 수 있다. 
(번거롭더라도 꼭 outline에서 확인하는 버릇 갖고 가자)
 

롬복을 사용하는 이유

롬복은 빌더패턴을 지원한다. 그리고 객체를 생성할 때는 반드시 빌더 패턴을 사용해야한다고 하는데, 왜그럴까?
여기서 빌더패턴의 간단한 정의는 하단의 사진을 참고하자.

 

빌더패턴의 장점

  1. 필요한 데이터만 설정할 수 있음
  2. 유연성을 확보할 수 있음
  3. 가독성을 높일 수 있음
  4. 변경 가능성을 최소화할 수 있음

@Builder

 

 

수제비 정보처리기사 실기 일부분

빌더 패턴에 대한 자세한 설명은 하단에 첨부한 링크를 참고하자.

 


Back to 작업.

 

pack.mybatis는 전에 mybatis 작업시 공부했던 내용을 참조하였다.

 

pack.model

인터페이스를 구현하는 SangpumImpl클래스에 @repositoy 걸어주었다. 

 

불러올 메소드는 하나뿐이다. selectDataAll().

package pack.model;

import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.stereotype.Repository;

import pack.mybatis.SqlMapConfig;

@Repository
public class SangpumImpl implements SangpumInter{
	private SqlSessionFactory factory = SqlMapConfig.getSqlSession();
	
	@Override
	public List<SangpumDto> selectDataAll() {
		SqlSession session = factory.openSession();
		List<SangpumDto> list = null;
		
		try {
			list = session.selectList("selectDataAll"); //datamapper의 sql
		} catch (Exception e) {
			System.out.println("selectDataAll err: "+e);
		}finally {
			if(session != null) session.close();
		}
		
		
		return list;
	}
}


 
pack.business
클래스에 @service를 걸어주어 해당 클래스가 로직을 구현할 클래스임을 표시해준다. 
 
init.xml

@Repository, @Service 걸어준 것 잊지말고, 

init.xml에 component-scan작업. 


 콘솔 출력!


이번에는 annotation을 이용해 좀 더 간결하게 작업하여 문제를 풀어보자. 


@ annotation을 이용한 작업

앞선 작업과 다른 점만 기술한다.

 

  •  Model 팩에 sqlmapperinter.java.

sqlmappterinter.java에서 메소드와 해당 메소드에 매칭시킬 sql문을 @Select 로 적어준다.

package pack.model;

import java.util.List;

import org.apache.ibatis.annotations.Select;

public interface SqlMapperInter {
	@Select("SELECT jikwon_no, jikwon_name, ifNull(buser_name, '무소속') as buser_name, year(jikwon_ibsail) as jikwon_ibsail "
			+ "FROM jikwon LEFT JOIN buser ON buser_num= buser_no")
	public List<JikwonDto> selectDataAll();
	
	@Select("select buser_name, count(*) as cc from jikwon inner join buser on buser_num=buser_no group by buser_num")
	public List<JikwonDto> selectDataBuser();
	
	@Select("select buser_name, jikwon_name, jikwon_pay from jikwon "
			+ "inner join buser on buser_num=buser_no "
			+ "where (buser_num, jikwon_pay) in (select buser_num, max(jikwon_pay) from jikwon group by buser_num)")
	public List<JikwonDto> selectDataBuserMax();
	//select buser_name, jikwon_name, jikwon_pay from buser inner join jikwon on buser_no=buser_num 
	//where jikwon_pay in(select max(jikwon_pay) from jikwon j where j.buser_num=jikwon.buser_num)
}

 

  • mybatis 팩에 dataMapper.xml 파일 제거

앞선 작업에서는 DataMapper.xml에 sql문들을 적어주었다. 이제 그 작업을 SqlMapperIner에서 수행하기에 불필요하므로 제거해주자.

 

  • Configuration.xml

typeAliases 부분과, DataMapper.xml 매핑 작업을 한 부분도 지워준다. 

 

  • SqlMayConfig.java
package pack.mybatis;

import java.io.Reader;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import pack.model.SqlMapperInter;


public class SqlMapConfig {
	public static SqlSessionFactory sqlSessionFactory;  //DB의 SQL명령을 실행시킬 때 필요한 메소드를 갖고 있다.
	 
	  static{
	     String resource = "pack/mybatis/Configuration.xml";
	     try {
	         Reader reader = Resources.getResourceAsReader(resource);
	         sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
	         reader.close();
	         
	      // mybatis annotation 사용시 추가   
	         Class[] mappers= {SqlMapperInter.class};
	         for(Class cl:mappers) {
	        	 sqlSessionFactory.getConfiguration().addMapper(cl);
	         }
 	     } catch (Exception e) {
	     System.out.println("SqlMapConfig 오류 : " + e);
	  }
	}
	 
	public static SqlSessionFactory getSqlSession(){
	     return sqlSessionFactory;
	  }
	
}

 

주석 처리 부분으로 해당 부분을 체크해두었다. 

 

출력결과물 캡쳐본

 

 


 

문제를 풀면서 마주한 sql문 에러 두가지 

package pack.model;

import java.util.List;

import org.apache.ibatis.annotations.Select;

public interface SqlMapperInter {
	@Select("SELECT jikwon_no, jikwon_name, ifNull(buser_name, '무소속') as buser_name, year(jikwon_ibsail) as jikwon_ibsail "
			+ "FROM jikwon LEFT JOIN buser ON buser_num= buser_no")
	public List<JikwonDto> selectDataAll();
	
	@Select("select buser_name, count(*) as cc from jikwon inner join buser on buser_num=buser_no group by buser_num")
	public List<JikwonDto> selectDataBuser();
	
	@Select("select buser_name, jikwon_name, jikwon_pay from jikwon "
			+ "inner join buser on buser_num=buser_no "
			+ "where (buser_num, jikwon_pay) in (select buser_num, max(jikwon_pay) from jikwon group by buser_num)")
	public List<JikwonDto> selectDataBuserMax();
	
}

 

1. 

 

두번째 sql문 부분 별명 cc를 보자.  (사실 이 설명은 첫번째 sql문 buser_name, jikwon_ibsail에도 적용된다. 그러나 두 별명은 모두 칼럼명으로 이미 선언+사용하고 있었기에, 정말 필요에 의해서 나중에 추가된 멤버변수 cc에 대해 설명하고자 한다.)

 

만약 as XX라고 별명을 붙였다면, get으로 부를때도 별명으로 불러줘야한다. 

BusinessImpl.java 일부분 

@Override
	public void dataPrint2() {
		System.out.println("\n---부서별 인원수---");
		List<JikwonDto> list = inter.selectDataBuser();

		// console로 출력
		for (JikwonDto b : list) {
			System.out.println(b.getBuser_name() + " "+ b.getCc()); 
		}

 

그리고 그 별명은 dto에 멤버변수로 선언되었어야 한다. 

JikwonDto.java 일부분


@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class JikwonDto {
	private String jikwon_no, jikwon_name, buser_name, jikwon_ibsail, jikwon_pay, cc;
}

 

 

2. 

가장 마지막 부분, 부서별 최대 급여자 부분의 sql문.

select buser_name, jikwon_name, jikwon_pay from jikwon 
inner join buser on buser_num=buser_no
where jikwon_pay = (select max(jikwon_pay) from jikwon group by buser_num);


ERROR 1242 (21000): Subquery returns more than 1 row

 

에러에 대한 리서치 결과.
The error you're encountering, ERROR 1242 (21000): Subquery returns more than 1 row, occurs because your subquery (select max(jikwon_pay) from jikwon group by buser_num) is returning multiple rows. This happens when there are multiple distinct jikwon_pay values for different buser_num groups. To fix this issue, you need to rethink how you're using the subquery. It seems like you want to find the maximum jikwon_pay for each buser_num and then retrieve the corresponding buser_name, jikwon_name, and jikwon_pay from the jikwon and buser tables.

 

SELECT buser_name, jikwon_name, jikwon_pay FROM jikwon INNER JOIN buser ON buser_num=buser_no
WHERE (buser_num, jikwon_pay) in (SELECT buser_num, MAX(jikwon_pay) FROM jikwon GROUP BY buser_num);

 

이렇게 where () in () 을 통해 직원연봉의 최대값과 해당 부서넘버와의 매칭작업을 수행할 수 있다.

 

where 구문에 대한 추가 설명.

WHERE Clause ((jikwon.buser_num, jikwon.jikwon_pay) IN (...)):

    • Filters the rows to only include those where the pair (jikwon.buser_num, jikwon.jikwon_pay) matches any pair in the result of the subquery.
    • This ensures that we are selecting rows from jikwon where jikwon_pay is the maximum pay for each buser_num group.

This approach ensures that you correctly retrieve the buser_name, jikwon_name, and jikwon_pay corresponding to the maximum jikwon_pay for each buser_num, resolving the issue of multiple rows returned by the subquery.

 

 

다른 동료분들의 좋았던 sql문을 덧붙인다. 

 

From DJ
SELECT buser_name, jikwon_name, jikwon_pay FROM jikwon INNER JOIN buser ON buser_num = buser_no 
WHERE jikwon_pay = (SELECT MAX(jikwon_pay) FROM jikwon WHERE buser_num = buser_no GROUP BY buser_num);
 ✔️ from 절의 buser 테이블의 buser_no를 서브쿼리에서 참조

 

From WJ
select buser_name, jikwon_name, jikwon_pay from buser inner join jikwon on buser_no=buser_num
where jikwon_pay in(select max(jikwon_pay) from jikwon j where j.buser_num=jikwon.buser_num)

 

From JH
SELECT buser_name, jikwon_name, jikwon_pay FROM jikwon INNER JOIN buser ON buser_num = buser_no, 
(SELECT buser_num, MAX(jikwon_pay) maxpay FROM jikwon GROUP BY buser_num) maxtab 
WHERE jikwon.buser_num = maxtab.buser_num AND jikwon_pay = maxtab.maxpay

 

 

db연동 by mybatis 부분에 대한 전체적인 코드는 깃허브 spr17, 18를 참고하자.

https://github.com/yoonah0327/spring_source.git
 

 

GitHub - yoonah0327/spring_source

Contribute to yoonah0327/spring_source development by creating an account on GitHub.

github.com

 


https://velog.io/@0giri/Lombok-%EC%82%AC%EC%9A%A9%EB%B2%95

 

Lombok 어노테이션 정리

force : final 필드가 존재할 때 이를 null 또는 0으로 초기화해 기본 생성자를 만들 수 있게 한다.access : 생성되는 메소드의 접근제어자 설정 (AccessLevel)onConstructor\_ = {@어노테이션,...} : 생성된 생성자

velog.io

https://mangkyu.tistory.com/163

 

[Java] 빌더 패턴(Builder Pattern)을 사용해야 하는 이유

객체를 생성하기 위해서는 생성자 패턴, 정적 메소드 패턴, 수정자 패턴, 빌더 패턴 등을 사용할 수 있습니다. 개인적으로 객체를 생성할 때에는 반드시 빌더 패턴을 사용해야 한다고 생각하는

mangkyu.tistory.com

 

https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EB%B9%8C%EB%8D%94Builder-%ED%8C%A8%ED%84%B4-%EB%81%9D%ED%8C%90%EC%99%95-%EC%A0%95%EB%A6%AC

 

💠 빌더(Builder) 패턴 - 완벽 마스터하기

Builder Pattern 빌더 패턴(Builder Pattern)은 복잡한 객체의 생성 과정과 표현 방법을 분리하여 다양한 구성의 인스턴스를 만드는 생성 패턴이다. 생성자에 들어갈 매개 변수를 메서드로 하나하나 받아

inpa.tistory.com