일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- github token
- jupyter
- database
- vscode
- 오류
- MySQL
- github clone
- 깃 토큰
- visualstudio code
- cmd
- 단축키
- console창
- run sql script
- csv
- Visual Studio Code
- 따옴표 삭제
- error
- 파이썬
- localhost
- 데이터베이스
- php
- OrCAD 다운로드
- 클론
- Python
- clone
- error 해결
- DataGrip
- PHPStorm
- 에러
- import data
- Today
- Total
개발 노트
10/27 Lombok 설치, gradle과 maven 비교, JPA 개념, JPA CRUD, oBootJpa01(JPA코드로 DB에 저장하는 프로그램), iBatis와 Hibernate비교 본문
10/27 Lombok 설치, gradle과 maven 비교, JPA 개념, JPA CRUD, oBootJpa01(JPA코드로 DB에 저장하는 프로그램), iBatis와 Hibernate비교
hayoung.dev 2022. 11. 15. 16:07repository > JdbcMemberRepository.java (class, MemberRepository 상속)
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 사용
/* DataSource를 bean작업으로 해줘야 하기 때문에 root에 SpringConfig.java를 생성한다. */
private final DataSource dataSource;
public JdbcMemberRepository(DataSource dataSource) {
this.dataSource = dataSource;
}
/* Connection은 외부에서 못쓰게 하기 위해 private로 한다. */
private Connection getConnection() {
/* connection을 DataSourceUtils로 사용하였기 때문에 DataSourceUtils를 닫아야 한다. */
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 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);
}
}
/* close는 두 가지 용도로 쓰인다. 메소드 오버로딩. */
/* DataSourceUtils를 닫는다. */
private void close(Connection conn) throws SQLException {
/* (dataSource)이 데이터소스를 가지고 (conn)연결을 해제(release)시킨다.*/
DataSourceUtils.releaseConnection(conn, dataSource);
}
/* close 모듈화한 코드 */
private void close(Connection conn, PreparedStatement pstmt, ResultSet rs) {
try {
if (rs != null)
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
if (pstmt != null)
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (conn != null)
close(conn);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
build.gradle의 dependencies에 하단 코드 추가 후 build gradle 우클릭 > gradle > refresh gradle project
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
(member1 테이블 형태만 있는 상태, 시퀸스 member_seq를 생성한 상태에서 진행)
localhost:서버번호/
를 했을 때
root(java > oBootDBConnect > SpringConfig.java) 가 실행된다.
출력 결과
회원 가입에 회원1, 회원2를 등록 후 회원 목록을 확인하면 하단과 같이 뜬다.
scott의 member1 테이블에도 저장이 되어있다.
이렇게 설정하면 세션을 종료하여도 없어지지 않는다.
[lombok 설치]
spring 폴더에 lombok.jar 파일을 저장한다.
cmd 창에서 lombok.jar를 저장한 경로로 이동하여 하단을 입력한다.
java -jar lombok.jar
STS.exe 경로가 설치되어있는 경로로 설정한다.
eclipse는 알아서 설정된다.
설치 성공
[JPA 개념]
JPA 동작
JPA 표준명세와 특징
JPA CRUD
JPA Performance
JPA Transation 지연.
커밋하는 순간 데이터베이스에 insert sql을 모아 보낸다.
JPA 구성요소
JPA Entity 생명주기
[oBootJpa01]
Oracle에서 scottJpa 계정을 만들어준다.
CREATE USER scottJpa IDENTIFIED BY tiger;
GRANT DBA TO scottJpa;
java > controller, service, repository, domain (package) 생성
application.properties
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
# none
spring.jpa.hibernate.ddl-auto=create
controller > MemberController.java
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 static final Logger logger = LoggerFactory.getLogger(MemberController.class);
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
/* 조회할 때 @GetMapping 사용 */
@GetMapping(value="/members/new")
public String createForm() {
System.out.println("MemberController /members/new start.. ");
return "members/createMemberForm";
}
/*수정, 삭제, 저장할 때 사용 @PostMapping 사용*/
@PostMapping(value = "members/save")
public String memberSave(Member member) {
System.out.println("MemberController memberSave 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";
}
}
service > MemberService.java
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 /* JPA는 @Transactional을 꼭 써주어야 한다. */
public class MemberService {
private final MemberRepository memberRepository;
@Autowired /* DI 쓰는 경우 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("MemberService memberSave After..");
/* 서비스에서 member.getId()이 리턴되는 순간 커밋된다. */
/* 그래서 트렌젝션을 같은 트렌젝션에 거는 것이 편하므로 트렌젝션을 서비스에 건다. */
return member.getId();
}
public List<Member> getListAllMember() {
List<Member> listMember = memberRepository.findAllMember();
System.out.println("MemberService getListAllMember listMember.size()->"+listMember.size());
return listMember;
}
}
repository > MemberRepository.java
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();
}
domain > Member.java
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")
//Member 이 객체에 대한 테이블이 member1이라는 뜻이다.
/* 객체는 Member가 되지만 table 이름은 member1로 동시에 두 개가 된다. */
public class Member {
@Id /* primary key 지정. JPA에서는 pk를 반드시 지정해주어야 한다. */
private Long id;
private String name;
}
build.gradle
plugins {
id 'org.springframework.boot' version '2.7.5'
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
id 'java'
}
group = 'com.oracle'
version = 'version1.0'
sourceCompatibility = '11'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
runtimeOnly 'com.oracle.database.jdbc:ojdbc8'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
useJUnitPlatform()
}
상단 코딩 후 build.gradle > gradle > refresh gradle project
repository > JpaMemberRepository.java (인터페이스, MemberRepository 상속)
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 /*DAO*/
public class JpaMemberRepository implements MemberRepository {
// JPA DML --> EntityManager 필수
private final EntityManager em;
public JpaMemberRepository(EntityManager em) {
this.em = em;
}
@Override
public Member memberSave(Member member) {
// 저장 method
// 하단 코드를 쓰면 DB에 저장이 된다.
em.persist(member);
System.out.println("JpaMemberRepository memberSave member After..");
return member;
}
@Override
public List<Member> findAllMember() {
/* 하단의 sql문에서 Member는 table명이 아니라 객체 entity이다. 주의해야 함. */
/* list로 가져오고 싶을 때 .getResultList(); 을 사용하면 List<Member> 타입으로 memberList에 넣어준다. */
/* sql문이 아닌 jpql문이다. */
List<Member> memberList = em.createQuery("select m from Member m", Member.class )
.getResultList();
System.out.println("JpaMemberRepository findAllMember memberList.size()->"+memberList.size());
return memberList;
}
}
resource > static > index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<a href="/members/new">Member 신규 생성</a><p>
<a href="/members">Member List 조회</a>
</body>
</html>
repository > members(package) > createMemberForm.html
<!DOCTYPE html>
<html xmlns: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> <!-- /container -->
</body>
</html>
repository > members(package) > memberList.html
<!DOCTYPE html>
<html xmlns: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> <!-- /container -->
</body>
</html>
실행 순서
1. Member 신규 생성 : /members/new
1) MemberController.java에 의해 members/createMemberForm(html)로 이동한다.
2) submit로 등록하면 id와 name이 post 방식으로 들어가 /members/save(controller)로 이동한다.
3) 컨트롤러의 코드에 따라
memberService.memberSave(member); 를 실행하고
그 전페이지를 리턴한다.
(1) 여기에서 .memberSave는 서비스에 있다.
memberRepository.memberSave(member); 를 실행하고
member.getID를 리턴한다.
(1-1) 여기에서 .memberSave는 repository에 있다.
em.persist(member);를 실행하고 member를 리턴한다.
persist는 저장 메소드이다. 즉 저장을 하고 그 member를 리턴한다.
그러면 다시 (1)로 돌아와 member.getID를 리턴하게 되고
다시 그 앞으로 돌아와 그 전페이지를 리턴한다.
2. Member List 조회 : /members
1) 컨트롤러의 /members코드에 따라
List<Member> memberList = memberService.getListAllMember();
model.addAttribute("members", memberList);
return "members/memberList";
코드를 실행하게 된다.
2) getListAllMember는 service 코드에 따라
List<Member> listMember = memberRepository.findAllMember();
return listMember;
코드를 실행하게 된다.
3) findAllMember는 repository 코드에 따라
List<Member> memberList = em.createQuery("select m from Member m", Member.class ).getResultList();
return memberList;
코드를 실행하게 된다.
select한 코드를 list로 가져와 memberList에 저장하고 memberList를 리턴한다.
다시 2)로 돌아가서 memberList를 listMember에 넣고 리턴한다.
다시 1)로 돌아가서 리턴값을 memberList에 넣고 members/memberList html을 리턴한다.
4) members/memberList.html에 따라 값이 출력된다.
scottJpa 계정의 member1 테이블 모습
참고로 console 창에 나오는
을 실행해도 table select이 실행이 되어 결과가 똑같이 나온다.
하단 코드는 @Transactional이 끝날 때 실행된다.
트렌젝션의 원칙(원자성, 일관성, 독립성, 지속성) 때문에 commit이나 rollback이 한 단위로 돼야 한다.
application.properties 코드에서 11줄의 create는 코딩 단계에서만 해준다. 잘못 건드려서 데이터가 날라갈 수 있기 때문이다. 코딩이 다 되었으면 none으로 설정해주어야 한다.
(중요 : 표시한것 기억하기)
iBatis는 SQL Mapping(Partial ORM), Hibernate는 OR Mapping(Full ORM)
'프로젝트 기반 JAVA 응용 SW개발 : 22.07.19~23.01.20 > Spring' 카테고리의 다른 글
11/8 oBootMybatis01 - 1 (0) | 2022.11.23 |
---|---|
10/28 JPA 연관관계, oBootJpa02 프로젝트 (0) | 2022.11.16 |
10/26 spring boot 예시 2개(메모리 사용하여 데이터 저장) (0) | 2022.11.10 |
10/25 pom.xml 코드, web.xml 한글처리, security 작업, 암호화 처리, Spring Boot 설치, 페이지 연결, 파라미터 받기, @Controller, @RequestMapping, @ResponseBody, @GetMapping (0) | 2022.11.03 |
10/24 spring MVC (0) | 2022.11.01 |