JAVA/spring

[1028 from 백기선 스프링 JPA] 릴레이션 vs JPA, postgres using docker

JDBC 관계형 데이터 베이스와 자바의 연결고리  Java database connectivity
- DataSource/DriverManager
- Connection
- PreparedStatement
SQL DDL, DML
ORM Object–relational mapping  : 객체 관계 맵핑 
- 도메인 모델 사용. 
    > 객체지향 프로그램 장점 활용, 디자인패턴, 코드재사용, 비즈니스 로직 구현 테스트 편함. 

애플리케이션의 클래스와 SQL 데이터 베이스의 테이블 사이의 맵핑 정보를 기술한 메타데이터를 사용하며, 자바 애플리케이션의 객체를 SQL 데이터베이스의 테이블에 자동으로 영속화 해주는 기술. 
- Java Persistence with Hibernate, Second Edition
장점 : 생산성, 유지보수성, 성능, 밴더 독립성
단점 : 학습비용
릴레이션/애트리뷰트
튜플/카디날리티
도메인
테이블 / 속성(ex_사원번호, 이름, 전화번호, 부서, 최소 차수는 1)
레코드 / 튜플갯수, 0일수있다. 
릴레이션에 포함된 각각 속성들이 가질수 있는 값들의 집합.  (ex_성별의 의 도메인 남, 여)

ORM 패러다임 불일치.

문제 객체 릴레이션
밀도 다양한 크기 객체 만듬, 커스텀 타입 만들기 쉬움 테이블 기본 데이터 타입
UDT(Uniform Data Type) CPU 실행 않고 PG 저장 비추
서브타입 상속구조 만들기 쉽다, 다형성 상속관계없음, 표준기술 아님, 다형적관계표현안됨
식별성 문제 레퍼러스 동일성(==)
인스턴스 동일성(equals())
주키(Primary key)
관계 객체 레퍼런스로 관계표현, 근복적으로 방향존재
다대다 가능
public class Study {
    User  user;
}
public class User{
    List<Study> studyList;
}
외래키(Foreign key)
방향 의미 없음, Join, 다대다 불가, 
데이터
네비게이션
레퍼런스를 이용해서 다른 객체로 이동 가능
콜렉션을 순회 활 수 도 있음
getOwner().getStudyList.stream().forEAch(s->s.getOwner());
비효율적. 
데이터 요청 적게 할수록 성능좋다. Join사용
많이 한번에 가져오는 것도 성능 많이씀. 
그렇다고 lazy loading을 하자니 그것도 문제다. (n+1 select)

 

JPA 사용이유

1. 도메인 주도 개발... 비지니스 로직 구현 집중 가능. 소프트웨어 복잡도 최소화 

도메인 : 사용자가 사용하는것.   영어사전: 1. 영역, 2. 소유지. 

+ 단건조회의 경우 jpa가 느릴수 있으나 객체와 데이터 베이스 사이 캐시사용으로 트랜젝션안 불필요한 쿼리를 날리지 않음. 

도메인 주도개발  참고 : 도메인 주도 설계(Domain-Driven Design) in Real Project — 도메인 

 

PostgreSQL 설치 및 서버 실행 (docker)

더보기

잘못만들어 삭제시

C:\Users\miseong>docker container ps
CONTAINER ID   IMAGE      COMMAND                  CREATED          STATUS          PORTS                    NAMES
6fe0fabf1380   postgres   "docker-entrypoint.s…"   22 minutes ago   Up 22 minutes   0.0.0.0:5432->5432/tcp   postgres_boot

C:\Users\miseong>docker container stop 6fe0fabf1380
6fe0fabf1380

C:\Users\miseong>docker container rm 6fe0fabf1380
6fe0fabf1380

C:\Users\miseong>docker images ps
REPOSITORY   TAG       IMAGE ID   CREATED   SIZE

C:\Users\miseong>docker images ps -a
REPOSITORY   TAG       IMAGE ID   CREATED   SIZE

 

 

--------
C:\Users\miseong>docker imgaes
docker: 'imgaes' is not a docker command.
See 'docker --help'

C:\Users\miseong>docker images ps
REPOSITORY   TAG       IMAGE ID   CREATED   SIZE

docker run -p 5432:5432 -e POSTGRES_PASSWORD=pass -e POSTGRES_USER=keesun -e POSTGRES_DB=springdata --name postgres_boot -d postgres

docker run -p 5432:5432 -e POSTGRES_PASSWORD=1234 -e POSTGRES_USER=day -e POSTGRES_DB=diarydata --name postgres_diary -d postgres


C:\Users\miseong>docker images
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
postgres     latest    26c8bcd8b719   2 weeks ago   314MB

C:\Users\miseong>docker ps -a
CONTAINER ID   IMAGE      COMMAND                  CREATED              STATUS              PORTS                    NAMES
6fe0fabf1380   postgres   "docker-entrypoint.s…"   About a minute ago   Up About a minute   0.0.0.0:5432->5432/tcp   postgres_boot


C:\Users\miseong>docker start postgres_diary
postgres_diary

 

C:\Users\miseong>docker exec -i -t postgres_diary bash
root@b1de21ce08f0:/# su - postgres
postgres@b1de21ce08f0:~$ psql --username day --dbname diarydata
psql (13.2 (Debian 13.2-1.pgdg100+1))
Type "help" for help.

diarydata=# CREATE TABLE ACCOUNT(id int, username varchar(255), password varchar(255));
CREATE TABLE
diarydata=# INSERT INTO ACCOUNT VALUES(1,'day', 'pass');
INSERT 0 1
diarydata=# SELECT * FROM ACCOuNT;
 id | username | password
----+----------+----------
  1 | day      | pass
(1 row)

데이터베이스 조회

diarydata=# list
diarydata-# ;
ERROR:  syntax error at or near "list"
LINE 1: list
        ^
diarydata=# \list
                             List of databases
   Name    | Owner | Encoding |  Collate   |   Ctype    | Access privileges
-----------+-------+----------+------------+------------+-------------------
 diarydata | day   | UTF8     | en_US.utf8 | en_US.utf8 |
 postgres  | day   | UTF8     | en_US.utf8 | en_US.utf8 |
 template0 | day   | UTF8     | en_US.utf8 | en_US.utf8 | =c/day           +
           |       |          |            |            | day=CTc/day
 template1 | day   | UTF8     | en_US.utf8 | en_US.utf8 | =c/day           +
           |       |          |            |            | day=CTc/day
(4 rows)

diarydata=#

 

테이블 조회

diarydata=# \dt
        List of relations
 Schema |  Name   | Type  | Owner
--------+---------+-------+-------
 public | account | table | day
(1 row)

잘못만들어 삭제시

C:\Users\miseong>docker container ps
CONTAINER ID   IMAGE      COMMAND                  CREATED          STATUS          PORTS                    NAMES
6fe0fabf1380   postgres   "docker-entrypoint.s…"   22 minutes ago   Up 22 minutes   0.0.0.0:5432->5432/tcp   postgres_boot

C:\Users\miseong>docker container stop 6fe0fabf1380
6fe0fabf1380

C:\Users\miseong>docker container rm 6fe0fabf1380
6fe0fabf1380

C:\Users\miseong>docker images ps
REPOSITORY   TAG       IMAGE ID   CREATED   SIZE

C:\Users\miseong>docker images ps -a
REPOSITORY   TAG       IMAGE ID   CREATED   SIZE

 

 

--------
C:\Users\miseong>docker imgaes
docker: 'imgaes' is not a docker command.
See 'docker --help'

C:\Users\miseong>docker images ps
REPOSITORY   TAG       IMAGE ID   CREATED   SIZE

docker run -p 5432:5432 -e POSTGRES_PASSWORD=pass -e POSTGRES_USER=keesun -e POSTGRES_DB=springdata --name postgres_boot -d postgres

docker run -p 5432:5432 -e POSTGRES_PASSWORD=1234 -e POSTGRES_USER=day -e POSTGRES_DB=diarydata --name postgres_diary -d postgres


C:\Users\miseong>docker images
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
postgres     latest    26c8bcd8b719   2 weeks ago   314MB

C:\Users\miseong>docker ps -a
CONTAINER ID   IMAGE      COMMAND                  CREATED              STATUS              PORTS                    NAMES
6fe0fabf1380   postgres   "docker-entrypoint.s…"   About a minute ago   Up About a minute   0.0.0.0:5432->5432/tcp   postgres_boot


C:\Users\miseong>docker start postgres_diary
postgres_diary

 

C:\Users\miseong>docker exec -i -t postgres_diary bash
root@b1de21ce08f0:/# su - postgres
postgres@b1de21ce08f0:~$ psql --username day --dbname diarydata
psql (13.2 (Debian 13.2-1.pgdg100+1))
Type "help" for help.

 

diarydata=# CREATE TABLE ACCOUNT(id int, username varchar(255), password varchar(255));
CREATE TABLE
diarydata=# INSERT INTO ACCOUNT VALUES(1,'day', 'pass');
INSERT 0 1
diarydata=# SELECT * FROM ACCOuNT;
 id | username | password
----+----------+----------
  1 | day      | pass
(1 row)

 

데이터베이스 조회

diarydata=# list
diarydata-# ;
ERROR:  syntax error at or near "list"
LINE 1: list
        ^
diarydata=# \list
                             List of databases
   Name    | Owner | Encoding |  Collate   |   Ctype    | Access privileges
-----------+-------+----------+------------+------------+-------------------
 diarydata | day   | UTF8     | en_US.utf8 | en_US.utf8 |
 postgres  | day   | UTF8     | en_US.utf8 | en_US.utf8 |
 template0 | day   | UTF8     | en_US.utf8 | en_US.utf8 | =c/day           +
           |       |          |            |            | day=CTc/day
 template1 | day   | UTF8     | en_US.utf8 | en_US.utf8 | =c/day           +
           |       |          |            |            | day=CTc/day
(4 rows)

diarydata=#

 

테이블 조회

diarydata=# \dt
        List of relations
 Schema |  Name   | Type  | Owner
--------+---------+-------+-------
 public | account | table | day
(1 row)

 

 

 

 

 

jdbc 예제

더보기
package com.today10sec.diary.basic;

import java.sql.*;

public class DBConnectTest {

    public static void main(String[] args) throws SQLException {
        String url = "jdbc:mariadb://localhost:3306/today10secDB";
        String userName = "today";
        String pw = "1234";

        //try with resource java 7  //자동 자원정리 메소드
        try(Connection connection = DriverManager.getConnection(url, userName,pw)){
            //System.out.println("Connection Created"+ connection);
            //String sql = " CREATE TABLE ACCOUNT(id int, username varchar(255), password varchar(255))";
            //String sql = " INSERT INTO ACCOUNT VALUES(1,'day', 'pass')";
            String sql =" SELECT * FROM ACCOuNT";
            ResultSet rs ;
            try(PreparedStatement statement = connection.prepareStatement(sql)){
                rs = statement.executeQuery();
                //int result = statement.executeUpdate();
                //System.out.println(result);
                while (rs.next()){
                    System.out.println(rs.getInt("id")+rs.getString("username")+rs.getString("password"));
                }
            }

        }
        //JDBC : 커넥션 만드는 비용이 비쌈 - 오래걸림.
        //스프링 부트 : hicari : 커넥션 객체 미리 만들고 사용. 
    }
}

JPA 프로그래밍: 프로젝트 세팅

 

데이터베이스 실행

  • PostgreSQL 도커 컨테이너 재사용
  • docker start postgres_boot
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>

스프링 부트

  • 스프링 부트 v2.*
  • 스프링 프레임워크 v5.*

 

스프링 부트 스타터 JPA

  • JPA 프로그래밍에 필요한 의존성 추가
    • JPA v2.*
    • Hibernate v5.*
  • 자동 설정: HibernateJpaAutoConfiguration
    • 컨테이너가 관리하는 EntityManager (프록시) 빈 설정
    • PlatformTransactionManager 빈 설정
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

 

엔티티메니저.. JPA 스팩. 

엔티티매니저 내부적 하이버네이트 사용. 

강의 해선. 스프링데이터 JPA 를 사용

 

 

JDBC 설정

  • jdbc:postgresql://localhost:5432/springdata
  • keesun
  • pass

 

application.properties

  • spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
  • spring.jpa.hibernate.ddl-auto=create

#spring.jpa.hibernate.ddl-auto : create or update ( 매번 스키마 생성.  )for dev, validate(매핑안되면 에러) for prod,추천 

update..  스키마 변경시...  스키마가 추가가됨.  주의 

 

spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation  :

추가한 드라이버가 메시지를 구현안했을때 경고  >> true  없음. 

 

 

 

spring.jpa.hibernate.ddl-auto=create..  운영에서 소스 잘못 올라가면 살인날듯

 

 

entityManager 

 

 

기본예제

더보기
#basic db
spring.datasource.url=jdbc:postgresql://localhost:5432/diarydata
spring.datasource.password=1234
spring.datasource.username=day

#hibernate
spring.jpa.hibernate.ddl-auto=create
#spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
package com.today10sec.diary.etc;

import org.hibernate.Session;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;

@Component
@Transactional
public class JpaRunner implements ApplicationRunner {

    @PersistenceContext
    EntityManager entityManager;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        Account account = new Account();
        account.setUsername("day");
        account.setPassword("12345");
        Session session = entityManager.unwrap(Session.class);
        session.save(account);
        entityManager.persist(account);
        System.out.println("접근");
    }
}
package com.today10sec.diary.etc;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Account {

    @Id @GeneratedValue
    private Long id;

    @Column
    private String username;

    private String password;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}