👇 수정사항
package com.oracle.oBootDBConnect.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import com.oracle.oBootDBConnect.domain.Member1;
import com.oracle.oBootDBConnect.service.MemberService;
@Controller
public class MemberController {
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
@GetMapping(value = "/members/memberForm")
public String createMemberForm() {
System.out.println("MemberController /members/memberForm start...");
return "members/createMemberForm";
}
@PostMapping(value = "/members/newSave")
public String memberSave(Member1 member1) {
System.out.println("MemberController memberSave start...");
memberService.memberSave(member1);
return "redirect:/";
}
// 전체 조회 화면
@GetMapping(value = "/members/memberList")
public String memberLists(Model model) {
List<Member1> memberList = memberService.findMembers();
model.addAttribute("members", memberList);
return "members/memberList";
}
}
<!DOCTYPE html>
<html xml:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>회원등록</h1>
<div class="container">
<form action="/members/newSave" method="post">
<div class="form-group">
<label for="name">이름</label>
<input type="text" id="name" name="name" placeholder="이름을 입력하세요">
</div>
<button type="submit">등록</button>
</form>
</div>
</body>
</html>
final -> 고정 / 바꾸지 않는다
DB - Member1 table 사용
package com.oracle.oBootDBConnect.repository;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.stereotype.Repository;
import com.oracle.oBootDBConnect.domain.Member1;
@Repository
public class JdbcMemberRepository implements MemberRepository {
// JDBC 사용
private final DataSource dataSource;
public JdbcMemberRepository(DataSource dataSource) {
this.dataSource = dataSource;
}
private Connection getConnection() {
return DataSourceUtils.getConnection(dataSource);
}
@Override
public Member1 save(Member1 member1) {
String sql = "insert into member1(id, name) values(member_seq.nextval,?)";
System.out.println("JdbcMemberRepository save sql->"+sql);
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, member1.getName());
pstmt.executeUpdate();
System.out.println("JdbcMemberRepository pstmt.executeUpdate After");
return member1;
} catch (Exception e) {
throw new IllegalStateException(e);
} finally {
close(conn, pstmt, rs);
}
}
@Override
public List<Member1> findAll() {
// TODO Auto-generated method stub
return null;
}
private void close(Connection conn) throws SQLException {
DataSourceUtils.releaseConnection(conn, dataSource);
}
private void close(Connection conn, PreparedStatement pstmt, ResultSet rs) {
try {
if( rs != null ) rs.close();
} catch (SQLException e) {
System.out.println(e.getMessage());
}
try {
if( pstmt != null ) pstmt.close();
} catch (SQLException e) {
System.out.println(e.getMessage());
}
try {
if( conn != null ) close(conn);
} catch (SQLException e) {
System.out.println(e.getMessage());
}
}
}
❌오류발생
MemoryMemberRepository -> @Repository 삭제
- String sql = "insert into member1(id, name) values(member_seq.nextval,?)"; 테이블 명 오류
- member_seq를 만들지 않아서 오류
환경작업
package com.oracle.oBootDBConnect;
import javax.sql.DataSource;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfig {
private final DataSource dataSource;
public SpringConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
}
package com.oracle.oBootDBConnect.repository;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.stereotype.Repository;
import com.oracle.oBootDBConnect.domain.Member1;
@Repository
public class JdbcMemberRepository implements MemberRepository {
// JDBC 사용
private final DataSource dataSource;
public JdbcMemberRepository(DataSource dataSource) {
this.dataSource = dataSource;
}
private Connection getConnection() {
return DataSourceUtils.getConnection(dataSource);
}
// 회원가입
@Override
public Member1 save(Member1 member1) {
String sql = "insert into member1(id, name) values(member_seq.nextval,?)";
System.out.println("JdbcMemberRepository save sql->"+sql);
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, member1.getName());
pstmt.executeUpdate();
System.out.println("JdbcMemberRepository pstmt.executeUpdate After");
return member1;
} catch (Exception e) {
throw new IllegalStateException(e);
} finally {
close(conn, pstmt, rs);
}
}
// 회원 목록 조회
@Override
public List<Member1> findAll() {
String sql = "select * from member1";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
List<Member1> members = new ArrayList<>();
while(rs.next()) {
Member1 member = new Member1();
member.setId(rs.getLong("id"));
member.setName(rs.getString("name"));
members.add(member);
}
return members;
} catch (Exception e) {
throw new IllegalStateException(e);
} finally {
close(conn, pstmt, rs);
}
}
private void close(Connection conn) throws SQLException {
DataSourceUtils.releaseConnection(conn, dataSource);
}
// close 오류에 대한 메소드
private void close(Connection conn, PreparedStatement pstmt, ResultSet rs) {
try {
if( rs != null ) rs.close();
} catch (SQLException e) {
System.out.println(e.getMessage());
}
try {
if( pstmt != null ) pstmt.close();
} catch (SQLException e) {
System.out.println(e.getMessage());
}
try {
if( conn != null ) close(conn);
} catch (SQLException e) {
System.out.println(e.getMessage());
}
}
}
MemoryMemberRepository, JdbcMemberRepository의 @Repository 선언을 삭제했을 때
- DB 연결 -> return new JdbcMemberRepository(dataSource);
- Memory 연결-> return new MemoryMemberRepository();
package com.oracle.oBootDBConnect;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.oracle.oBootDBConnect.repository.JdbcMemberRepository;
import com.oracle.oBootDBConnect.repository.MemberRepository;
import com.oracle.oBootDBConnect.repository.MemoryMemberRepository;
@Configuration
public class SpringConfig {
private final DataSource dataSource;
public SpringConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
@Bean
MemberRepository memberRepository() {
// return new JdbcMemberRepository(dataSource);
return new MemoryMemberRepository();
}
}
❗ 스프링 빈을 등록하는 2가지 방법
1. 컴포넌트 스캔과 자동 의존관계 설정
2. 자바 코드로 직접 스프링 빈 등록하기
@Repository를 선언하지 않고 SpringConfig에서 @Bean 선언하여 환경작업을 하면 굳이 따로 선언안해도 configuration에서 검증을 해준다.
Lombok이란 어노테이션 기반으로 코드를 자동완성 해주는 라이브러리이다. 롬북참고
❗모든 팀원이 lombok 설치 해야 함
lombok.jar를 스프링 폴더에 넣는다
sts 종료
cmd
-> cd C:\spring
java -jar lombok.jar
롬북 설치
-> specify location
sts.exe(select)
install/update
( Java Persistence API )
내부적으로 JDBC API를 사용
SQL별로 표준을 지키지 않은 데이터베이스만의 고유한 기능, 데이터베이스 방언
lombok 설정 추가
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
username -> scottJpa 변경
server.port=8383
# Oracle Connect
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@localhost:1521/xe
spring.datasource.username=scottjpa
spring.datasource.password=tiger
# Jpa Setting
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=create
테이블을 바꾸지 않는다면 create 후 none으로 변경
package com.oracle.oBootJpa01.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import com.oracle.oBootJpa01.domain.Member;
import com.oracle.oBootJpa01.service.MemberService;
@Controller
public class MemberController {
private final MemberService memberService;
private static final Logger logger = LoggerFactory.getLogger(MemberController.class);
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
@GetMapping(value = "/members/new")
public String createFrom() {
System.out.println("MemberController /members/new start...");
return "/members/createMemberForm";
}
@PostMapping(value = "/members/save")
public String memberSave(Member member) {
System.out.println("MemberController /members/save start...");
System.out.println("member.getId()->"+member.getId());
System.out.println("member.getName()->"+member.getName());
memberService.memberSave(member);
System.out.println("MemberController memberSave After...");
return "redirect:/";
}
}
@Entity, lombok-getter, setter 추가
@Id -> Id를 primary-key로 지정
package com.oracle.oBootJpa01.domain;
import javax.persistence.Entity;
import javax.persistence.Id;
import lombok.Getter;
import lombok.Setter;
@Entity
@Getter
@Setter
// @Data -> 객체를 보고 싶을 때 2
public class Member {
@Id
private Long id;
private String name;
// toString -> 객체를 보고 싶을 때 1
@Override
public String toString() {
String returnStr = "";
returnStr = "[id : "+this.id + " 이름:" + this.name+"]";
return returnStr;
}
}
package com.oracle.oBootJpa01.repository;
import com.oracle.oBootJpa01.domain.Member;
public interface MemberRepository {
Member memberSave(Member member);
}
package com.oracle.oBootJpa01.repository;
import javax.persistence.EntityManager;
import com.oracle.oBootJpa01.domain.Member;
@Repository
public class JpaMemberRepository implements MemberRepository {
// DML 작업을 하기 위해서 필수
private final EntityManager em;
public JpaMemberRepository(EntityManager em) {
this.em = em;
}
@Override
public Member memberSave(Member member) {
// 저장 method
em.persist(member);
System.out.println("JpaMemberRepository memberSave member After...");
return member;
}
}
@Transactional 추가
package com.oracle.oBootJpa01.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.oracle.oBootJpa01.domain.Member;
import com.oracle.oBootJpa01.repository.MemberRepository;
@Service
@Transactional
public class MemberService {
private final MemberRepository memberRepository;
@Autowired
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
// 회원가입
public Long memberSave(Member member) {
System.out.println("MemberService memberSave member->"+member);
memberRepository.memberSave(member);
System.out.println("memberRepository memberSave After");
return member.getId();
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<a href="/members/new">Member 신규 생성</a>
<a href="/members">Member List 조회</a>
</body>
</html>
<!DOCTYPE html>
<html xml:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>JPA 회원등록</h1>
<div class="container">
<form action="/members/save" method="post">
ID : <input type="text" id="id" name="id" required="required"><p>
이름 : <input type="text" id="name" name="name" placeholder="이름을 입력하세요">
<button type="submit">등록</button>
</form>
</div>
</body>
</html>
알아서 테이블까지 생성한다.
👉결과화면
관계형 데이터베이스에서 실행되는 여러개의 sql 명령문을 하나의 논리적 작업 단위로 처리하는 개념
(동시에 커밋하거나 동시에 롤백을 하는 단위)
엔티티와 매팅
spring 어노테이션 정리
-> 클래스 이름과 테이블 이름이 다를 경우 JPA에게 테이블 이름을 알려주는 것
- 엔티티와 매핑할 테이블을 지정
- 생략 시 매핑한 엔티티를 테이블 이름으로 사용
table을 member1로 지정했기 때문에 알아서 member1 테이블을 찾는다.
만약 table name을 지정하지 않는다면 클래스 member를 찾아 테이블을 생성한다.
package com.oracle.oBootJpa01.domain;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.Getter;
import lombok.Setter;
@Entity
@Getter
@Setter
@Table(name = "member1")
public class Member {
@Id
private Long id;
private String name;
// 객체를 보고 싶을 때?
@Override
public String toString() {
String returnStr = "";
returnStr = "[id : "+this.id + " 이름:" + this.name+"]";
return returnStr;
}
}
package com.oracle.oBootJpa01.controller;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import com.oracle.oBootJpa01.domain.Member;
import com.oracle.oBootJpa01.service.MemberService;
@Controller
public class MemberController {
private final MemberService memberService;
private static final Logger logger = LoggerFactory.getLogger(MemberController.class);
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
@GetMapping(value = "/members/new")
public String createFrom() {
System.out.println("MemberController /members/new start...");
return "/members/createMemberForm";
}
@PostMapping(value = "/members/save")
public String memberSave(Member member) {
System.out.println("MemberController /members/save start...");
System.out.println("member.getId()->"+member.getId());
System.out.println("member.getName()->"+member.getName());
memberService.memberSave(member);
System.out.println("MemberController memberSave After...");
return "redirect:/";
}
@GetMapping(value = "/members")
public String listMember(Model model) {
List<Member> memberList = memberService.getListAllMember();
logger.info("memberList.size() ->"+memberList.size());
model.addAttribute("members", memberList);
return "members/memberList";
}
}
package com.oracle.oBootJpa01.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.oracle.oBootJpa01.domain.Member;
import com.oracle.oBootJpa01.repository.MemberRepository;
@Service
@Transactional
public class MemberService {
private final MemberRepository memberRepository;
@Autowired
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
// 회원가입
public Long memberSave(Member member) {
System.out.println("MemberService memberSave member->"+member);
memberRepository.memberSave(member);
System.out.println("memberRepository memberSave After");
return member.getId();
}
public List<Member> getListAllMember() {
List<Member> listMember = memberRepository.findAllMember();
System.out.println("MemberService getListAllMember() listMember.size() ->"+listMember.size());
return listMember;
}
}
package com.oracle.oBootJpa01.repository;
import java.util.List;
import com.oracle.oBootJpa01.domain.Member;
public interface MemberRepository {
Member memberSave(Member member);
List<Member> findAllMember();
}
em.createQuery("select m from Member m", Member.class)
-> 여기서 Member는 객체다 / 테이블이 아니다
package com.oracle.oBootJpa01.repository;
import java.util.List;
import javax.persistence.EntityManager;
import org.springframework.stereotype.Repository;
import com.oracle.oBootJpa01.domain.Member;
@Repository
public class JpaMemberRepository implements MemberRepository {
// DML 작업을 하기 위해서 필수
private final EntityManager em;
public JpaMemberRepository(EntityManager em) {
this.em = em;
}
@Override
public Member memberSave(Member member) {
// 저장 method
em.persist(member);
System.out.println("JpaMemberRepository memberSave member After...");
return member;
}
@Override
public List<Member> findAllMember() {
List<Member> memberList = em.createQuery("select m from member m", Member.class)
.getResultList();
System.out.println("JpaMemberRepository findAllMember memberList.size()->"+memberList.size());
return memberList;
}
}
<!DOCTYPE html>
<html xml:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<div class="container">
<div>
<table border="1">
<thead>
<tr>
<th>No</th>
<th>이름</th>
</tr>
</thead>
<tbody>
<tr th:each="member : ${members}">
<td th:text="${member.id}"></td>
<td th:text="${member.name}"></td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>
Isolation Level
예를 들어 은행 업무를 한다고 생각해보자.
입금이 되는 순간 출금이 되는 순간이 있을 것이다.
여러가지 작업을 동시에 커밋해야 하는데 그렇기 때문에 service에서 dao결과를 가져와 동시에 커밋을 해준다.
servcie에서 return 하는 순간 commit 된다.
따라서 동시에 커밋하고 롤백하는 과정(DML)은 service에서 담당해야하기 때문에 service에서 transaction 시킨다.