CRUD is four necessary functions of the storage application
- create
- read
- update
- delete
Today lets take a look at how to create a simple blog website using spring MVC.
- create Legacy Project
- Change Spring Version in pom.xml
- Add Spring test, Spring JDBC, Spring tx
- Change Junit Version
- Change Servlet Version
- Add HiakriCp, MyBatis, Mybatis-spring, Log4jdbc
- Add JDBC Driver into the project
- Lombok setting etc
Add Dependency from Maven Repository to pom.xml
Pros and cons of Hikari.
- Fast with a small number of the user( up to 16000)
- Can be slow with a big number of user
<!-- https://mvnrepository.com/artifact/com.zaxxer/HikariCP --><dependency><groupId>com.zaxxer</groupId><artifactId>HikariCP</artifactId><version>3.4.5</version></dependency>
Log4jdbc
- To print log.
- Useful for developers.
Here is the screen video of
- create a project
- add dependencies from maven repository
add dependencies from maven repository
spring tx: spring transaction 5.2.8
DB connect pool
-hikaricp
mybatis 3.5.5
mybatis spring 2.0.5
Lombok 1.18.12
- Bean
- Annotation — automatically generate getter and setter.
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.12</version><scope>provided</scope></dependency>
Log4Jdbc Log4j2 JDBC 4 1
<dependency><groupId>org.bgee.log4jdbc-log4j2</groupId><artifactId>log4jdbc-log4j2-jdbc4.1</artifactId><version>1.16</version></dependency>
- change server path
- set Language Filter(Korean)
→ Copy and paste the appropriate filter on the XML file
Oracle
1. create an account
2. Create a table and insert a row
create sequence seq_board;
create sequence seq_board;create table board(
bno number primary key,
title varchar2(100) not null,
content varchar2(2000)not null,
writer varchar2(50) not null,
regdate date default sysdate,
updatedate date default sysdate);
insert into board(bno,title, content, writer)values (seq_board.nextval,
'테스트 제목' ,'테스트내용' ,'user01');select * from baord;
select * from the board; here is a video of the process.
To connect to the database
connect pool
<!-- Root Context: defines shared resources visible to all other web components --><!-- resource적는거임 --><!-- Root Context: defines shared resources visible to all other web components --><bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig"><!-- <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/><property name="jdbcUrl" value="jdbc:oracle:thin:@localhost:1521:XE"/> --><property name="driverClassName"value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"></property><property name="jdbcUrl"value="jdbc:log4jdbc:oracle:thin:@localhost:1521:XE"></property><property name="username" value="spring" /><property name="password" value="1234" /></bean>
SQL session factory
<bean id="sqlSessionFactory"class="org.mybatis.spring.SqlSessionFactoryBean"><!-- datasource로 --><property name="dataSource" ref="dataSource" /></bean>
create a package to create a mapper.
Match the path with BoardMapper.java
→ Service → Controller → Return Result
controller.java
package org.knr.boardex001.controller;import org.knr.boardex001.domain.BoardVO;
import org.knr.boardex001.service.BoardService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;import lombok.AllArgsConstructor;
import lombok.extern.java.Log;
import oracle.net.ano.Service;@Controller
@Log
@RequestMapping("/board")
@AllArgsConstructor
public class BoardController {
private BoardService service;
@GetMapping("/list")
public void list(Model model) {
log.info("list..........!");
model.addAttribute("list",service.getList());
}
@GetMapping("/register")
public void register() {
}
@PostMapping("/register")
public String registerPro(BoardVO board,RedirectAttributes rttr) {
log.info("registerPro");
service.register(board);
rttr.addFlashAttribute("result", board.getBno());
return "redirect:list";
}
@GetMapping("/get")
public void get(Long bno,Model model) {
log.info("get");
model.addAttribute("board", service.get(bno));
}
@PostMapping("/update")
public String update(BoardVO board) {
log.info("update");
if(service.modify(board)) {
return "redirect:list";
}else {
return "redirect:get";
}
}
@GetMapping("/delete")
public String delete(Long bno) {
log.info("delete");
if(service.remove(bno)) {
return "redirect:list";
}else {
return "redirect:get";
}
}
}
Add objdbc6.jar
- project →build path →configure build path →Add external Jar
- Build path
project → properties→ deployment assembly→ add
→java build path entries
SET Data Context
- Write Resource
- Setting regards database
Domain Package
- same with oracle database content
“@Data”
package org.knr.boardex001.domain;import java.util.Date;
import lombok.Data;@Data
public class BoardVO {
private Long bno;
private String title;
private String content;
private Date regdate;
private Date updatedate;}
Mapper
- make as an interface
package org.knr.boardex001.mapper;import java.util.List;import org.apache.ibatis.annotations.Select;
import org.knr.boardex001.domain.BoardVO;//원래 결과arraylist에 넣던거를 이렇게 간단하게 할수있음
public interface BoardMapper {
@Select("select * from board where bno>0")
public List<BoardVO> getList();
let know the Location of Mybatis<the path of BoardMapper in my example>
in root-context.xml
<mybatis-spring:scan base-package="org.knr.boardex001.mapper"/>
Test database connection with test code
if it doesn’t work, try out the maven update
package org.knr.boardex001;import java.sql.Connection;import javax.sql.DataSource;import org.junit.Test;
import org.junit.runner.RunWith;
import org.knr.boardex001.domain.BoardVO;
import org.knr.boardex001.mapper.BoardMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import lombok.Setter;
import lombok.extern.log4j.Log4j;//junit가지고 테스트 할게요
@RunWith(SpringJUnit4ClassRunner.class)
//안에 db설정한 파일 설정한거
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
//로그 찍는거
//함수를 쓰려면 객체를 만들어야 되는데
// 객체를 가져와야 하는데 객체를 못 만들기 때문에@Log4j
public class BoardMapperTest {@Setter(onMethod_ = @Autowired)
private BoardMapper mapper;@Setter(onMethod_ = @Autowired)
private DataSource dataSource;@Test
public void testConnection() {try (Connection conn = dataSource.getConnection()) {log.info(conn);} catch (Exception e) {
e.printStackTrace();}
}// mapper에 getlist의 결과를 얻어와서 각각의 것을 board에 넣어서 log.info를 출력함
@Test
public void testGList() {mapper.getList().forEach(board -> log.info(board));
}@Test
public void testInsert() {BoardVO board = new BoardVO();
board.setTitle("새글 제목");
board.setContent("새글 내용");
board.setWriter("newUser");
mapper.insertSelectKey(board);
log.info(board);}@Test
public void testRead() {
// L을 붙이는 이유는 Long이라서
BoardVO board = mapper.read(1L);
log.info(board);
}@Test
public void testDelete() {
int n= mapper.delete(2l);
if(n==1) {
log.info("삭제성공");
}
}
@Test
public void testUpdate() {
BoardVO board = new BoardVO();
board.setBno(1L);
board.setTitle("수정제목");
board.setContent("수정내용");
board.setWriter("user01");
int n =mapper.update(board);
if(n==1) {
log.info("수정성공");
}
}}
Add Mybatis
get data source from the bottom of the root-context file
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
xsi:schemaLocation="http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"><!-- Root Context: defines shared resources visible to all other web components -->
<!-- resource적는거임 -->
<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
<!-- 데이터베이스,url, pw뭔지 -->
<property name="driverClassName"
value="oracle.jdbc.driver.OracleDriver" />
<property name="jdbcUrl"
value="jdbc:oracle:thin:@localhost:1521:XE"></property>
<property name="username" value="spring" />
<property name="password" value="1234" /></bean>
<!-- 실제로 resource얻을때 datasource로 얻음 이 안에 데이터 설정해 놓은 hikariConfig를 넣는다. -->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
<constructor-arg ref="hikariConfig" /></bean>
<!-- 하나 접속이 세션 그걸 생성하는 factorybean -->
<!-- mybatis는 sql로 이루어져 있고 , 접속하는bean을 만드는 factory -->
<!-- 원래는 field로 datasource를 들고 있음 -->
<!-- jsp로 할때는 없어도 되는데 지금은 my batis를 사용하기 때문에 필요함 -->
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- datasource로 -->
<property name="dataSource" ref="dataSource" /></bean>
<!-- mybatis가 어디 있는지 -->
<mybatis-spring:scan base-package="org.knr.boardex001.mapper"/>
<!-- 스캔하는 default package -->
<context:component-scan base-package="org.knr.boardex001"/>
</beans>
Connectpool
- Save the used one and bring it from the pool.
- Add JDBC Driver into the project.
Allow Eclipse to use Lombok.
This part is testing directly by Direct Injection.
@Setter(onMethod_=@Autowired)
private DataSource dataSource;
Inject this Bean
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
<constructor-arg ref="hikariConfig" /></bean>
Servlet Context
- Include beans managing web setting
- find controller location
- Logical view -> physical view
Root context
Settings are other than the web.
Make mapper with MyBatis
<?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">
우리가 만들 mybatis의 namespace위치
<mapper namespace="org.mybatis.example.BlogMapper">
<select id="selectBlog" resultType="Blog">
select * from Blog where id = #{id}
</select>
</mapper><?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="org.knr.boardex001.mapper.BoardMapper"><select id="getList"
resultType="org.knr.boardex001.domain.BoardVo">
<!-- where절을 넣지 않으면 index사용 안함 -->
select * from board
</select>
</mapper><?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="org.knr.boardex001.mapper.BoardMapper"><select id="getList"
resultType="org.knr.boardex001.domain.BoardVo">
<!-- where절을 넣지 않으면 index사용 안함 -->
select * from board
</select><insert id="insert">
insert into board(bno, title, content, writer)
values
(seq_board.nextval, #{title},#{content},#{writer})
</insert><insert id="insertSelectKey">
<selectKey keyProperty="bno" order="BEFORE"
resultType="long">
select seq_board.nextval from dual
</selectKey>insert into board(bno, title, content, writer)
values (#{bno},#{title},#{content},#{writer})</insert>
</mapper>
Insert test
- just have to create function because mapper exists
Business
- transaction
For example in the bank
Transaction of A and B are grouped as
- Money going out from A‘s account
- Money going into B’s account
if one of them fails the transaction itself fails.
We do the same here.
We group the service according to the use of the website.
- Create a Service package
2. create a service interface
package org.knr.boardex001.service;import java.util.List;import org.knr.boardex001.domain.BoardVO;
public interface BoardService {
//게시물 등록, 얻기 , 수정 , 삭제, 리스트 얻기
//인터페이스니까 추상 메소드로
public void register(BoardVO board);
public BoardVO get(Long bno);
public boolean modify(BoardVO board);
public boolean remove(Long bno);public List<BoardVO> getList();}
3. Implement interface and create a class
- import mapper
- add log and service
package org.knr.boardex001.service;import java.util.List;import org.knr.boardex001.domain.BoardVO;
import org.knr.boardex001.mapper.BoardMapper;
import org.springframework.stereotype.Service;import lombok.AllArgsConstructor;
import lombok.extern.java.Log;//로그 찍기 위해서
@Log
//자동으로dependency injection할거라서 component라는 걸 알려줘야 스캔을 해서 만들 수 있음
@Service
//서비스 객체 생성할때 field자동으로 다 만들어서 주입한다.
@AllArgsConstructor
public class BoardServicempl implements BoardService {
// mapper가 있어야함private BoardMapper boardMapper;@Override
public void register(BoardVO board) {
// TODO Auto-generated method stub
boardMapper.insert(board);
}@Override
public BoardVO get(Long bno) {
// TODO Auto-generated method stub
return boardMapper.read(bno);
}@Override
public boolean modify(BoardVO board) {
// TODO Auto-generated method stub
int n = boardMapper.update(board);
if (n == 1) {return true;
} else {
return false;
}}@Override
public boolean remove(Long bno) {
// TODO Auto-generated method stub
int n = boardMapper.delete(bno);
if (n == 1) {return true;
} else {
return false;
}
}@Override
public List<BoardVO> getList() {
// TODO Auto-generated method stub
return boardMapper.getList();
}}
allargsconstructor
create and inject field automatically when creating a service object
- autowire all mapper inside.
Create a controller
package org.knr.boardex001.controller;import org.knr.boardex001.domain.BoardVO;
import org.knr.boardex001.service.BoardService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;import lombok.AllArgsConstructor;
import lombok.extern.java.Log;
import oracle.net.ano.Service;@Controller
@Log
@RequestMapping("/board")
@AllArgsConstructor
public class BoardController {
private BoardService service;
@GetMapping("/list")
public void list(Model model) {
log.info("list..........!");
model.addAttribute("list",service.getList());
}
@GetMapping("/register")
public void register() {
}
@PostMapping("/register")
public String registerPro(BoardVO board,RedirectAttributes rttr) {
log.info("registerPro");
service.register(board);
rttr.addFlashAttribute("result", board.getBno());
return "redirect:list";
}
@GetMapping("/get")
public void get(Long bno,Model model) {
log.info("get");
model.addAttribute("board", service.get(bno));
}
@PostMapping("/update")
public String update(BoardVO board) {
log.info("update");
if(service.modify(board)) {
return "redirect:list";
}else {
return "redirect:get";
}
}
@GetMapping("/delete")
public String delete(Long bno) {
log.info("delete");
if(service.remove(bno)) {
return "redirect:list";
}else {
return "redirect:get";
}
}
}
2. list.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<table>
<tr>
<th>bno</th>
<th>title</th>
<th>writer</th>
<th>regdate</th>
<th>updatedate</th>
</tr><c:forEach items="${list }" var="board">
<tr>
<td>${board.bno }</td>
<td>${board.title }</td>
<td>${board.writer }</td>
<td><fmt:formatDate value="${board.regdate }" /> </td>
<td><fmt:formatDate value="${board.updatedate }" /></td></tr>
</c:forEach>
</table>
</body>
</html>
3.get.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="update" method="post">
<table>
<tr>
<th>번호</th>
<td><input type="text" name="bno" value="${board.bno}" readonly></td>
</tr>
<tr>
<th>제목</th>
<td><input type="text" name="title" value="${board.title}"></td>
</tr>
<tr>
<th>내용</th>
<td><textarea rows="5" cols="50" name="content">${board.content}</textarea></td>
</tr>
<tr>
<th>작성자</th>
<td><input type="text" name="writer" value="${board.writer}"></td>
</tr>
<tr>
<th>작성일</th>
<td>${board.regdate}"</td>
</tr>
<tr>
<th>수정일</th>
<td>${board.updatedate}</td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit" value="수정">
<input type="button" value="삭제"
onclick="location.href='delete?bno=${board.bno}'"> <input
type="reset" value="취소"></td>
</tr>
</table>
</form>
</body>
</html>
register.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="register" method="post">
<table>
<tr>
<th>제목</th>
<td><input type="text" name="title"></td>
</tr>
<tr>
<th>내용</th>
<td><textarea rows="5" cols="50" name="content"></textarea></td>
</tr>
<tr>
<th>작성자</th>
<td><input type="text" name="writer"></td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit" value="등록">
<input type="reset" value="취소"></td>
</tr>
</table>
</form>
</body>
</html>