H2 데이터베이스
개발이나 테스트 용도로 가볍고 편리한 DB, 웹 화면 제공 (실무에선 MySQL, Oracle 많이씀)
설치
H2 Database Engine (redirect)
H2 Database Engine Welcome to H2, the free SQL database. The main feature of H2 are: It is free to use for everybody, source code is included Written in Java, but also available as native executable JDBC and (partial) ODBC API Embedded and client/server mo
www.h2database.com
1. 설치 후 "설치폴더>bin"경로로 터미널 열기
2. 권한 주기(Mac)
chmod 755 h2.sh
3. 실행
./h2.sh //(Mac)
h2.bat //(Window)
4. H2콘솔창 뜨면 끝

데이터베이스 파일 생성
1. 위사진대로 연결(JDBC URL:jdbc:h2:~/test 최초 한번)
2. 터미널로 홈에 파일 '~/test.mv.db' 생성됐는지 확인
3. 이후 부턴 JDBC URL: jdbc:h2:tcp://localhost/~/test 로 접속 (파일 직접 접근아닌 소켓통한 접근) 이래야 여러곳에서 접근가능
테이블 생성
create table member(
id bigint generated by default as identity,
name varchar(255),
primary key (id)
)
Long = bigint
generated by default as identity : 값없이 insert하면 디비 자동 증가번호 할당
GENERATED ALWAYS AS IDENTITY: 오직 자동 증가 값만 사용 가능 (사용자값입력x)
*따로 최상위에 sql폴더,ddl.sql파일만들어 관리하면 좋음
순수 JDBC
고전에 쓰던 서버와 DB 연결 방식으로, 직접 SQL을 작성하고 수동으로 리소스를 관리해야 하는 방식
(가볍게 보자...)
-build.gradle에 라이브러리 추가
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
runtimeOnly 'com.h2database:h2' // H2 데이터베이스 (테스트용 내장 DB)
-resources/application.properties 에 스프링 부트 데이터베이스 연결 설정 추가 (밑에 자세히 설명)
spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver //빨간색이뜬다면build.gradle 새로고침
spring.datasource.username=sa
-Jdbc 리포지토리 구현(JdbcMemberRepository) --생략
-스프링 설정 변경
import hello.hello_spring.repository.JdbcMemberRepository;
import hello.hello_spring.repository.MemberRepository;
import hello.hello_spring.repository.MemoryMemberRepository;
import hello.hello_spring.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class SpringConfig {
private final DataSource dataSource;
public SpringConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
// return new MemoryMemberRepository();
return new JdbcMemberRepository(dataSource);
}
}
JdbcTemplate
순수JDBC로 만들기 힘들어 jdbc템플릿 또는 MyBatis 이용
이를 통해 반복 코드를 대부분 제거 but, SQL은 직접 작성
-build.gradle에 라이브러리 추가
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
runtimeOnly 'com.h2database:h2'
-resources/application.properties 에 스프링 부트 데이터베이스 연결 설정 추가
spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
application.properties에 DB 정보를 입력하면 자동으로 DataSource를 빈등록생성
@Autowired 또는 생성자 주입으로 사용
-JdbcTemplateMemberRepository
public class JdbcTemplateMemberRepository implements MemberRepository {
private final JdbcTemplate jdbcTemplate; // JdbcTemplate 객체
//@Autowired 생략가능
public JdbcTemplateMemberRepository(DataSource dataSource) { //DataSource(DB와 연결 관리 객체) 주입
jdbcTemplate = new JdbcTemplate(dataSource);
}
@Override
public Member save(Member member) {
//걍 가볍게 보기
//SimpleJdbcInsert: JDBC에서 쿼리작성없이 INSERT 문을 쉽게 실행하게 해주는 클래스
SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate);
//insert할 테이블 지정, 자동 생성되는 키 컬럼 지정
jdbcInsert.withTableName("member").usingGeneratedKeyColumns("id");
//INSERT할 값 설정
Map<String, Object> parameters = new HashMap<>();
parameters.put("name", member.getName());
//INSERT 실행 + 자동 생성된 키 값 가져오기
Number key = jdbcInsert.executeAndReturnKey(new
MapSqlParameterSource(parameters));
member.setId(key.longValue());
return member;
}
@Override
public Optional<Member> findById(Long id) {
//jdbcTemplate.query(SQL, RowMapper: 쿼리결과데이터를 자바객체에 매핑, ?에 들어갈 파라미터) <-List 반환
List<Member> result = jdbcTemplate.query("select * from member where id = ?", memberRowMapper(),id);
return result.stream().findAny();
}
@Override
public List<Member> findAll() {
return jdbcTemplate.query("select * from member", memberRowMapper());
}
@Override
public Optional<Member> findByName(String name) {
List<Member> result = jdbcTemplate.query("select * from member where name = ?", memberRowMapper(), name);
return result.stream().findAny();
}
private RowMapper<Member> memberRowMapper() {
return new RowMapper<Member>() { //opt엔터로 람다 바꿀수 있음
@Override
public Member mapRow(ResultSet rs, int rowNum) throws SQLException {
//rs → 쿼리 결과 행(row)의 데이터, rowNum → 행의 인덱스
Member member = new Member();
member.setId(rs.getLong("id"));// 현재 row에서 "id" 값을 Long 타입으로 가져옴
member.setName(rs.getString("name"));// 현재 row에서 "name" 값을 String 타입으로 가져옴
return member;
}
};
}
// private RowMapper<Member> memberRowMapper() {
// return (rs, rowNum) -> {
// Member member = new Member();
// member.setId(rs.getLong("id"));
// member.setName(rs.getString("name"));
// return member;
// }
// }
}
-스프링 설정 변경
@Bean
public MemberRepository memberRepository() {
//return new MemoryMemberRepository();
//return new JdbcMemberRepository(dataSource);
return new JdbcTemplateMemberRepository(dataSource);
}
MyBatis 참고
1. pom.xml에 MyBatis-Spring Boot Starter 추가 (SqlSessionFactory, SqlSessionTemplate을 생성) <- MyBatis는 이를통해 DataSource 주입함
-Maven
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.0</version> <!-- 최신 버전 확인 필요 -->
</dependency>
<!-- MySQL -->
//<dependency>
// <groupId>mysql</groupId>
// <artifactId>mysql-connector-java</artifactId>
// <scope>runtime</scope>
//</dependency>
<!-- Oracle -->
//<dependency>
// <groupId>com.oracle.database.jdbc</groupId>
// <artifactId>ojdbc8</artifactId>
// <version>19.8.0.0</version> <!-- 버전 확인 필요 -->
//</dependency>
-gradle
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.3.0'
runtimeOnly 'mysql:mysql-connector-java:8.0.25'
#implementation 'mysql:mysql-connector-java:8.0.25' // MySQL사용할 경우.최신 버전 확인 필요
#implementation 'com.oracle.database.jdbc:ojdbc8:19.8.0.0' //Oracle. 버전 확인 필요
2. DB 설정 (application.properties)
# Oracle 사용 시
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@localhost:1521:xe
spring.datasource.username=root
spring.datasource.password=1234
# MySQL 사용 시
# spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# spring.datasource.url=jdbc:mysql://localhost:3306/dbname
# spring.datasource.username=root
# spring.datasource.password=1234
mybatis.mapper-locations=classpath:database/mappers/*Mapper.xml
mybatis.config-location=classpath:database/config/MybatisConfig.xml
DataSource가 자동 주입됨
3. Mapper 인터페이스 사용 (자동으로 MyBatis 연동됨)
@Mapper
public interface UserMapper {
ArrayList<HashMap<String, Object>> findAll();
}
4. Mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.app.mapper.UserMapper"> //Mapper 인터페이스 지정
<select id="findAll" resultType="HashMap"> //Mapper 인터페이스의 메서드명과 id 동일
select * from member
</select>
</mapper>
parameterType="Pagination"
#{code}
JPA
SQL도 JPA가 직접 만들어 실행해 객체를 바로 DB에 저장하고 관리할 수 있도록 도와주는 기술로, 개발 생산성을 높이고 객체 중심 설계를 가능
-build.gradle에 라이브러리 추가
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
추가 시 자동 EntityManagerFactory 빈 등록됨 이를통해 EntityManager생성되고
@Autowired private EntityManager em;통해 자동주입됨
-resources/application.properties 스프링부트에 JPA 설정 추가
spring.jpa.show-sql=true //JPA가 실행하는 SQL 쿼리를 콘솔에 출력
spring.jpa.hibernate.ddl-auto=none //테이블 자동으로 생성 기능
//create하면 테이블도 자동으로 만들어줌
- domain/Member
import jakarta.persistence.*;
@Entity //JPA가 관리하는 엔티티 표현
public class Member {
@id //기본 키(PK) 지정
@GeneratedValue(strategy = GenerationType.IDENTITY) // DB가 자동으로 ID 생성
private Long id;
//@Column(name = "username") //컬럼명 (테이블의 "username"이라는 컬럼과 매핑)
private String name;
...
*@GeneratedValue
기본 키 값을 자동으로 생성 방법 지정, @Id와 함께 사용
| GenerationType.AUTO | 사용 중인 DB에 맞게 자동 선택 |
| GenerationType.IDENTITY | MySQL, MariaDB 등에서 사용하는 AUTO_INCREMENT 방식 |
| GenerationType.SEQUENCE | Oracle, PostgreSQL 등에서 사용하는 시퀀스(Sequence) 기반 자동 증가 |
| GenerationType.TABLE | 별도의 키 생성용 테이블을 만들어 ID 관리 (잘 사용되지 않음) |
-JpaMemberRepository
public class JpaMemberRepository implements MemberRepository{
private final EntityManager em; //jpa EntityManager를 통해 동작
public JpaMemberRepository(EntityManager em) {
this.em = em;
}
@Override
public Member save(Member member) {
em.persist(member); //INSERT
return member;
}
@Override
public Optional<Member> findById(Long id) {
//= SELECT * FROM Member WHERE id = ?;
//pk통한 단일 조회,(조회할 타입,식별자pk)
Member member = em.find(Member.class, id);
return Optional.ofNullable(member);
}
@Override
public Optional<Member> findByName(String name) {
//나머지 조회. JPA Query Language 객체제한 쿼리언어
// 테이블아닌 객체(엔티티)대상 쿼리날림 이걸 sql로 번역됨. select *이 아닌 엔티티자체(m) 셀렉트
List<Member> result = em.createQuery("select m from Member m where m.name = :name", Member.class)
.setParameter("name", name)
.getResultList(); //SELECT결과 여러 개일 때 리스트로 반환
return result.stream().findAny();
}
@Override
public List<Member> findAll() {
return em.createQuery("select m from member m",Member.class)
.getResultList(); //여러개 결과 받을 때
}
}
| INSERT | em.persist(member) |
| INSERT INTO Member (name) VALUES ('홍길동'); | |
| SELECT (pk로 하나만 조회) |
em.find(Member.class(), id) //(조회할 타입, 식별자pk) |
| SELECT * FROM Member WHERE id = ?; | |
| SELECT (나머지) |
createQuery("SELECT m FROM Member m WHERE m.name = :name") em.createQuery("select m from member m",Member.class) .getResultList(); |
| SELECT id, name FROM Member WHERE name = '홍길동'; | |
| UPDATE (변경 감지) | member.setName("이순신") |
| UPDATE Member SET name = '이순신' WHERE id = ?; | |
| UPDATE (JPQL 직접 실행) | createQuery("UPDATE Member m SET m.name = :name WHERE m.id = :id") |
| UPDATE Member SET name = '이순신' WHERE id = ?; | |
| DELETE | em.remove(member) |
| DELETE FROM Member WHERE id = ?; | |
| DELETE (JPQL 직접 실행) | createQuery("DELETE FROM Member m WHERE m.id = :id") |
| DELETE FROM Member WHERE id = ?; |
- 서비스 계층에 트랜잭션 추가
import org.springframework.transaction.annotation.Transactional
@Transactional
public class MemberService {}
해당 클래스 메서드를 실행할 때 트랜잭션 시작, 메서드가 정상 종료되면 트랜잭션 커밋, 런타임 예외가 발생하면 롤백
JPA를 통한 모든 데이터 변경은 트랜잭션 안에서 실행해야함.
-스프링 설정 변경
private final EntityManager em;
@Autowired
public SpringConfig(EntityManager em) {
this.em = em;
}
@Bean
public MemberRepository memberRepository() {
// return new MemoryMemberRepository();
// return new JdbcMemberRepository(dataSource);
// return new JdbcTemplateMemberRepository(dataSource);
return new JpaMemberRepository(em);
}
스프링 데이터 JPA
JPA를 더 편리하게 쓸수 있도록 한번 감싼 기술(그래서 JPA를 잘알아야함)
레포지토리에 구현 클래스 없이 인터페이스 만으로 개발가능
-JPA 설정을 그대로
-SpringDataJpaMemberRepository(인터페이스)
public interface SpringDataJpaMemberRepository extends JpaRepository<Member, Long>, MemberRepository {
//JpaRepository는 JPA에서 기본적인 CRUD 기능(findByIde등 메서드)을 제공하는 인터페이스, <엔티티 클래스, pk타입>
//이를 상속한 인터페이스는 Spring Data JPA가 자동으로 구현체를 생성하고, 이를 Spring Bean으로 등록
@Override
Optional<Member> findByName(String name);
}
-스프링 설정 변경
@Configuration
public class SpringConfig {
private final MemberRepository memberRepository;
@Autowired // 스프링 컨테이너에서 멤버 레포지터리 찾음
public SpringConfig(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Bean
public MemberService memberService() {
return new MemberService(memberRepository);
}
}
*스프링 데이터 JPA 제공 기능
- 인터페이스를 통한 기본적인 CRUD
- findByName(), findByEmail() 처럼 메서드 이름 만으로 조회 기능 제공
- 페이징 기능 자동 제공
참고: 복잡한 동적 쿼리는 Querydsl이라는 라이브러리 사용함
'Spring' 카테고리의 다른 글
| [Spring] 스프링 개요, SOLID, DI 컨테이너 💡 (0) | 2025.01.31 |
|---|---|
| [Spring] AOP(Aspect-Oriented Programming) (0) | 2025.01.22 |
| [Spring] From 입력, 조회 (0) | 2024.12.22 |
| [Spring] 스프링 빈 등록과 의존관계 (0) | 2024.12.19 |
| [Spring] 웹 계층 구조, Test (0) | 2024.12.17 |