Spring - PostgreSQL 연결

TopOfTheHead·2025년 11월 9일

Spring Boot

목록 보기
18/26

Postgres DB와 Spring Boot 연결하기 참고
SpringPSA에 의해 MySQL, H2 DB 등에 Spring JPA를 통해 구현한 DB Entity의 구현내용을 재활용하여 PostgreSQL DB에도 적용이 가능하다.
Spring JDBC , Spring JPA를 사용할 경우 PostgreSQL DB , MySQL DB , H2-DB 등에 관계없이 구축된 DB Entity을 매우 쉽게 적용할 수 있다.
Spring JDBCJdbcTemplate Class를 이용하여 SQL을 하드코딩하여 PostgreSQL DB와 상호작용을 수행할 수 있다.

  • Spring Boot에 PostgreSQL DB 연결 시 필요한 dependency
    • Maven
      pom.xml에 정의

      Spring Web

      <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
      </dependency>

      Spring JDBC

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-data-jdbc</artifactId>
      </dependency>

      Spring Data JPA

      <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-jpa</artifactId>
      </dependency> 

      PostgreSQL Driver

      <dependency>
                <groupId>org.postgresql</groupId>
                <artifactId>postgresql</artifactId>
                <scope>runtime</scope>
      </dependency>


    • Gradle
      build.gradle에 정의
      implementation 'org.springframework.boot:spring-boot-starter-web' // Spring Web 
      implementation 'org.springframework.boot:spring-boot-starter-jdbc' // Spring JDBC
      implementation 'org.springframework.boot:spring-boot-starter-data-jpa' // Spring Data JPA
      implementation group: 'org.postgresql' , name: 'postgresql' , version: '42.2.23'  // PostgreSQL Driver
      runtimeOnly 'org.postgresql:postgresql' 


  • application.ymlPostgreSQL DB DataSource 정의
    Spring Boot가 기본적으로 자동으로 연결할 PostgreSQL DBDataSource를 설정하기위해 application.ymlpostgresql에 관한 정보를 정의.
    @Bean을 선언하여 직접 DataSource instance를 생성하는 방법은 아래에서 소개.
    JdbcUserDetailsManager(DataSource객체)에서 사용됨.

    Spring Boot가 자동으로 연결할 DataSource를 설정 시 application.properties에 기존에 정의된 h2-db 설정을 삭제해야한다.
spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/All4Runner
    username: ${DB_USERNAME:postgres}
    password: ${DB_PASSWORD:wjd747}
  jpa:
    database-platform: org.hibernate.dialect.PostgreSQLDialect
    show-sql: true
    hibernate:
      ddl-auto: update

    • spring.datasource.url=JDBC URL :
      。 특정 DB의 JDBC URL
      ▶ Local에서 실행되고 있는 PostgreSQL DB의 URL을 정의.
      H2-DB에서 동적 URL을 고정하기 위한 정적 JDBC URL 설정 시에도 사용됨.
      spring.datasource.url=jdbc:h2:mem:testdb

    • spring.datasource.username , spring.datasource.password :
      DB에 접근하기 위한 사용자 이름과 비밀번호

    • spring.jpa.database-platform :
      Hibernate가 사용할 PostgreSQL DB의 Dialect

    • spring.jpa.show-sql :
      SQL Query를 Console에 출력할지의 여부

    • spring.jpa.hibernate.ddl-auto :
      。Application 실행 시 Hibernate가 DB Schema를 자동 Update할지의 여부를 설정.
      spring.jpa.hibernate.ddl-auto=update로 설정할 경우, Entity Class와 DB Table Schema를 동기화.
      • Hibernate
        JPA를 구현한 대표적인 오픈소스.
        Hibernate JAR을 class path에 추가해서 Hibernate를 JPA 구현체로 사용.

      • Dialect :
        JPA 또는 Hibernate에서 사용하는 특정 DB에 해당하는 SQL문법과 기능을 정의한 설정.
        ▶ 각 DB( MySQL , PostgreSQL , Oracle )등은 공통적으로 SQL 표준을 따르지만, 각각 조금씩 차이가 발생하는 SQL 문법을 가지므로 Hibernate는 해당 문법/기능적 차이를 Dialect를 통해 자동으로 조정.

        주요 Dialect

        • org.hibernate.dialect.MySQLDialect : MySQL
        • org.hibernate.dialect.MariaDBDialect : MariaDB
        • org.hibernate.dialect.PostgreSQLDialect : PostgresDB
        • org.hibernate.dialect.H2Dialect : H2DB


  • Spring JDBCPostgreSQL DB DataSource instance 생성하기
    PostgreSQL에서 JdbcUserDetailsManager(DataSource객체)을 통한 DB에 사용자 자격증명을 추가할때의 용도로 활용할 DataSource instance 정의하기. JdbcUserDetailsManager 설정
    ▶ 사용자 자격증명 저장이 필요없는 경우, 설정안해도 application.properties에서 Spring Boot에 자동연결설정된 DataSource를 활용하여 기본적으로 CRUD를 수행 가능.
    • DataSource 생성하는 @Bean Method 정의
      @Configuration이 선언된 Configuration Class에서 DataSource instance를 반환하는 @Bean Method를 생성.
    	@Bean
        public DataSource pgdataSource() {
            HikariConfig config = new HikariConfig();
            config.setJdbcUrl("jdbc:postgresql://localhost:5432/GeoDB");
            config.setUsername("postgres");
            config.setPassword("wjd747");
            return new HikariDataSource(config);
        }

    postgresql DataSource를 정의하기위해 application.properties에서 작성된 JDBC 내용과 유사.



  • Spring JPA 기능을 활용하여 PostgreSQL과 연동
    Spring JPA를 활용해 REST API를 통해 PostgreSQL DB와 상호작용 수행 시 활용.
    PostgreSQL DB의 Table과 Mapping된 JPA Entity Class 생성
    H2 DBSpring JPA를 통해 구현한 DB Entity의 구현내용을 재활용하여 PostgreSQL DB에도 적용이 가능하다.
    • DB Entity 생성
      PostgreSQL DB와 Data Binding되어 상호작용을 수행하는 Entity Class 생성
      @Entity를 선언하여 Entity Class 생성 및 @Id@GeneratedValueprimary key field에 선언
      ▶ 기존 H2-DB로 실습한 Entity Class를 그대로 활용.
      // PostgresUser.java
    import com.fasterxml.jackson.annotation.JsonIgnore;
    import com.fasterxml.jackson.annotation.JsonProperty;
    import jakarta.persistence.*;
    import jakarta.validation.constraints.Past;
    import jakarta.validation.constraints.Size;
    import java.time.LocalDate;
    import java.util.List;
    @Entity
    public class PostgresUser {
        @Id
        @GeneratedValue
        Integer id;
        @JsonProperty("username")
        @Size(min=2, message = "Name should have at least 2 characters.")
        String name;
        @JsonProperty("birth_date")
        @Past(message="Birth Date should be in the past.")
        LocalDate birthDate;
        public List<PostgresPost> getPosts() { return posts; }
        public void setPosts(List<PostgresPost> posts) { this.posts = posts; }
        @OneToMany(mappedBy="user")
        @JsonIgnore
        private List<PostgresPost> posts;
        public PostgresUser(){}
        public PostgresUser(Integer id, String name, LocalDate birthDate) {
            this.id = id;
            this.name = name;
            this.birthDate = birthDate;
        }
        public Integer getId() { return id; }
        public String getName() { return name; }
        public LocalDate getBirthDate() { return birthDate; }
        public void setId(Integer id) { this.id = id; }
        public void setName(String name) { this.name = name; }
        public void setBirthDate(LocalDate birthDate) { this.birthDate = birthDate; }
    }

    id field의 경우 Wrapper ClassInteger type으로 설정.
    int와 달리 Wrapper Class객체 이므로 Null값을 저장할 수 있다.
    DB에서 pk가 없거나 아직 할당되지 않은 상태를 지시하기 위해 NULL을 사용할 수 있게 설정.
    Integer vs int

    intInteger 차이
    Java에서 둘다 정수를 다루지만, 기본 자료형(primitive type)과 wrapper class라는 차이가 존재.

    • int
      。기본자료형 ( primitive type )
      null값을 가질 수 없다.

    • Integer
      java.lang.Integer Class 객체
      int를 객체로 감싼 Wrapper Class 객체
      new 키워드로 선언하여 객체 생성.

      null값을 저장 가능.
      DB Entityprimary key로 활용.


    • JPACRUD를 수행하기위한 JpaRepository<Entity,ID Field> Interface 정의
    import org.springframework.data.jpa.repository.JpaRepository;
    public interface PgRepository extends JpaRepository<PostgresUser,Integer> { }

    .

    • JPABusiness Logic을 구현하기위한 Service Class 정의
      Business Logic 구현을 위한 Spring Bean이므로 @Service 선언.
      @Entity에서 명시적 구체화.

      JpaRepository<> interface의 instance 생성 및 생성자주입을 수행하여 CRUD method를 구현
    import java.util.List;
    import java.util.Optional;
    import org.springframework.stereotype.Service;
    @Service
    public class pgService {
        // JpaRepository instance
        PgRepository pgRepository;
        // @Autowired 생략 가능한 Constructor Based Dependency Injection
        public pgService(PgRepository pgRepository) {
            this.pgRepository = pgRepository;
        }
        // Create
        public PostgresUser CreatePGUser(PostgresUser user) {
            return pgRepository.save(user);
        }
        // Update
        public PostgresUser UpdatePGUser(PostgresUser user) {
            return pgRepository.save(user);
        }
        // Read
        public Optional<PostgresUser> GetPGUser(Integer id) {
            return pgRepository.findById(id);
        }
        public List<PostgresUser> GetAllPGUsers() {
            return pgRepository.findAll();
        }
        // Delete
        public void DeletePGUser(Integer id) {
            pgRepository.deleteById(id);
        }
    }

    JpaRepository.save(DB Entity)CREATEUPDATE 기능을 수행.
    ▶ 기존 Entity를 사용 시 UPDATE, 새로운 Entity를 사용 시 INSERT

    • CRUD기능을 포함한 REST API를 구현할 Controller Class 생성
      JPA CRUD를 사전에 정의한 pgService Spring Bean instance를 생성하여 의존성 주입하여 수행.
    import jakarta.validation.Valid;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.*;
    import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
    import java.net.URI;
    import java.util.List;
    import java.util.Optional;
    @RestController
    public class PgController {
        // JPA Business Logic을 사전에 정의한 Spring Bean
        pgService pgservice;
        // @Autowired 생략 가능한 Constructor Based Dependency Injection
        public PgController(pgService pgservice) {
            this.pgservice = pgservice;
        }
        // GET Method 구현
        // GET /users : 모든 사용자 조회
        @GetMapping(path="/pg/jpa/users")
        public List<PostgresUser> ListAllUsers(){
            // JpaRepository를 구현한 Service Class의 JpaRepository객체.findAll() Method 활용.
            List<PostgresUser> users = pgservice.GetAllPGUsers();
            return users;
        }
        // GET /users/{id} : 특정 사용자 조회
        @GetMapping(path="/pg/jpa/users/{id}")
        public Optional<PostgresUser> GetUserById(@PathVariable int id){
            // JpaRepository를 구현한 Service Class의 JpaRepository객체.findById() Method 활용.
            // 가져온 데이터는 Optional<PostgresUser>로 return.
            Optional<PostgresUser> user = pgservice.GetPGUser(id);
            if(user.isEmpty()){ // 해당 id가 존재하지 않는 경우 발생.
                throw new UserNotFoundException("id :" + id);
            }
            return user;
        }
        // POST Method 구현
        // POST /users : 새로운 사용자 생성
        @PostMapping(path="/pg/jpa/users")
        public ResponseEntity<PostgresUser> createUser(@Valid @RequestBody PostgresUser user) {
            // @RequestBody : HttpRequest의 Body(=JSON Format)를 Bean으로 변환하여 저장.
            // JpaRepository를 구현한 Service Class의 JpaRepository객체.save() Method 활용.
            PostgresUser savedUser = pgservice.SavePGUser(user);
            //REST API에서 알맞은 Response Status를 Client에게 반환하는 코드
            URI locationHeader = ServletUriComponentsBuilder.fromCurrentRequest()
                    .path("/{id}").buildAndExpand(savedUser.getId()).toUri();
            // 201 : created status에 해당하는 ResponseEntity<User> 객체를 생성하여 반환.
            return ResponseEntity.created(locationHeader).build();
        }
        // Post의 Update를 수행하고 Save한 PostgresPost instance를 return.
        // PUT /users/{id}
        @PutMapping(path="/pg/jpa/users/{id}")
        public PostgresUser UpdateUserById(@PathVariable int id, @RequestBody PostgresUser user){
            // HTTP Request Body가 @RequestBody를 통해 변수에 Mapping되어 Update를 수행.
            pgservice.UpdatePGUser(user);
            return user;
        }
        @DeleteMapping(path="/pg/jpa/users/{id}")
        public void DeleteUserById(@PathVariable int id){
            // JpaRepository를 구현한 Service Class의 JpaRepository객체.deleteById() Method 활용.
            pgservice.DeletePGUser(id);
        }
    }



    PostgreSQL DB에 해당 Entity Class의 Table 명이 없을 경우 Spring JPA에 의해 자동생성됨.



  • Spring JDBC 기능을 활용하여 PostgreSQL과 연동 Spring JDBC
    SQL을 조작하여 PostgreSQL DB과 상호작용시 사용.
    • Spring JDBC를 통해 data의 삽입을 용이하게 하는 Spring Bean Class 생성
      。해당 Class를 통해 Spring Bean instance를 생성하여 Spring JDBC를 통해 DB에 Insert를 수행.
    import java.time.LocalDate;
    public class PostgresUserforJDBC {
        int id;
        String name;
        LocalDate birth_date;
        public PostgresUserforJDBC(int id, String name, LocalDate birth_date) {
            this.id = id;
            this.name = name;
            this.birth_date = birth_date;
        }
        public int getId() {return id; }
        public void setId(int id) {this.id = id;}
        public String getName() {return name;}
        public void setName(String name) {this.name = name;}
        public LocalDate getBirth_date() {return birth_date;}
        public void setBirth_date(LocalDate birth_date) {this.birth_date = birth_date; }
    }
    • Spring JDBC을 활용하여 DAO를 구현하는 Class 생성
      Spring JDBC를 통해 하드코딩한 SQL를 DB에 반영하는 Class객체에 @Repository를 선언하여 Spring Bean으로 구체화.
      。특정 SQL문을 JdbcTemplate Class의 instance를 활용하여 JdbcTemplate객체.update(SQL구문)로 DB에 SQL을 반영하는 Logic을 구현.
      DAO(Data Access Obejct : DB에 접근하는 역할을 수행하는 객체)
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.stereotype.Repository;
    @Repository
    public class pgJdbcRepository {
        private JdbcTemplate jdbcTemplate;
        // @Autowired  : 생성자기반 의존성주입
        public pgJdbcRepository(JdbcTemplate jdbcTemplate) {
            this.jdbcTemplate = jdbcTemplate;
        }
        // text-block을 활용한 sql 문자열
        private static String sqlinsert =
                """
                insert into postgres_user(id,birth_date,name) values(?,?,?);
                """;
        public void insert(PostgresUserforJDBC user){
            jdbcTemplate.update(sqlinsert,user.getId(),user.getBirth_date(),user.getName());
        }
    }
    • CommandLineRunner 상속 Class 생성
      CommandLineRunner를 상속한 Class를 정의하여 run()을 통해 application 구동 시 DB와 상호작용하는 기능을 구현한 Spring Bean( = pgJdbcRepositor ) instance의 method를 작동.
      JdbcTemplateupdate(sql) method로 DB와 상호작용을 수행하는 method를 구현.

      Query문 입력 시 text-block내에 SQL문을 입력.
     import org.springframework.boot.CommandLineRunner;
    import org.springframework.stereotype.Component;
    import java.time.LocalDate;
    @Component
    public class pgCodeLineRunnerClass implements CommandLineRunner {
        private pgJdbcRepository jdbcRepository;
        // @Autowired  : 생성자기반 의존성주입
        public pgCodeLineRunnerClass(pgJdbcRepository jdbcRepository) {
            this.jdbcRepository = jdbcRepository;
        }
        @Override
        public void run(String... args) throws Exception {
            jdbcRepository.insert(new PostgresUserforJDBC(2, "Lee",LocalDate.now()));
        }
    }

    @SpringBootApplication Class의 Component Scan에 의해 @Component가 선언된 해당 CommandLineRunner Class를 식별하여 run() method를 실행하여 DAO에서 구현된 JdbcTemplateSQLSpring JDBC 기능을 실행.

    。다음처럼 성공적으로 insert into postgres_user(id,birth_date,name) values(?,?,?); SQL 구문을 통해 Data Insert가 완료된것을 확인 가능.

    JDBC / Spring JDBC / JPA / Spring Data JPA 차이

    • JDBC :
      。SQL을 많이 작성하면서 Java 코드가 많다.
      ▶ 하드코딩이 많음.

    • Spring JDBC :
      。SQL을 많이 작성
      JdbcTemplate instance를 통해 Java코드는 적게 사용.

    • JPA :
      。SQL을 작성하지않음
      EntityManager을 통해 DB Entity를 DB Table로 Mapping 하는 과정 필요.

    • Spring JPA :
      SQL , EntityManager을 사용하지 않는다.
      EntityManager 대신 JpaRepository<Entity class, ID> Interface를 상속한 Interface를 Instance로 생성하여 활용.

      Spring JDBC , Spring JPA를 사용할 경우 PostgreSQL DB , MySQL DB , H2-DB 등에 관계없이 구축된 DB Entity을 매우 쉽게 적용할 수 있다.

      Spring JDBCJdbcTemplate객체의 경우 직접 SQL을 작성하여 JdbcTemplate객체.update(SQL)로 상호작용한다면 JpaRepository는 자동으로 Query가 생성되는 Method를 제공.
      • DAO ( Data Access Object ) :
        DB와 직접 상호작용을 수행하는 Object로서 개발자가 SQL을 직접 작성하여 CRUD를 수행.
        Spring에서는 보통 @Repository Annotation으로 선언하여 Spring Bean instance으로 생성.

        JdbcTemplate 같은 Spring JDBC를 활용하여 DB와 상호작용하는 method를 제공하는 Class를 instance로 생성하여 활용.

      • ORM ( Object-Relational Mapping )
        。Application의 DB Entity Class instance를 RDBMS의 DB Table에 자동으로 영속화 하는것을 의미.
        DB Entity instance와 RDBMS DB Table 간 자동 Mapping.

        SQL을 직접 사용하지 않고, DB Entity instance를 중심으로 DB의 Data 조작을 수행.
        RDBMS ( H2-DB, mySQL , postgreSQL ) 의 변경에도 유연한 적용이 가능.
profile
공부기록 블로그

0개의 댓글