스프링 부트에서 사용하는 JPA 기능 정리

web/JPA|2018. 11. 4. 23:53

스프링 프레임워크에서 제공하는 JPA는 별도의 구현 클래스 없이 인터페이스만을 사용할 수 있도록 제공한다. 제공되는 인터페이스 JpaRepository는 실행시점에 자동으로 인터페이스 내용을 연결하는 엔티티에 맞게 자동으로 구현해준다. 만약 스프링 JPA 인터페이스에서 제공하지 않는 기능을 사용하고 싶을 때는 메서드명을 특정한 규칙대로 만들어서 사용하면 인터페이스가 알아서 그 이름에 맞는 JPQL을 만들어서 실행해준다.


스프링 JPA 인터페이스는 Mysql같은 RDBMS 뿐만 아니라 Mongodb, Redis와 같은 NoSQL에도 동일한 인터페이스를 사용해서 기능을 사용할 수 있도록 제공해준다. 공통으로 사용할 수 있기에 아주 편리하다.


우선 스프링 부트에 JPA를 사용하기 위해서 Gradle에 라이브러리를 넣자.

1
compile ('org.springframework.boot:spring-boot-starter-data-jpa')
cs

 

기본적인 구조

JpaRepository를 상속받아 구현하고자 하는 인터페이스를 만들고 제네릭에 구현하려는 엔티티와 엔티티의 식별자 타입을 지정하여 인터페이스를 선언한다.

1
2
3
public interface StudentRepository extends JpaRepository<Student, Long> {
}
 
cs


주요 메서드 몇개만 정리해보자.

save() : 저장하거나 업데이트한다.

delete(entity) : em.remove() 호출하여 엔티티를 제거한다.

findOne(ID) : em.find() 호출하여 엔티티를 찾는다.

findAll() : 엔티티 모두를 조회한다. 


JpaRepository 인터페이스로 부족한 기능을 구현한 PagingAndSortingRepository CrudRepository 사용해서 보완할 있다.


JPA에서 메서드 사용하는 방법

JPA에서 엔티티에 맞는 메서드를 사용하는 방법은 크게 3가지이다.

- 메서드 이름으로 쿼리 생성

- 메서드 이름으로 JPA NamedQuery 호출

- @Query 어노테이션을 사용해서 레포지토리 인터페이스에 쿼리 직접 정의


1. 메서드 이름으로 쿼리 생성

메서드 이름으로 쿼리를 생성할 있는데 정식 Document 이용하면 자세히 나와있다

https://docs.spring.io/spring-data/jpa/docs/2.1.2.RELEASE/reference/html/#jpa.query-methods


2. 메서드 이름으로 JPA NamedQuery 호출

JPA NamedQuery 쿼리에 이름을 부여해서 사용하는 방법은 다음과 같이 엔티티에 선언해주면 된다.

1
2
3
4
5
@NamedQuery(
  name="student.findByName",
  query="select s from student s where s.name = :name")
public class Student {

}
cs


위와 같이 선언하고 실제 사용할 때는 entityManager에 아래와 같이 createQuery를 사용해서 쿼리를 호출하면 된다.

1
2
3
4
5
6
7
@PersistenceContext
private EntityManager entityManager;
 
public List<Student> findByUser(String name) {
  List<Student> students = entityManager.createQuery("student.findByName", Student.class).setParameter("name""wedul").getResultList();
}
 
cs


위와 같이 EntityManager를 사용할 수 있지만 스프링 JPA를 사용하여 간단하게 메소드 이름만으로 호출이 가능하다. 이렇게 호출하면 레포지토리에서 Student.쿼리메소드 형태로 쿼리를 찾는다. 만약 실행할 쿼리가 없으면 메서드 이름으로 쿼리를 자동으로 바꿔 동작한다.

1
2
3
4
5
public interface StudentRepository extends JpaRepository<Student, Long> {
  
  List<Student> findByUserName(@Param("name") String name);
  
}
cs


3. @Query 어노테이션을 사용해서 레포지토리 인터페이스에 쿼리 직접 정의

2번에서는 @Entity 클래스에서 정의한 쿼리를 레포지토리에서 메소드 형태로 접근하여 사용하였다.  이번에는 레포지토리에서 직접적으로 쿼리를 만들어서 조회하는 방식을 확인해보자.


@Query("select s from Student s where s.name = ?1")

Student findByName(String name);


인터페이스에 정의하는 메소드에 @Query 어노테이션을 붙혀서 정의하고 사용하면 된다. 바인딩 값은 1부터 시작한다. 스프링 데이터 JPA에서는 ?1 ?2 같은 위치기반 파라미터와 :name 같은 이름 기반 방식을 모두 사용가능하다.


페이징과 정렬

스프링 데이터 JPA에서 쿼리 메서드에 페이징과 정렬 기능을 사용할 있다. 파라미터로 Pageable 인터페이스를 사용할 경우에는 Page 또는 List 반환 받을 있다.

1
2
3
4
public interface StudentRepository extends JpaRepository<Student, Long> {
 
  Page<Student> findByNameStartingWith(String name, Pageable pageable);
}
cs


실제 사용할 때는 Pageable 인터페이스이기 때문에 구현체인 PageRequest 객체를 사용해서 사용한다.

// 파라미터 순서대로 페이지 번호, 사이즈, 정렬 기준 등으로 사용한다.

1
PageRequest pageRequest = new PageRequest(0, 10, new Sort(Sort.Direction.DESC, "name"));
cs


반환되는 값인 Page에서 제공하는 다양한 메소드를 사용해서 편하게 페이징과 소트 기능을 사용할 있다. 

컨트롤러에서 사용자 요청에게 전달되는 페이징 정보를 받기 위해서는 다음과 같이 Pageable 인터페이스를 받으면 되고 받을 속성값은 page, size, sort 사용해서 받는다. (/student?page=0&size=20&sort=name,desc&sort=address.city)


1
2
3
4
5
6
7
@GetMapping("/student")
public String list(Pageable pageable, Model model) {
  Page<Student> page = studentRepository.findByNameStartingWith("dd", pageable);
 
  return "dfdfdf";
}
 
cs


참고 싸이트

https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/domain/Page.html


출처. : 자바 ORM 표준 JPA 프로그래밍


댓글()
  1. ParkNoIl 2020.02.25 16:10 댓글주소  수정/삭제  댓글쓰기

    안녕하세요 선배님? 잘지내시나요?

    다름이 아니라 회사 내에서 응용프로그램(.exe파일)을 개발 해야하는데,

    JPA에 대해 공부도 할겸 적용을 해볼까하는데, 응용프로그램에 JPA를 적용 할수 있나요?

    JPA 관련 인강 및 구글링에서는 대부분 웹과 연동 하여 사용한 내용만 있어서요..

    답변 부탁드립니다. 감사합니다.

    • Favicon of https://wedul.site BlogIcon 위들 wedul 2020.02.29 22:29 신고 댓글주소  수정/삭제

      안녕하세요 잘지내죠??

      exe 프로그램을 자바로 만드시려면 rcp 아니면 swing으로 하실거 같은데

      jpa를 spring data를 사용하는 방식처럼은 사용하기 어려워도 https://wiki.eclipse.org/EclipseLink/Examples/JPA/RCP

      https://stackoverflow.com/questions/7491761/jpa-on-a-desktop-swing-application

      위 링크들과 같이 EntityManager를 잘 활용하면 어플리케이션에서도 사용할 수 있을거 같아요.

    • ParkNoIl 2020.03.01 08:50 댓글주소  수정/삭제

      선배님 답변 감사합니다.

연관관계 매핑 - 다대일 매핑 (단반향)

web/JPA|2018. 10. 23. 18:30

연관관계 매핑을 해야하는 경우가 많다. 예를 들어 학생을 가지고 학생에 소속 반을 찾거나 반을 사용해서 학생들을 찾거나 때가 있다.

 

양방향과 단방향 관계가 존재하는데 아래의 객체 형태를 보면 이해가 쉽다.

 

단방향

class Student {

 Class class;

}

 

class Class {}

 

양방향

class Student {

 Class class;

}

 

class Class {

 Student student;

}


이중에서 먼저 단방향 연관 관계에 대해 먼저 공부해보자.

 

단방향 연관관계

  • 학생과 반이있다.
  • 학생은 하나의 반에 소속된다.
  • 학생과 반은 다대일 관계이다. (학생이 , 반이 )
  • 학생 테이블을 담는 객체는 Student, 반 테이블을 담는 객체는 Classes를 사용한다.
  • 학생 테이블은 반 테이블의 키 CLASSES_ID를 외래키로 테이블과 연관 관계를 가진다.

 

학생 테이블이 다, 반 테이블이 일로 다대일 관계이기 때문에 Student 클래스에 포함된 Classes 객체에 다음과 같이 선언한다.


1
2
3
@ManyToOne
@JoinColumn(name="CLASSES_ID")
private Classes classes;
cs


@ManyToOne

- 다대일 관계라는 표현

 

 

@JoinColumn(name = "CLASSES_ID")

- 조인 컬럼은 외래키를 매핑할 때 사용한다. 이 어노테이션은 생략해도 된다.

- 만약 생략 할 경우 외래키는 다음과 규칙으로 찾는다. 필드명 + _ + 참조하는 테이블의 컬럼명 그래서 위에서는 생략할 경우 classes_CLASSES_ID의 형태의 외래키를 찾는다.


다대다 테이블 관계 매핑설정이 끝난 객체


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
32
package com.wedul.springboottest.student.dto;
 
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
 
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
 
/**
 * springboottest
 *
 * @author wedul
 * @since 23/10/2018
 **/
@Table(name = "classes")
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Classes {
 
  @Id
  @Column(name = "CLASSES_ID")
  private String id;
 
  @Column(name = "name")
  private String name;
 
}
cs
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
32
33
package com.wedul.springboottest.student.dto;
 
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
 
import javax.persistence.*;
 
/**
 * springboottest
 *
 * @author wedul
 * @since 23/10/2018
 **/
@Entity
@Table(name = "student")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
 
  @Id
  @Column(name = "STUDENT_ID")
  private String id;
 
  @Column(name = "name")
  private String name;
 
  @ManyToOne
  @JoinColumn(name = "CLASSES_ID")
  private Classes classes;
 
}
cs


삽입

그럼 이 다대다 관계를 이용해서 데이터를 넣어보자. entityManager를 데이터를 삽입하는 코드를 만들자

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
32
33
34
35
36
37
38
39
40
41
42
package com.wedul.springboottest.student.serviceImpl;
 
import com.wedul.springboottest.student.dto.Classes;
import com.wedul.springboottest.student.dto.Student;
import com.wedul.springboottest.student.service.StudentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
 
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;
 
/**
 * springboottest
 *
 * @author wedul
 * @since 23/10/2018
 **/
@Service
@Slf4j
public class StudentServiceImpl implements StudentService {
 
  @PersistenceContext
  private EntityManager entityManager;
 
  public StudentServiceImpl(EntityManager entityManager) {
    this.entityManager = entityManager;
  }
 
  @Override
  @Transactional
  public void insertStudent() {
    Classes classes1_1 = new Classes("1-1""1학년 1반");
    entityManager.persist(classes1_1);
 
    Student wedul = new Student("1-1-01""wedul", classes1_1);
    entityManager.persist(wedul);
 
    Student chul = new Student("1-1-02""chul", classes1_1);
    entityManager.persist(chul);
  }
}
cs


테스트 코드를 통해 테스트해보자.

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
package com.wedul.springboottest.member;
 
import com.wedul.springboottest.member.service.MemberService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 
/**
 * springboottest
 *
 * @author wedul
 * @since 03/10/2018
 **/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest // rolleback 설정
public class MemberTest {
 
  @Autowired
  MemberService memberService;
 
  @Test
  public void jpa_test() {
    memberService.jpaService();
  }
 
}
 
cs


조회

1

2

3

4

5

6

@Override

@Transactional

public void selectStudent() {

  Student wedul = entityManager.find(Student.class"1-1-01");

  print(wedul);

}

Colored by Color Scripter

cs

 

출력 결과

아이디 : 1-1-02, 이름 : chul, 소속반: 1?? 1?

 

JPQL을 사용한 조회

1

2

3

4

5

6

7

8

9

 

  @Override

  @Transactional

  public void selectStudentWithJPQL() {

    String jpql = "select s from Student s join s.classes c where s.name=:studentName";

 

    Student student = entityManager.createQuery(jpql, Student.class).setParameter("studentName""chul").getSingleResult();

    print(student);

  }

Colored by Color Scripter

cs

 

 


연관관계제거


1

2

3

4

5

6

@Override

@Transactional

public void removeClasses() {

  Student wedul = entityManager.find(Student.class"1-1-01");

  wedul.setClasses(null);

}

Colored by Color Scripter


wedul 학생의 반 정보가 지워진것을 알 수 있다.


생각보다 어렵지 않다. 

댓글()

JPA(Java Persistence Api)의 장점과 단점 및 사용해야 하는 이유.

web/JPA|2018. 10. 6. 10:56

국내에서는 Mybatis를 사용하지만 대기업과 여러 스타트업에서는 JPA를 사용하고 있는 추세이다. 그리고 세계적으로 JPA 특히 ORM 프레임워크인 하이버네이트 사용률이 더 높다.

그래서 더 자세한 부분을 이해하기 위해서 공부를 시작했다. 공부 시작하기전에 왜 JPA가 좋은지 정리해봤다.


생산성

- 개발자가 일일히 CRUD용 쿼리를 작성해줘야하던 Mybatis와 같은 Mapper방식은 컬럼이 추가되거나하면 수정해주어야하는 부분이 상당히 많았다. 이로 인해서 자바를 사용하지만 객체중심 개발이 아니라 데이터베이스 흐름으로 개발을 하게되는 문제가 있다. JPA를 사용하게 되면 쿼리를 직적 생성하는 것이 아니고 만들어진 객체로 데이터베이스를 다루기 때문에 객체 중심으로 개발을 진행할 수 있다. 


유지보수

- SQL을 직접적으로 작성하지 않고 엔티티 필드가 되는 객체를 다뤄서 데이터베이스를 동작시키기 때문에 유지보수가 더욱 간결하다. 왜냐하면 쿼리가 수정되면 그에 따라서 그를 담을 DTO 필드도 모두 변경이 되야 하지만 JPA를 사용하게 되면 단순히 엔티티 클래스 정보만 변경하면 쉽게 관리가 가능하기 때문이다.


성능

- 일반적인 Spring의 encache 기능처럼 동일한 쿼리에 대한 캐시 기능을 사용하기 때문에 더욱 높은 성능적 효율성을 경험할 수있다. 


RDBMS 종류와 무관한 코딩

- 객체 중심으로 동작하기 때문에 Oracle, Mysql, Mssql과 같이 서로 다른 벤더사 데이터베이스를 사용하려고 할 때 문법을 바꿔줘야하는 수고를 줄일 수 있다. 


제약사항 및 단점

- JPA는 통계처리와 같이 복잡한 쿼리보다는 실시간 처리용 쿼리에 더 최적화되어 있다. 물론 JPA에서 제공하는 Native query기능을 사용할 수 있지만 통계처럼 복잡하고 미세하게 쿼리 작업이 필요하다면 Mybatis와 같은 Mapper 방식을 사용하는 것이 더 효율적일 수 있다. Spring에서 JPA와 Mybatis를 혼용해서 사용할 수 있기 때문에 필요에 따라 적절한 방식으로 선택해가면서 사용하면 될 것 같다.



댓글()
  1. 이사작전 2019.01.20 21:26 댓글주소  수정/삭제  댓글쓰기

    좋은 포스팅 감사합니다.

Spring boot maven 빌드 후 jar 파일 실행 시 Mybatis type Alias 에러 수정

web/Spring|2018. 7. 1. 21:26

Spring Boot에서 Maven으로 빌드 후 생성된 jar 파일을 java -jar wedulpos.jar로 실행시키려 하였으나, 다음과 같은 오류가 발생하였다.


에러내용

[ERROR] [SpringApplication.java:842] Application run failed

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'authProvider': Unsatisfied dependency expressed through field 'userService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'UserService': Unsatisfied dependency expressed through field 'userDao'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userMapper' defined in URL [jar:file:/wedulpos.jar!/BOOT-INF/classes!/com/wedul/wedulpos/user/dao/UserMapper.class]: Unsatisfied dependency expressed through bean property 'sqlSessionFactory'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sqlSessionFactory' defined in class path resource [com/wedul/common/config/RootApplicationContextConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.apache.ibatis.session.SqlSessionFactory]: Factory method 'sqlSessionFactory' threw exception; nested exception is org.springframework.core.NestedIOException: Failed to parse mapping resource: 'class path resource [mapper/user/UserMapper.xml]'; nested exception is org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. The XML location is 'class path resource [mapper/user/UserMapper.xml]'. Cause: org.apache.ibatis.builder.BuilderException: Error resolving class. Cause: org.apache.ibatis.type.TypeException: Could not resolve type alias 'UserDto'.  Cause: java.lang.ClassNotFoundException: Cannot find class: UserDto

at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:587)

at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:91)

at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:373)

at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1344)

at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:578)

at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:501)

at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317)

at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)

at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)

at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)

at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:760)

at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:869)

at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)

at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140)

at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759)

at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:395)

at org.springframework.boot.SpringApplication.run(SpringApplication.java:327)

at org.springframework.boot.SpringApplication.run(SpringApplication.java:1255)

at org.springframework.boot.SpringApplication.run(SpringApplication.java:1243)

at com.wedul.wedulpos.WedulposApplication.main(WedulposApplication.java:12)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.lang.reflect.Method.invoke(Method.java:498)

at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48)

at org.springframework.boot.loader.Launcher.launch(Launcher.java:87)

at org.springframework.boot.loader.Launcher.launch(Launcher.java:50)

at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:51)

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'UserService': Unsatisfied dependency expressed through field 'userDao'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userMapper' defined in URL [jar:file:/wedulpos.jar!/BOOT-INF/classes!/com/wedul/wedulpos/user/dao/UserMapper.class]: Unsatisfied dependency expressed through bean property 'sqlSessionFactory'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sqlSessionFactory' defined in class path resource [com/wedul/common/config/RootApplicationContextConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.apache.ibatis.session.SqlSessionFactory]: Factory method 'sqlSessionFactory' threw exception; nested exception is org.springframework.core.NestedIOException: Failed to parse mapping resource: 'class path resource [mapper/user/UserMapper.xml]'; nested exception is org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. The XML location is 'class path resource [mapper/user/UserMapper.xml]'. Cause: org.apache.ibatis.builder.BuilderException: Error resolving class. Cause: org.apache.ibatis.type.TypeException: Could not resolve type alias 'UserDto'.  Cause: java.lang.ClassNotFoundException: Cannot find class: UserDto

at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:587)

at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:91)

at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:373)

at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1344)

at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:578)

at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:501)

at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317)

at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)

at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)

at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)

at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:251)

at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1138)

at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1065)

at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:584)

... 27 common frames omitted

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userMapper' defined in URL [jar:file:/wedulpos.jar!/BOOT-INF/classes!/com/wedul/wedulpos/user/dao/UserMapper.class]: Unsatisfied dependency expressed through bean property 'sqlSessionFactory'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sqlSessionFactory' defined in class path resource [com/wedul/common/config/RootApplicationContextConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.apache.ibatis.session.SqlSessionFactory]: Factory method 'sqlSessionFactory' threw exception; nested exception is org.springframework.core.NestedIOException: Failed to parse mapping resource: 'class path resource [mapper/user/UserMapper.xml]'; nested exception is org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. The XML location is 'class path resource [mapper/user/UserMapper.xml]'. Cause: org.apache.ibatis.builder.BuilderException: Error resolving class. Cause: org.apache.ibatis.type.TypeException: Could not resolve type alias 'UserDto'.  Cause: java.lang.ClassNotFoundException: Cannot find class: UserDto

at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireByType(AbstractAutowireCapableBeanFactory.java:1439)

at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1326)

at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:578)

at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:501)

at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317)

at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)

at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)

at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)

at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:251)

at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1138)

at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1065)

at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:584)

... 40 common frames omitted

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sqlSessionFactory' defined in class path resource [com/wedul/common/config/RootApplicationContextConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.apache.ibatis.session.SqlSessionFactory]: Factory method 'sqlSessionFactory' threw exception; nested exception is org.springframework.core.NestedIOException: Failed to parse mapping resource: 'class path resource [mapper/user/UserMapper.xml]'; nested exception is org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. The XML location is 'class path resource [mapper/user/UserMapper.xml]'. Cause: org.apache.ibatis.builder.BuilderException: Error resolving class. Cause: org.apache.ibatis.type.TypeException: Could not resolve type alias 'UserDto'.  Cause: java.lang.ClassNotFoundException: Cannot find class: UserDto

at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:587)

at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1250)

at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1099)

at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:541)

at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:501)

at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317)

at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)

at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)

at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)

at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:251)

at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1138)

at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1065)

at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireByType(AbstractAutowireCapableBeanFactory.java:1424)

... 51 common frames omitted

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.apache.ibatis.session.SqlSessionFactory]: Factory method 'sqlSessionFactory' threw exception; nested exception is org.springframework.core.NestedIOException: Failed to parse mapping resource: 'class path resource [mapper/user/UserMapper.xml]'; nested exception is org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. The XML location is 'class path resource [mapper/user/UserMapper.xml]'. Cause: org.apache.ibatis.builder.BuilderException: Error resolving class. Cause: org.apache.ibatis.type.TypeException: Could not resolve type alias 'UserDto'.  Cause: java.lang.ClassNotFoundException: Cannot find class: UserDto

at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)

at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:579)

... 63 common frames omitted

Caused by: org.springframework.core.NestedIOException: Failed to parse mapping resource: 'class path resource [mapper/user/UserMapper.xml]'; nested exception is org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. The XML location is 'class path resource [mapper/user/UserMapper.xml]'. Cause: org.apache.ibatis.builder.BuilderException: Error resolving class. Cause: org.apache.ibatis.type.TypeException: Could not resolve type alias 'UserDto'.  Cause: java.lang.ClassNotFoundException: Cannot find class: UserDto

at org.mybatis.spring.SqlSessionFactoryBean.buildSqlSessionFactory(SqlSessionFactoryBean.java:523)

at org.mybatis.spring.SqlSessionFactoryBean.afterPropertiesSet(SqlSessionFactoryBean.java:380)

at org.mybatis.spring.SqlSessionFactoryBean.getObject(SqlSessionFactoryBean.java:547)

at com.wedul.common.config.RootApplicationContextConfig.sqlSessionFactory(RootApplicationContextConfig.java:85)

at com.wedul.common.config.RootApplicationContextConfig$$EnhancerBySpringCGLIB$$66b8a7c7.CGLIB$sqlSessionFactory$1(<generated>)

at com.wedul.common.config.RootApplicationContextConfig$$EnhancerBySpringCGLIB$$66b8a7c7$$FastClassBySpringCGLIB$$93beeadf.invoke(<generated>)

at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)

at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:361)

at com.wedul.common.config.RootApplicationContextConfig$$EnhancerBySpringCGLIB$$66b8a7c7.sqlSessionFactory(<generated>)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.lang.reflect.Method.invoke(Method.java:498)

at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)

... 64 common frames omitted

Caused by: org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. The XML location is 'class path resource [mapper/user/UserMapper.xml]'. Cause: org.apache.ibatis.builder.BuilderException: Error resolving class. Cause: org.apache.ibatis.type.TypeException: Could not resolve type alias 'UserDto'.  Cause: java.lang.ClassNotFoundException: Cannot find class: UserDto

at org.apache.ibatis.builder.xml.XMLMapperBuilder.configurationElement(XMLMapperBuilder.java:120)

at org.apache.ibatis.builder.xml.XMLMapperBuilder.parse(XMLMapperBuilder.java:92)

at org.mybatis.spring.SqlSessionFactoryBean.buildSqlSessionFactory(SqlSessionFactoryBean.java:521)

... 77 common frames omitted

Caused by: org.apache.ibatis.builder.BuilderException: Error resolving class. Cause: org.apache.ibatis.type.TypeException: Could not resolve type alias 'UserDto'.  Cause: java.lang.ClassNotFoundException: Cannot find class: UserDto

at org.apache.ibatis.builder.BaseBuilder.resolveClass(BaseBuilder.java:118)

at org.apache.ibatis.builder.xml.XMLStatementBuilder.parseStatementNode(XMLStatementBuilder.java:68)

at org.apache.ibatis.builder.xml.XMLMapperBuilder.buildStatementFromContext(XMLMapperBuilder.java:135)

at org.apache.ibatis.builder.xml.XMLMapperBuilder.buildStatementFromContext(XMLMapperBuilder.java:128)

at org.apache.ibatis.builder.xml.XMLMapperBuilder.configurationElement(XMLMapperBuilder.java:118)

... 79 common frames omitted

Caused by: org.apache.ibatis.type.TypeException: Could not resolve type alias 'UserDto'.  Cause: java.lang.ClassNotFoundException: Cannot find class: UserDto

at org.apache.ibatis.type.TypeAliasRegistry.resolveAlias(TypeAliasRegistry.java:120)

at org.apache.ibatis.builder.BaseBuilder.resolveAlias(BaseBuilder.java:149)

at org.apache.ibatis.builder.BaseBuilder.resolveClass(BaseBuilder.java:116)

... 83 common frames omitted

Caused by: java.lang.ClassNotFoundException: Cannot find class: UserDto

at org.apache.ibatis.io.ClassLoaderWrapper.classForName(ClassLoaderWrapper.java:200)

at org.apache.ibatis.io.ClassLoaderWrapper.classForName(ClassLoaderWrapper.java:89)

at org.apache.ibatis.io.Resources.classForName(Resources.java:261)

at org.apache.ibatis.type.TypeAliasRegistry.resolveAlias(TypeAliasRegistry.java:116)

… 85 common frames omitted


그래서 이를 해결하기 위해 구글링 하던 도중에 SqlSessionBean 클래스에 VFS를 등록해주지 않아서 발생한 문제라는 글을 발견하였다.

생각보다 쉽게 해결되었는데 이는 Mybatis에 버그인거 같다.


VFS는 Virtual File System이라는데 아직 잘 모르겠다. 


참고 URL

https://github.com/mybatis/spring-boot-starter/issues/38

https://github.com/mybatis/spring-boot-starter/issues/38

댓글()

Mybatis에서 쿼리 작성 시 부등호 사용하면 발생하는 Error 수정

web/Spring|2018. 6. 12. 13:36

Mybatis에서 사용하는 쿼리중에 < > 와 같은 부등호가 들어가게 되면 에러가 발생한다.


[에러내용]

"The content of elements must consist of well-formed character data or markup."


이를 해결하기 위해서는 부등호를 사용하는 쿼리는 

<![CDATA[ ]]>을 감싸주어야 한다.


1
2
3
4
5
6
7
8
9
10
<select id="test resulttype="dto">
<![CDATA[
SELECT
    jumin
FROM
    member
WHERE 
    id > 1
]]>
</select>
cs


댓글()

Mabatis #, $ 차이점

web/Spring|2018. 5. 27. 11:45

Mabatis 사용 시 

#, $의 차이점을 정확하게 정리하지 않아 헷갈렸다.

[차이점]

1. select * from member where id = #{dto.id} 
2. select * from member where id = ${dto.id} 

1번 방식(#)은 select * from member where id = ?  
다음과 같이 진행되어 바인딩 되어 실행되기 때문에 SQL Injection 대비가 가능하다. 

2번 방식($)은 ${dto.id} 영역에 데이터로 값을 치환해서 텍스트 자체로 사용한다.

댓글()

Mybatis에서 parameterType을 List로 insert하기

web/Spring|2018. 5. 27. 11:40

Mybatis에서
parameterType을 List로 지정하여 쿼리를 수행하고 싶은 경우 다음과 같이 진행한다,




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 전송 데이터 형식
List<AccountDto> accouts = new ArrayList<>();
accounts.add(new AccountDto("wedul", 123);
accounts.add(new AccountDto("cjung", 456);
 
accountDao.insertAccounts(accounts);
 
 
 
// Mybatis
<insert id="insert" parameterType="java.util.List">
INSERT INTO account(
 id,
 age
) VALUES 
<foreach item="item" index="index" collection="list">
(
 #{item.id}
 ,#{item.age}
)
</foreach>
</insert>
 
cs


댓글()

Mybatis String을 ParameterType으로 넘길때

web/Spring|2018. 5. 27. 11:30

mybatis에서 String(문자열) 하나의 값만 넘길때는  

다음과 같이 사용한다.




댓글()