JPA - 하이버네이트와 스프링 연동
Pom.xml을 통해 관련 의존성을 추가하고 root-context.xml, servlet-context.xml 파일 설정을 통해 JPA와 스프링프레임워크를 연동합니다. 이어서 컨트롤러, 서비스, 도메인, 레파지토리 단위로 나눠 스프링프레임워크 애플리케이션을 개발합니다.
pom.xml
의존성을 추가해 줍니다.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>jpabook</groupId>
<artifactId>ch11-jpa-shop</artifactId>
<version>1.0-SNAPSHOT</version>
<name>jpa-shop</name>
<packaging>war</packaging>
<properties>
<!-- 기본 설정 -->
<java.version>1.6</java.version>
<!-- 프로젝트 코드 인코딩 설정 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- 스프링 프레임워크 버전 -->
<spring-framework.version>4.1.6.RELEASE</spring-framework.version>
<!-- JPA, 하이버네이트 버전 -->
<hibernate.version>4.3.10.Final</hibernate.version>
<!-- 데이터베이스 버전 -->
<tomcat-jdbc.version>7.0.57</tomcat-jdbc.version>
<h2db.version>1.4.187</h2db.version>
<!-- JSP, WEB 버전 -->
<jsp.version>2.2</jsp.version>
<jstl.version>1.2</jstl.version>
<servlet.version>3.0.1</servlet.version>
<!-- 로그 버전 -->
<logback.version>1.1.1</logback.version>
<slf4j.version>1.7.6</slf4j.version>
<!-- 테스트 버전 -->
<junit.version>4.12</junit.version>
</properties>
<dependencies>
<!-- 스프링 MVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<!-- 스프링 ORM -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<!-- JPA, 하이버네이트 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!-- H2 데이터베이스 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>${h2db.version}</version>
<scope>runtime</scope>
</dependency>
<!-- 커넥션 풀 -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
<version>${tomcat-jdbc.version}</version>
<scope>compile</scope>
</dependency>
<!-- WEB -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>${jstl.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version>
<scope>provided</scope>
</dependency>
<!-- 로깅 SLF4J & LogBack -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
<scope>runtime</scope>
</dependency>
<!-- 테스트 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring-framework.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<path>/</path>
<uriEncoding>UTF-8</uriEncoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
xml 구성
기본적으로 STS를 이용한다면 root-context.xml와 servlet-context.xml 파일에 관한 설정이 web.xml 파일이 포함되어 있는 것을 알 수 있습니다.
- root-context.xml : 비즈니스 로직, 도메인 계층, 서비스 계층, 데이터 저장 계층을 담당하고 있습니다.
- servlet-context.xml : MVC 설정을 포함해 웹 계층에 대한 설정을 담당합니다 .
root-context.xml 구성
root-context.xml을 구성하면 다음과 같이 구성합니다.
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<context:component-scan base-package="kr.nchurch.app" />
</beans:beans>
servlet-context.xml 구성
MariaDB를 이용한 servlet-context.xml 파일의 구성은 다음과 같이 구성합니다.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<tx:annotation-driven/>
<context:component-scan base-package="JPA.examples"/>
<bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/myapp?useUnicode=true&characterEncoding=utf8" />
<property name="username" value="root"/>
<property name="password" value=""/>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- JPA 예외를 스프링 예외로 변환 -->
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan" value="JPA.examples.domain"/>
<!-- @Entity 탐색 시작 위치 -->
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.use_sql_comments">true</prop>
<prop key="hibernate.id.new_generator_mappings">true</prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
</props>
</property>
</bean>
</beans>
만약 h2 데이터베이스라면 dataSource 빈은 다음과 같이 설정합니다.
<bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource">
<property name="driverClassName" value="org.h2.Driver"/>
<property name="url" value="jdbc:h2:mem:jpashop"/>
<property name="username" value="sa"/>
<property name="password" value=""/>
</bean>
계층 분리를 고려한 개발
스프링 프레임워크 연동이 끝나면 계층 나눠 개발을 진행합니다.
- 컨트롤러
- 서비스
- 레파지토리
- 도메인
컨트롤러
package jpabook.jpashop.web;
import jpabook.jpashop.domain.Address;
import jpabook.jpashop.domain.Member;
import jpabook.jpashop.service.ItemService;
import jpabook.jpashop.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.util.List;
@Controller
public class MemberController {
@Autowired MemberService memberService;
@Autowired ItemService itemService;
@RequestMapping(value = "/members/new", method = RequestMethod.GET)
public String createForm() {
return "members/createMemberForm";
}
@RequestMapping(value = "/members/new", method = RequestMethod.POST)
public String create(Member member, String city, String street, String zipcode) {
Address address = new Address(city, street, zipcode);
member.setAddress(address);
memberService.join(member);
return "redirect:/";
}
@RequestMapping(value = "/members", method = RequestMethod.GET)
public String list(Model model) {
List<Member> members = memberService.findMembers();
model.addAttribute("members", members);
return "members/memberList";
}
}
서비스
import jpabook.jpashop.domain.Member;
import jpabook.jpashop.repository.MemberRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@Transactional
public class MemberService {
@Autowired
MemberRepository memberRepository;
/**
* 회원 가입
*/
public Long join(Member member) {
validateDuplicateMember(member); //중복 회원 검증
memberRepository.save(member);
return member.getId();
}
private void validateDuplicateMember(Member member) {
List<Member> findMembers = memberRepository.findByName(member.getName());
if (!findMembers.isEmpty()) {
throw new IllegalStateException("이미 존재하는 회원입니다.");
}
}
/**
* 전체 회원 조회
*/
public List<Member> findMembers() {
return memberRepository.findAll();
}
public Member findOne(Long memberId) {
return memberRepository.findOne(memberId);
}
}
레파지토리
import org.springframework.stereotype.Repository;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.List;
@Repository
public class MemberRepository {
@PersistenceContext
EntityManager em;
public void save(Member member) {
em.persist(member);
}
public Member findOne(Long id) {
return em.find(Member.class, id);
}
public List<Member> findAll() {
return em.createQuery("select m from Member m", Member.class)
.getResultList();
}
public List<Member> findByName(String name) {
return em.createQuery("select m from Member m where m.name = :name", Member.class)
.setParameter("name", name)
.getResultList();
}
}
도메인
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
private String name;
@Embedded
private Address address;
@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<Order>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public List<Order> getOrders() {
return orders;
}
public void setOrders(List<Order> orders) {
this.orders = orders;
}
@Override
public String toString() {
return "Member{" +
"id=" + id +
", name='" + name + '\'' +
", address=" + address +
'}';
}
}
참고
출처: https://happygrammer.tistory.com/150?category=891896 [happygrammer]
'Java 관련 > JPA' 카테고리의 다른 글
JPA - 스프링 데이터 JPA에서 쿼리 메소드 안에 지원되는 키워드 (0) | 2021.11.17 |
---|---|
QueryDsl 환경설정 (0) | 2021.11.17 |
JPA - 스프링 데이터 JPA (0) | 2021.11.17 |
JPA - JPQL과 Criteria 쿼리 (0) | 2021.11.17 |
JPA - 엔티티 매핑 (0) | 2021.11.17 |
JPA - 엔티티 매니저와 트랜잭션 (0) | 2021.11.17 |
JPA에 기반한 비즈니스로직 중심의 S/W 개발 (0) | 2021.11.17 |
JPA 요소 (0) | 2021.10.05 |