관계형 데이터베이스 프로그래밍을 더 쉽게 할 수 있도록 도와주는 Java Framework
Database를 사용하는 이유
스프링부트는 자바로 만들어진 프레임워크
스프링부트 내에서 프로그래밍 언어를 사용할 때 Java 클래스(POJO)를 통해 데이터를 다룬다
Plain Old Java Object (POJO) an ordinary Java object not bound by any special restriction and not requiring any classpath used for increasing the readablility and reusability of a program basically defines an entity
a POJO should not extend prespecified classes (ex. public class Example extends javax.servlet.http.HttpServlet...) implement prespecified interfaces (ex. public class Example implements javax.ejb.EntityBean...) contain prespecified annotations (ex. @javax.persistence.Entity public class Example...)
<Example> no restriction on access-modifiers of fields (can be private, default, protected, or public) not necessary to include any constructor
// Employee POJO class to represent entity Employee
public class Employee {
// default field
String name;
// private salary
private double salary;
//arg-constructor to initialize fields
public Employee(String name, double salary){
this.name = name;
this.salary = salary;
}
// getter method for name
public String getName(){
return name;
}
// getter method for salary
public Double getSalary(){
return salary;
}
}
<Working example of the POJO class> Controllers interact with Business Logic Business Logic interacts with POJO to access the Database in example below, a database entity is represented by POJO (POJO has the same members as the database entity)
Java Beans Beans are special type of POJOs. restrictions on POJO to be a bean - all JavaBeans are POJOs but not all POJOs are JavaBeans - should implement Serializable interface - fields should be private (to provide complete control on fields) - fields should have getters or setters of both - should have a no-arg constructor (default constructor) - fields are accessed only by constructor or getter setters
POJO
Java Bean
doesn't have special restrictions other than those foced by Java language
a special POJO which haver some restrictions
doens't provide much control on members
provides complete control on members
can implement Serializable interface
should implement serializable interface
fields can bre accessed by their names
fiels only have private visibility
may/may not have a no-arg constuctor
should have a no-arg constructor
used when you don't want to give restriction on your members and give user complete access of your entity
used when you want to provide user your entity but only some part of your entity
mapper.xml 파일에서 작성한 SQL 선언문들을 각 mapper interface의 함수명으로 작성하면 xml에 쓰여진 SQL문과 연결됨
DTO 예시 (PostDto.java)
//id int
//title varchar
//content varchar
//writer varchar
//board int
public class PostDto {
private int id;
private String title;
private String content;
private String writer;
private int board;
}
DTO의 인자값들을 table의 column값과 이름을 동일하게 설정해야 함
board: joint table에서 사용하기 위한 board id
mapper interface 예시 (PostMapper.java)
mapper.xml파일에서 작성한 SQL문이 mapper namespace에 있는 함수와 연결지어짐
public interface PostMapper {
int createPost(PostDto dto);
PostDto readPost(int id);
List<PostDto> readAllPost();
int updatePost(PostDto dto);
int deletePost(int id);
}
void 대신 int를 사용하는 이유: insert, update, delete문의 경우 int를 사용. insert, update, delete의 경우 결과로써 몇 개의 row가 조작되었는지를 결과로 돌려주기 때문에 "몇 개의 row인가"가 int로서 결과로 return되기 때문.
mapper.xml 예시 (post-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="dev.summer.mybatis.mapper.PostMapper">
<insert id="createPost" parameterType="dev.summer.mybatis.dto.PostDto">
insert into POST(title, content, writer, board)
values (#{title}, #{content}, #{writer}, ${board})
<!-- #{title} == 'title' -->
<!-- ${title} == title -->
</insert>
<select id="readPost" parameterType="int" resultType="dev.summer.mybatis.dto.PostDto">
select * from post where id = ${id}
</select>
<select id="readAllPost" resultType="dev.summer.mybatis.dto.PostDto">
select * from post
</select>
<update id="updatePost" parameterType="dev.summer.mybatis.dto.PostDto">
update post set title = #{title},
content = #{content},
writer = #{writer},
board = ${board}
where id = ${id}
</update>
<delete id="deletePost" parameterType="dev.summer.mybatis.dto.PostDto">
delete from post where id = ${id}
</delete>
</mapper>
mapper namespace: 어떤 interface와 매치할지 결정하고
insert id: 어떤 함수와 아래 SQL문을 매치할지 결정함
parameterType: 인터페이스의 함수에서 받을 인자/parameter type을 의미
insert를 이용해 POST라는 table의 4개의 column(title, content, writer, board)에 values를 이용해서 각 값을 넣어준다 (SQL문과 동일하게 작성)
id의 경우 auto-increment이기 때문에 넣지 않음
#{변수명}: 문자열로 사용. parameterType으로 전달받은 dto Object의 멤버변수 중 일치하는 것을 가져옴
${변수명}: $로 선언할 경우 인자의 값이 그대로 SQL 선언문의 일부로서 작성됨
<select id="readPost" parameterType="int" resultType="dev.summer.mybatis.dto.PostDto">
select * from post where id = ${id}
</select>
<select id="readAllPost" resultType="dev.summer.mybatis.dto.PostDto">
select * from post
</select>
select * from post where id = ${id}
인자를 하나 가져올 때(readPost)는 database에서는 일반적으로 primary key를 사용함 > 예제의 경우 id
select * from post
result type의 결과물이 한 row가 아니라 table이기 때문 (readAllPost)
DAO 예시 (PostDao.java)
@Repository
public class PostDao {
private final SqlSessionFactory sessionFactory;
public PostDao(
@Autowired SqlSessionFactory sessionFactory
){
this.sessionFactory = sessionFactory;
}
public int createPost(PostDto dto){
SqlSession session = sessionFactory.openSession();
// 세션 오픈
// openSession(autoCommit: true/false): default는 true. select를 제외한 테이블에 데이터에 영향을 주는 것들에 대한 행위를 자동으로 저장할지
PostMapper mapper = session.getMapper(PostMapper.class);
// session에서 PostMapper(선언한 인터페이스 타입)와 동일한 구현체를 달라(getMapper)고 하면 PostMapper 인터페이스의 구현체가 mapper에 주입됨
// 이제 mapper는 PostMapper 인터페이스의 함수들을 사용 가능
int rowAffected = mapper.createPost(dto);
// 이 시점에서 데이터베이스와 통신 완료
session.close();
// 세션을 남기지 않는다(close)
return rowAffected;
}
}
PostDao: 실제로 mapper를 사용하여 통신을 하는 클래스
데이터베이스와 소통하기 위해 사용한 session은 통신을 마친 후 닫아줌. 데이터베이스가 또다른 곳과 연결될 때 session을 다시 활용하기 위함.
session을 열고닫으면서 mapper를 새로 받는 이유: mapper instance는 thread-safe하지 않기 때문에, 요청이 두번 연속적으로 빠르게 들어왔을 때 데이터베이스 통신이 길어졌을 경우 서로 다른 함수의 결과가 서로에게 영향을 미칠 수 있음
public int createPost(PostDto dto){
try (SqlSession session = sessionFactory.openSession()) {
PostMapper mapper = session.getMapper(PostMapper.class);
return mapper.createPost(dto);
Java 일정 버전 이상에서는 위와 같이 쓸 수도 있음. try with resources 형태로, try문의 {} 안에서만 ()안의 변수(session)가 살아있도록 만들어주는 문법.
SqlSession SqlSession은 하나의 Java interface SqlSession interface를 통해 명령을 실행하고(execute commands), 매퍼를 가져오고(get mappers), manage transactions할 수 있다. MyBatis에서는 SqlSessionFactory의 instance에 의해 SqlSession이 생성됨 SqlSessionFactory는 SqlSession들의 다양한 인스턴스를 만들기 위한 함수들을 포함하고 있음 SqlSessionFactory는 SqlSessionFactoryBuilder에 의해 생성되며, SqlSessionFactoryBuilder는 xml파일, 어노테이션, 또는 직접 작성한 java configuration으로부터 SqlSessionFactory를 생성할 수 있음 세션을 한번 생성하면 매핑된 구문을 실행하거나 연결(connection)을 커밋 또는 롤백하기 위해 사용하고, 더 이상 필요하지 않은 상태가 되면 세션을 닫는다. https://mybatis.org/mybatis-3/java-api.html
SQL 조건문을 mapper.xml에서 작성하는법
<select id="readPostQuery" parameterType="dev.summer.mybatis.dto.PostDto" resultType="dev.summer.mybatis.dto.PostDto">
select * from post
where title = #{title}
<if test="writer != null">
and writer = #{writer}
</if>
</select>
ddl-auto: 테이블을 생성하고 제거하는 과정을 자동으로 하는 것에 대한 옵션. 상용 환경에서는 create로 사용하지 않음 (일반적으로는 none 사용)
show-sql: jpa가 작동하면서 실제로 실행하는 sql문을 보여줄지 말지에 대한 설정
dialect: mysql을 사용한다는 것을 알려줌
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class PostEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String content;
private String writer;
}
jpa를 사용할 때는 primitive type보다는 클래스 기반의 object를 사용할 것 (ex. int 대신 Long)
@Id를 통해 JPA에 테이블의 primary key의 역할을 하는 변수를 알려줌
@GeneratedValue: id 생성하는 규칙을 설정하기 위한 어노테이션
@Entity
public class BoardEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(
targetEntity = PostEntity.class,
fetch = FetchType.LAZY,
mappedBy = "boardEntity"
)
private List<PostEntity> postEntityList = new ArrayList<>();
mappedBy: postEntity의 ManyToOne 관계에 정의된 boardEntity와 연결하기 위해 작성
RDB???
fetchtype
BaseEntity: 모든 entity들이 가져야 할 속성에 대해 정의하고 싶을 때. @MappedSuperclass @EntityListeners
CRUD에 데이터베이스 적용
Controller: front gateway
Service: business logic
Repository: data access
데이터를 주고받기 위한 객체(Dto)
Entity를 Dto 대신 사용하는 것은 좋지 않음
Entity는 데이터의 표현만 나타내는 것이기도 하지만, 그 외에도 내부 객체를 가지고 있음. 즉, 데이터 자체만 전송하는 것 이상으로 전달하는 데이터가 있다는 점이 약점. 따라서 단순한 CRUD operation에서 Entity를 Dto로 그대로 사용하는 것은 좋은 선택이 아님.