개인프로젝트/어린이사고다발지역검색

spring-boot h2database 설정 및 data-jpa 관련 작업

질주하는구 2022. 3. 18. 14:45

오픈api에서 데이터 가지고 오는 작업을 완료 했으니 서비스 페이지에서 사용할 법정동 코드 정보를

DB에 구축하고 해당 정보를 조회하는 data-jpa관련 소스 작업을 진행 해야 합니다.

 

실제 서비스가 아닌 연습용이고 1개의 테이블에 몇천건의 데이터만 있으면 되기 때문에 database는 h2 데이터베이스를 사용 할것이고 조회는 data-jpa을 이용해서 간단하게 작성 하려고 합니다.

 

우선 h2 database관련 설정을 추가 하기 위해 application.properties에 아래의 내용을 추가 해줍니다.

spring.h2.console.enabled=true
spring.h2.console.path=/h2-console

spring.datasource.url=jdbc:h2:mem:test;
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

url을 보면 h2메모리 DB를 사용할것이기 때문에 h2:mem:test로 명시되어 있습니다. 이렇게 작성 하면 메모리에 test라는

database가 생성되고 서비스 운영중에만 유효한 데이터 변동이 발생됩니다.

(서비스 종료시에는 작업 내용이 모두 삭제 되기 때문에 해당 내용을 유지하고자 하면 h2:~/test 로 설정을 해주면 됩니다. 이 경우 내문서 하위에 test db파일이 생성되고 작업 내용을 서비스 여부와 상관없이 유지 해줍니다.)

비밀번호 없이 로그인 가능하기 때문에 비밀번호 설정은 하지 않아도 됩니다.

 

상단의 console관련 설정이 필요 없다면 하지 않아도 되지만 운영중 테이블 정보를 확인 하기 위해서는 해당 설정을 해주는게 좋습니다.

운영중인 서비스에 /h2-console path로 접속을 해주면 로그인 화면이 나오고 간단한 관리자 화면이 노출 됩니다.

접속시 jdbc url 부분을 mem에 맞춰 주셔야 합니다.

접속 하면 운영중인 메모리 DB의 내용을 확인 할 수 있습니다.

 

해당 설정 까지 해주시면 h2 데이터베이스에 대한 기본적인 설정은 마무리가 되었습니다.

이제 추가 적인 설정 부분을 작성 하고 설정 부분에 대한 설명은 마무리 하겠습니다.

spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
spring.sql.init.mode=always
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.hibernate.ddl-auto=none

위의 내용중 구현체로 하이버네티를 사용하기 때문에 하이버네이트 관련 설정이 들어가 있습니다.

특별한 내용이 없는데 특이한 부분은 있습니다. sql.init.mod / hibernate.ddl-auto 부분 입니다. 저는 schema.sql, data.sql

파일을 이용해서 데이터 베이스 초기화를 해줄것이기 때문에 sql.init.mode 부분을 활성화 했습니다. 또한 hibernate의

auto ddl관련 부분이 중복으로 실행되어 문제를 일의 킬수 있기 때문에 해당 부분은 동작하기 않게 none로 설정 했습니다.

(의도하지 않은 DB변화가 생기는 경우가 있어 하이버네이트 단독으로 사용할때는 저 부분은 none로 설정하고 사용 했었습니다.)

해당 설정에 맞춰서 resources 폴더 하위에 schema.sql 파일과 data.sql파일을 생성해서 넣어 주시면 서비스 시작시 해당 테이블과 정보가 생성 됩니다.

 

이제 h2 데이터베이스와 data-jpa관련 설정이 끝났으니 data-jpa관련 소스를 생성 해주면 됩니다.

repository 폴더 하위에 데이터 조회를 위한 class를 생성 해줍니다.

@Repository("koreaAreaCodeRepository")
public interface KoreaAreaCodeRepository extends JpaRepository<AreaCode, String>{
	
	//depth에 해당하는 지역코드 목록
	List<AreaCode> findByAreaDepth(String areaDepth, Sort sort);
	
	//parentCode에 해당하는 지역코드 목록
	List<AreaCode> findByParentCode(String parentCode, Sort sort);
}

queryDsl까지는 필요 하지 않고 간단하게 메소드로 쿼리문을 작성 할 수 있습니다.

해당 메소드를 간단하게 설명 해주면

findByAreaDepth(String areaDepth, Sort sort) select쿼리 실행 where 조건 area_depth order by 조건으로 Sort객체 사용

입니다.

해당 repository를 호출하는 service 부분 소스를 작성 해야 합니다.

@Service("koreaAreaCodeService")
public class KoreaAreaCodeServiceImpl implements KoreaAreaCodeService {
	private Logger logger = LoggerFactory.getLogger(this.getClass());
	
	@Resource(name="koreaAreaCodeRepository")
	private KoreaAreaCodeRepository koreaAreaCodeRepository;
	
	@Override
	public List<AreaCodeDTO> koreaAreaCodeList(String depth) throws Exception {
		List<AreaCode> dataList 					= koreaAreaCodeRepository.findByAreaDepth(depth, Sort.by(Sort.Direction.ASC, "viewOrder"));
		List<AreaCodeDTO> areaCodeList 	= dataList.stream().map(AreaCode::entityToDTO).collect(Collectors.toList());
		
		return areaCodeList;
	}

	@Override
	public List<AreaCodeDTO> koreaAreaCodeChildList(String parentCode) throws Exception {
		List<AreaCode> dataList 					= koreaAreaCodeRepository.findByParentCode(parentCode, Sort.by(Sort.Direction.ASC, "viewOrder"));
		List<AreaCodeDTO> areaCodeList 	= dataList.stream().map(AreaCode::entityToDTO).collect(Collectors.toList());
		
		return areaCodeList;
	}

}

설명할 만한 부분이 거의 없습니다. 정렬 조건을 추가 해주는 부분과 domain->DTO로 변경 하는 부분만 보시면 됩니다.

해당 class에서 사용한 domain 및 DTO 클래스는 아래와 같습니다.

@Entity
@Getter
@ToString
@NoArgsConstructor
@Table(name = "KOREA_AREA_CODE")
public class AreaCode implements Serializable{
	private static final long serialVersionUID = 1L;
	@Id
    @Column(name = "AREA_CODE", nullable=false, insertable=true, updatable=false)
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private String areaCode;
	
	@Column(name = "AREA_NAME", length=100, nullable=true, insertable=true, updatable=true)
	private String areaName;
	
	@Column(name = "PARENT_CODE", length=10, nullable=true, insertable=true, updatable=true)
	private String parentCode;
	
	@Column(name = "VIEW_ORDER", nullable=true, insertable=true, updatable=true)
	private Integer viewOrder;
	
	@Column(name = "AREA_DEPTH", length=2, nullable=true, insertable=true, updatable=true)
	private String areaDepth;
	
	@JsonIgnore
	@ManyToOne(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY)//LAZY 지연호출, EAGER 즉시호출
    @JoinColumn(name = "PARENT_CODE", insertable=false, updatable=false)
    private AreaCode parent;
	
	@JsonProperty("nodes")
	@OneToMany(fetch = FetchType.LAZY)
	@JoinColumn(name = "PARENT_CODE")
	@OrderBy("viewOrder asc")
	private List<AreaCode> children = new ArrayList<AreaCode>();
	
	@Builder(builderClassName = "ByAreaCodeBuilder", builderMethodName = "ByAllBuilder")
	public AreaCode(String areaCode, String areaName, String parentCode, Integer viewOrder) {
		this.areaCode = areaCode;
		this.areaName = areaName;
		this.parentCode = parentCode;
		this.viewOrder = viewOrder;
	}
	
	public AreaCodeDTO entityToDTO() {
		AreaCodeDTO areaCodeDTO = new AreaCodeDTO();
		ModelMapper mMapper = new ModelMapper();
		mMapper.map(this, areaCodeDTO); 
		
		return areaCodeDTO;
    }
}
@Getter
@Setter
@ToString
@NoArgsConstructor
public class AreaCodeDTO {
    private String areaCode;
	private String areaName;
	private String parentCode;
	private Integer viewOrder;
	
	public AreaCode dtoToEntity() {
		AreaCode areaCode = AreaCode.ByAllBuilder().areaCode(this.areaCode).areaName(this.areaName).parentCode(this.parentCode).viewOrder(this.viewOrder).build();
		return areaCode;
    }
}

특이한 내용은 없고 소스 간소화를 위해 lombok을 사용했다 정보만 인지 하시면 될거 같습니다.

이제 해당 서비스를 호출하는 Controller 부분만 간략하게 다음에 정리하면 backend부분은 모두 마무리가 됩니다.

반응형