[Spring] JDBC: Spring, MyBatis (Day 56)

์ฝ”๋”ฉ๊ธฐ๋กยท2024๋…„ 12์›” 26์ผ

[๐Ÿ”—JDBC Java ์ฝ”๋“œ]

- ๐Ÿ”ง ์ดˆ๊ธฐ ์„ธํŒ…

- ๐Ÿฆ Vanilla ์ฝ”๋“œ

  1. @Repository ํด๋ž˜์Šค์— ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด DB ์ „์šฉ ๋“œ๋ผ์ด๋ฒ„ ๋กœ๋”ฉ
    -- Class.forName("org.mariadb.jdbc.Driver");

  2. DriverManager.getConnection(url, username, password)๋กœ DB์— ์—ฐ๊ฒฐํ•œ ํ›„

  3. Connection.preparedStatement(sql)์„ ํ†ตํ•ด sql ์ฟผ๋ฆฌ ์„ธํŒ…

  4. PreparedStatement.setLong() ๋“ฑ์„ ํ†ตํ•ด ์ฟผ๋ฆฌ์˜ ? ๊ฐ’์„ ์„ธํŒ…

  5. PreparedStatement.update(sql) : ์กฐํšŒ ์ œ์™ธ
    PreparedStatement.query(sql) : ์กฐํšŒ -> ResultSet ๋ฐ˜ํ™˜

  6. (์กฐํšŒ์˜ ๊ฒฝ์šฐ)
    while(ResultSet.next()) {
    Long field1 = ResultSet.getLong(column1)
    new Product(field1, field2)
    }

@Repository
public class PersonRepository {

    // Database์— ๋กœ๊ทธ์ธํ•  ์ •๋ณด
    private String username = "root";
    private String password = "joder9141";
    // ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์„ค์น˜๋œ ์ฃผ์†Œ (JDBC URL)
    private String url = "jdbc:mariadb://localhost:3306/practice";

    // ์ „์šฉ ๋“œ๋ผ์ด๋ฒ„ ํด๋ž˜์Šค
    private String driverClassName = "org.mariadb.jdbc.Driver";

    // ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ „์šฉ ๋“œ๋ผ์ด๋ฒ„ ๋กœ๋”ฉ
    public PersonRepository() {
        try {
            // ์ „์šฉ ๋กœ๋”ฉ ํด๋ž˜์Šค - DB๋งˆ๋‹ค ๋‹ค๋ฆ„
            Class.forName(driverClassName);
        } catch (ClassNotFoundException e) {
            System.out.println("DB ์—ฐ๊ฒฐ ์‹คํŒจ!");
        }
    }
    
    // ๋‹ค์ค‘ SELECT - ๋ชฉ๋ก ์กฐํšŒ
    public List<Person> findAll() {
        String sql = """
                    SELECT * FROM tbl_person
                """;

        List<Person> personList = new ArrayList<>();

        try {
            // 1. DB์— ์ ‘์†ํ•˜๊ณ  ์ ‘์† ์ •๋ณด๋ฅผ ๋ฐ›์•„์˜ด
            Connection conn = DriverManager.getConnection(url, username, password);

            // 2. SQL์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ์‹คํ–‰๊ธฐ ๊ฐ์ฒด๋ฅผ ๊ฐ€์ ธ์˜ด
            PreparedStatement pstmt = conn.prepareStatement(sql);

            // 3. ?๊ฐ’์„ ์„ธํŒ…

            // 4. SQL ์‹คํ–‰ ๋ช…๋ น
            //   4-a : ๊ฐฑ์‹ (INSERT, UPDATE, DELETE) ๋ช…๋ น : executeUpdate()
            //   4-b : ์กฐํšŒ(SELECT) ๋ช…๋ น                 : executeQuery()

            // ResultSet : ์กฐํšŒ๊ฒฐ๊ณผ๋กœ ๋‚˜์˜ค๋Š” 2์ฐจ์›์˜ ํ‘œ
            //   -> ํ‘œ์— ์ ‘๊ทผํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ๋‹ค.
            ResultSet rs = pstmt.executeQuery();


            // next(): ํฌ์ธํ„ฐ๋ฅผ ํ•œ ํ–‰์”ฉ ์ด๋™
            while (rs.next()) {
                // ์ปค์„œ๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๋Š” ํ–‰์˜ ๋ฐ์ดํ„ฐ ์ถ”์ถœ
                long id = rs.getLong("id");
                String personName = rs.getString("person_name");
                int age = rs.getInt("age");

                Person p = new Person(id, personName, age);
                System.out.println("p = " + p);

                personList.add(p);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return personList;
    }


}

- ๐ŸŒฑ Spring ์ฝ”๋“œ

[ 1. ๊ธฐ๋ณธ ์„ธํŒ… ]

  • main > resources > application.xml ํŒŒ์ผ ์ƒ์„ฑ ํ›„ ์•„๋ž˜์™€ ๊ฐ™์ด ์„ค์ •
// maria DB ๊ธฐ์ค€
spring:
   datasource:
      url: jdbc:mariadb://localhost:3306/practice
      username: root
      password: ์„ค์ • ๋น„๋ฐ€๋ฒˆํ˜ธ
      driver-class-name: org.mariadb.jdbc.Driver

[ 2. ์ž‘๋™ ์›๋ฆฌ ]

๐Ÿ”ถ 1) yml ํŒŒ์ผ์—์„œ db ์—ฐ๊ฒฐ ์„ธํŒ…์„ ํ•œ๋‹ค(id, password, driver, url)

  • ์œ„์˜ ๊ธฐ๋ณธ ์„ธํŒ… ๋‚ด์šฉ ์ฐธ๊ณ 

๐Ÿ”ถ 2) JDbcTemplate๋กœ DB์™€ ํ†ต์‹ ํ•˜๊ธฐ ์œ„ํ•ด JbcTemplate๋ฅผ @Autowired๋กœ ์ƒ์„ฑ

@Autowired
 prive final JdbcTemplate jdbcTemplate;
- ํ˜น์€ class๋ฅผ @RequiredArgsConstructor๋กœ ํ•˜๊ณ , private final JdbcTemplete ๋ณ€์ˆ˜๋ช…;
@RequiredArgsConstructor  // final ํ•„๋“œ๋งŒ ๊ณจ๋ผ๋‚ด์„œ ์ƒ์„ฑ์ž ๋งŒ๋“ฌ
 public class ProductRepository {
    private final JdbcTemplate jdbcTemplate; // ์˜์กด๊ฐ์ฒด๋Š” final์ฒ˜๋ฆฌ

๐Ÿ”ถ 3) DB ๋ช…๋ น์–ด๋ฅผ ๋งŒ๋“ค์–ด ์ค€๋‹ค.

๐Ÿ”ถ 4) ๋ช…๋ น์–ด๋ฅผ DB์— ๋ช…๋ นํ•ด ์ค€๋‹ค.
ย ย ย ย ย ๐Ÿ”นSELECT ์™ธ : JdbcTemplate.update(์‹คํ–‰ํ•  SQL ๋ช…๋ น์–ด, SQL์—์„œ ?๋กœ ํ‘œ์‹œ๋˜๋Š” ์ž๋ฆฌ์˜ ๊ฐ’)

int result = jdbcTemplate.update(
  "DELETE FROM scores WHERE id = ?", // ์‹คํ–‰ํ•  sql ๋ช…๋ น์–ด) 
  Product.getId() // ์œ„์˜ sql ๋ช…๋ น์–ด์—์„œ ? ๋ถ€๋ถ„์— ๋“ค์–ด๊ฐˆ ๊ฐ’ 
);

ย ย ย ย ย ๐Ÿ”นSELECT์—์„œ DB ์ „์ฒด ์กฐํšŒ :
ย ย ย ย ย ย ย ย ย ย 
ย ย ย ย ย ย ย ย ย  - i. List< T > result = jdbcTemplate.query(String sql, RowMapper< T > rowMapper);
ย ย ย ย ย ย ย ย ย ย  => **rowMapper ๋ถ€๋ถ„ ์“ฐ๋Š” ๋ฐฉ๋ฒ•์€ ์•„๋ž˜ ์ฐธ๊ณ 

 	   // 1. ์ต๋ช…ํด๋ž˜์Šค ์‚ฌ์šฉ
	  //  - ์ž‘๋™ ์›๋ฆฌ : query๋กœ ๋ฐ˜ํ™˜๋œ REsultSet์˜ ๊ฐ ํ–‰์„ ๋Œ์•„๊ฐ€๋ฉด์„œ, 
	  // product ๊ฐ์ฒด๋กœ ๋งŒ๋“ค๊ณ , ๊ทธ product๋ฅผ ๋‹ด์€ list๋ฅผ ๋งŒ๋“ ๋‹ค.
 	 new RowMapper<Product> {
   	    @Override
   	    public Product mapRow(ResultSet rs, int rowNum) throws SQLException {
  	       		return new Product(rs);
   	    });
  
  	// 2. ๋žŒ๋‹ค์‹
 	(rs, rawNum) -> new Product(rs);
 

ย ย ย ย ย  - ii. JdbcTemplate.query(sql, new BeanPropertyRowMapper(DB์—์„œ ๋ณ€ํ™˜ํ•˜๊ณ ์ž ํ•˜๋Š” ๊ฐ์ฒดํ˜•ํƒœ.class)

	  //jdbcTemplate.query(sql, new BeanPropertyRowMapper(db๋ฅผ ๋ณ€ํ™˜ํ•˜๊ณ ์ž ํ•˜๋Š” ๊ฐ์ฒด ํƒ€์ž….class);
	  return jdbcTemplate.query("""
   	  SELECT * FROM products
   	  """,
      new BeanPropertyRowMapper<>(Product.class));



ย ย ย ย ย ๐Ÿ”นSELECT์—์„œ DB ์ „์ฒด ์กฐํšŒ :
ย ย ย ย ย ย ย ย ย  - List< T > result = jdbcTemplate.queryForObject(String sql, RowMapper< T > rowMapper, ํ•„ํ„ฐ๋ง ๊ฐ’);

    public Board findOne(Long id) {
      return jdbcTemplate.queryForObject("""
              SELECT * FROM tbl_board
              WHERE id = ?
              """, new BeanPropertyRowMapper<>(Board.class)
              , id);
  }



๐Ÿ”ถ ์กฐํšŒ ์ตœ์ ํ™”๋ฅผ ์œ„ํ•ด, Service ํด๋ž˜์Šค์— @Transactional(readOnly=true)์„ ํ•ด์ค€๋‹ค.

- ๐Ÿ”Œ MyBatis ์ฝ”๋“œ

[ 1. ๊ธฐ๋ณธ ์„ธํŒ… ]

  • src > resources > application.yml ํŒŒ์ผ์— ์•„๋ž˜์™€ ๊ฐ™์ด ์ฝ”๋“œ ์ž‘์„ฑ
# database setting
Spring: 
  datasource:
    url: jdbc:mariadb://localhost:3306/practice
    username: root
    password: ****
    driver-class-name: org.mariadb.jdbc.Driver

# MyBatis Setting 
  mybatis:
    # sql file location
    #  - @Mapper๊ฐ€ ๋ถ™์–ด ์žˆ๋Š” ์ธํ„ฐํŽ˜์ด์Šค์˜ ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด
    #  - mapper-locations ํด๋”์—์„œ resource>์ ์–ด๋†“์€ ๊ฒฝ๋กœ>์ ์–ด๋†“๋Š” ํ™•์žฅ์ž์˜ ํŒŒ์ผ์„ ์ฐพ์•„์„œ ๊ทธ ํŒŒ์ผ์—์„œ ํ•ด๋‹น ๋ฉ”์†Œ๋“œ์™€ ์—ฐ๊ฒฐ๋„๋‹ˆ ์ฟผ๋ฆฌ๋ฅผ ์ฐพ์•„์„œ ์‹คํ–‰ํ•ด๋ผ
    mapper-locations: classpath:mapper/**/*.xml
    # ๋ฐ˜ํ™˜ํ•  ๊ฐ์ฒด์˜ package ์ฃผ์†Œ
    type-aliases-package: com.spring.mvcproject.database.mybatis
    # ๋‹ˆ๋„ค๊ฐ€ DB column ์ด๋ฆ„์ด aa_bb๋ฉด ๊ฐ์ฒด ํ•„๋“œ๋ช…์„ aaBb๋กœ ํ•˜๋ฉด ๋‚ด๊ฐ€ ์•Œ์•„์„œ db๋ž‘ ๊ฐ์ฒด๋ž‘ mapping ํ•ด์ค„๊ป˜
    configuration:
      # Java ํ•„๋“œ๋ช…๊ณผ DB ์นผ๋Ÿผ๋ช… ๊ฐ„์˜ ์ด๋ฆ„ ๋งคํ•‘ ์„ค์ •
      # DB ์นผ๋Ÿผ๋ช…์ด aa_bb์ธ ๊ฒฝ์šฐ, Java ๊ฐ์ฒด์˜ ํ•„๋“œ๋ช…์„ aaBb๋กœ ์ž๋™ ๋งคํ•‘
      map-underscore-to-camel-case: true

[ 2. ์ž‘๋™ ์›๋ฆฌ ]

๐Ÿ”ถ DB<->๊ฐ์ฒด ๊ฐ„์˜ ๋ณ€ํ™˜์„ ์œ„ํ•œ entity ๊ฐ์ฒด ์ƒ์„ฑ

ย ย ย ย  - @EqualAndHashCode(of="id")
ย ย ย ย ย ย : id๊ฐ€ ๊ฐ™์œผ๋ฉด ๊ฐ™์€ ๊ฐ์ฒด๋กœ ํŒ๋‹จ

ย ย ย ย  - @Builder (lombok ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ)
ย ย ย ย ย ย : Employee.builder.ํ•„๋“œ๋ช…(ํ•„๋“œ๊ฐ’).build()ํ•˜๋ฉด ์•Œ์•„์„œ this.ํ•„๋“œ๋ช… = ํ•„๋“œ๊ฐ’ ์ƒ์„ฑ

๐Ÿ”ถ @Mapper ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋งŒ๋“ ๋‹ค.
ย ย ย ย  - Repository ์—ญํ• ์„ ํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋กœ,
ย ย ย ย ย ย  @Mapper์„ ๋ถ™์ž„์œผ๋กœ์จ ์ด ์ธํ„ฐํŽ˜์ด์Šค์— ์ •์˜๋œ ๋ฉ”์†Œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด,
ย ย ย ย ย ย  resources>application.yml ํŒŒ์ผ์˜ MyBatias:Configuration:mapper-locations์—์„œ ์ง€์ •๋œ ๊ฒฝ๋กœ์—์„œ ๋งคํ•‘๋œ SQL๋ฅผ ์ฐพ์•„ ์‹คํ–‰ํ•œ๋‹ค.

@Mapper
public interface PetRepository {


    // Create: spring์ด๋‚˜ vanilla code์™€ ๋‹ฌ๋ฆฌ, **boolean ์ด ๋ฐ˜ํ™˜๋จ**
    boolean save(Pet pet);

    // Read - Single Matching
    Pet findById(Long id);

    // Read - Multiple Matching
    List<Pet> findAll();

    // Read - Count
    int petCount();

    // Update : spring์ด๋‚˜ vanilla code์™€ ๋‹ฌ๋ฆฌ, **boolean ์ด ๋ฐ˜ํ™˜๋จ**
    boolean updatePet(Pet pet);

    // Delete - Single : spring์ด๋‚˜ vanilla code์™€ ๋‹ฌ๋ฆฌ, **boolean ์ด ๋ฐ˜ํ™˜๋จ**
    boolean deleteById(Long id);

๐Ÿ”ถ resources>application.yml ํŒŒ์ผ์˜ MyBatias:Configuration:mapper-locations์—์„œ ์ง€์ •๋œ ๊ฒฝ๋กœ์— ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด, SQL์ฟผ๋ฆฌ๋ฅผ ๋งคํ•‘


<?xml version="1.0" encoding="UTF-8"?>
<!-- mapper ํƒœ๊ทธ๋กœ ์‹œ์ž‘๋˜๋ฉฐ, ๊ทœ์น™์€ "" ์•ˆ์— ์žˆ๋Š” ์ฃผ์†Œ์˜ ๊ทœ์น™์„ ๋”ฐ๋ฅธ๋‹ค. -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!-- namespace: ์—ฐ๋™ํ•  ์ธํ„ฐํŽ˜์ด์Šค์˜ ํ’€๋„ค์ž„  -->
<mapper namespace="com.spring.mvcproject.database.mybatis.repository.PetRepository">

    <!-- [๊ตฌ์กฐ]
          <์ฟผ๋ฆฌ์ข…๋ฅ˜(insert/select/update/delete)
                id="@Mapper ํด๋ž˜์Šค์˜ ์–ด๋–ค ๋ฉ”์†Œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋ ๋•Œ ์ด ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜ ๊ฒƒ์ธ๊ฐ€"
                    (DB์˜ ์ •๋ณด๋ฅผ ์ถ”์ถœํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ „๋‹ฌํ•  ๊ฒฝ์šฐ) resultType="DB ๋‚ด์šฉ์„ ์–ด๋–ค ํ˜•ํƒœ(๊ฐ์ฒด, int ๋“ฑ)๋กœ ๋ณ€ํ™˜ํ•  ๊ฒƒ์ธ๊ฐ€"
           >
                  ์ฟผ๋ฆฌ๋ฌธ
                  (ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋ฉ”์†Œ๋“œ์˜ ์ธ์ž๋กœ ๋ฐ›์€ ๊ฐ’์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด, #{์ธ์ž} ํ˜น์€ #{์ธ์ž์ธ ๊ฐ์ฒด์˜ ํ•„๋“œ๋ช…})
          </์ฟผ๋ฆฌ๋‹ซ๊ธฐ>
    -->
    <insert id="save" keyProperty="id", useGeneratedKeys="true">
        INSERT INTO tbl_pet
            (pet_name, pet_age, injection)
        VALUES
        --  ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋ฉ”์†Œ๋“œ์˜ ์ธ์ž๋กœ Pet์„ ๋ฐ›์•˜๋Š”๋ฐ(Pet ํด๋ž˜์Šค์˜ save() ๋ฉ”์†Œ๋“œ paraemter๋ณด๋ฉด Pet์„ ๋ฐ›์Œ),
        --  DB์˜ pet_Name ์นผ๋Ÿผ์˜ ๊ฐ’์„ Values์„ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ „๋‹ฌ๋ฐ›์€ Pet ๊ฐ์ฒด์˜ petName ํ•„๋“œ ๊ฐ’์œผ๋กœ ๋„ฃ์–ด๋‹ฌ๋ผ.
            (#{petName}, #{petAge}, #{injection})
    </insert>

    <!--  select๋Š” resultType(DB๋ฅผ ์–ด๋–ค ๊ฒƒ์œผ๋กœ ๋ณ€ํ™˜ํ•  ๊ฒƒ์ธ์ง€๋ฅผ ํ’€ ๋„ค์ž„์œผ๋กœ) ํ•„์ˆ˜ -->
    <!-- ์›๋ž˜๋Š” com.spring.mvcproject.database.mybatis.Pet์ธ๋ฐ ๊ท€์ฐฎ์œผ๋‹ˆ๊นŒ ์„ค์ •ํŒŒ์ผ์—์„œ type-aliases-package์— ์ง€์ •-->
    <select id="findById" resultType="Pet"> 
        SELECT * FROM tbl_pet
        WHERE id = #{id}
        -- PetRepository ์ธํ„ฐํŽ˜์ด์Šค์˜
        -- (ํ•ด๋‹น ์ธํ„ฐํŽ˜์ด์Šค๋Š” @Mapper๊ฐ€ ์„ค์ •๋˜์–ด ์žˆ์–ด์•ผ ํ•˜๋ฉฐ, ์ด ํŒŒ์ผ์—์„œ๋„ mapper namespace์— ํ•ด๋‹น ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์„ค์ •๋˜์–ด ์žˆ์–ด์•ผ ํ•จ) 
        -- findById(Long id) ๋ฉ”์†Œ๋“œ๊ฐ€ ํ˜ธ์ธจ๋˜๋ฉด (id = "findById")
        --    ->  ํ•ด๋‹น ์ฟผ๋ฆฌ๋ฅผ๋ฅผ ์‹คํ–‰ํ•ด๋ผ.
        --    -> ์ด ๋•Œ, WHERE id = ์˜ ์กฐ๊ฑด์€ findById(Long id)์˜ ์ธ์ž์ธ id๋ฅผ ์จ๋ผ.
        --    -> ๊ทธ๋ฆฌ๊ณ  next()์„ ํ•˜๋ฉด์„œ ํ•œ ์ค„ ํ•œ ์ค„ ๋Œ์•„๊ฐ€๋ฉด์„œ (resultType="Pet"์ด๋ฏ€๋กœ) Pet ๊ฐ์ฒด๋กœ ๋ฐ”๊ฟ”์ค˜๋ผ.
    </select>

}
  
 

</mapper>

๐Ÿ”ถ sql ์‹ฌํ™” ๊ณผ์ •
** 1) if๋ฌธ์„ mybatis์— ์ ์šฉํ•˜๊ธฐ ( when test , otherwise, choose )

  • repository๊ฐ€ public User getUserData(String userRole) ์ผ ๋•Œ, ์•„๋ž˜์™€ ๊ฐ™์ด mapper์งค ์ˆ˜ ์žˆ์Œ

๐Ÿ”ถ ์กฐํšŒ ์ตœ์ ํ™”๋ฅผ ์œ„ํ•ด, Service ํด๋ž˜์Šค์— @Transactional(readOnly=true)์„ ํ•ด์ค€๋‹ค.

0๊ฐœ์˜ ๋Œ“๊ธ€