들어가기 전에
이번 시간엔 방명록을 Spring 프레임워크를 이용해 만들어 보도록 하겠습니다.
이를 통해 각 레이어별로 어떤 내용들을 작성해야 하는지 알아보고, 완전히 동작하는 웹 어플리케이션을 개발해 봄으로써 Spring 웹 어플리케이션에 대한 이해를 높이는 시간이 될 수 있길 바랍니다.
학습 목표
- Spring 프레임워크를 이용한 웹 어플리케이션 프로젝트를 구성할 수 있습니다.
- Spring 프레임워크를 이용한 웹 어플리케이션 개발 시 어떤 요소들을 개발해야하는지 이해합니다.
핵심 개념
- @Controller
- @Service
- @Repository
- @Transactional
학습하기
실습코드
GuestbookService.java
package kr.or.connect.guestbook.service;
import java.util.List;
import kr.or.connect.guestbook.dto.Guestbook;
public interface GuestbookService {
public static final Integer LIMIT = 5;
public List<Guestbook> getGuestbooks(Integer start);
public int deleteGuestbook(Long id, String ip);
public Guestbook addGuestbook(Guestbook guestbook, String ip);
public int getCount();
}
GuestbookServiceImpl.java
package kr.or.connect.guestbook.service.impl;
import java.util.Date;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import kr.or.connect.guestbook.dao.GuestbookDao;
import kr.or.connect.guestbook.dao.LogDao;
import kr.or.connect.guestbook.dto.Guestbook;
import kr.or.connect.guestbook.dto.Log;
import kr.or.connect.guestbook.service.GuestbookService;
@Service
public class GuestbookServiceImpl implements GuestbookService{
@Autowired
GuestbookDao guestbookDao;
@Autowired
LogDao logDao;
@Override
@Transactional
public List<Guestbook> getGuestbooks(Integer start) {
List<Guestbook> list = guestbookDao.selectAll(start, GuestbookService.LIMIT);
return list;
}
@Override
@Transactional(readOnly=false)
public int deleteGuestbook(Long id, String ip) {
int deleteCount = guestbookDao.deleteById(id);
Log log = new Log();
log.setIp(ip);
log.setMethod("delete");
log.setRegdate(new Date());
logDao.insert(log);
return deleteCount;
}
@Override
@Transactional(readOnly=false)
public Guestbook addGuestbook(Guestbook guestbook, String ip) {
guestbook.setRegdate(new Date());
Long id = guestbookDao.insert(guestbook);
guestbook.setId(id);
Log log = new Log();
log.setIp(ip);
log.setMethod("insert");
log.setRegdate(new Date());
logDao.insert(log);
return guestbook;
}
@Override
public int getCount() {
return guestbookDao.selectCount();
}
}
GuestbookServiceTest.java
package kr.or.connect.guestbook.service.impl;
import java.util.Date;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import kr.or.connect.guestbook.config.ApplicationConfig;
import kr.or.connect.guestbook.dto.Guestbook;
import kr.or.connect.guestbook.service.GuestbookService;
public class GuestbookServiceTest {
public static void main(String[] args) {
ApplicationContext ac = new AnnotationConfigApplicationContext(ApplicationConfig.class);
GuestbookService guestbookService = ac.getBean(GuestbookService.class);
Guestbook guestbook = new Guestbook();
guestbook.setName("kang kyungmi22");
guestbook.setContent("반갑습니다. 여러분. 여러분이 재미있게 공부하고 계셨음 정말 좋겠네요^^22");
guestbook.setRegdate(new Date());
Guestbook result = guestbookService.addGuestbook(guestbook, "127.0.0.1");
System.out.println(result);
}
}
comment
A boolean flag that can be set to
true
if the transaction is effectively read-only, allowing for corresponding optimizations at runtime.Defaults to
false
.This just serves as a hint for the actual transaction subsystem; it will not necessarily cause failure of write access attempts. A transaction manager which cannot interpret the read-only hint will not throw an exception when asked for a read-only transaction but rather silently ignore the hint.
TransactionDefinition.isReadOnly()
,TransactionSynchronizationManager.isCurrentTransactionReadOnly()
빠른 복붙강의다.. 정신없다..
무엇보다 트랜잭션에 대한 설명과 이해과정이 인상적이었어요!
혹시 그대로 따라했는데
Error configuring application listener of class org.springframework.web.context.ContextLoaderListener
이런 에러가 뜬다면 아래 링크를 따라서 해보세요.
이거때문에 하루이상 프로젝트가 지연됬네요 ㅠㅠ
https://myblog.opendocs.co.kr/archives/1657
pom.xml을 많은 분들이 댓글을 통해서 올려주셨는데 이 댓글 양식 자체가 정렬이 안됩니다. 그래서 정렬에 어려움을 겪으시는 분들이 있으실텐데 xml 형식을 자동정렬하는 사이트를 올려드리겠습니다.
XML 자동 정렬 사이트 : http://chris.photobooks.com/xml/default.htm
XML INPUT 칸에다가 복사한 코드를 붙이신다음에(옵션을 비롯해서 다른건 안 건드리셔도 됩니다.) 옵션 밑에 Render버튼 누르시면 바로 자동정렬된 아웃풋이 나옵니다. 여러분의 시간은 소중하잖아요? ㅋㅋㅋ 포기하지 마시고 수강하시길
Exception in thread "main" org.springframework.dao.DataAccessResourceFailureException: Error retrieving database metadata;
이런 에러가 뜨고 디버깅 해보니까 Long id = guestbookDao.insert(guestbook);
여기서 오류가 발생하는거 같은데 그 이상은 잘 모르겠네요 문제가 뭘까요? 구글링해도 명확한 답이 안나오네요 ㅜㅜ
에러로그1. guestbook/list로 들어가지지 않음
pom.xml에 다음을 추가 (아래 댓글에서도 확인가능)
<!-- FOR JAVA 1.8 -->
<build>
<finalName>guestbook</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
에러로그2. database not found
: public GuestbookDao(DataSource dataSource) {
this.jdbc = new NamedParameterJdbcTemplate(dataSource);
this.insertAction = new SimpleJdbcInsert(dataSource)
.withTableName("guestbook")
.usingGeneratedKeyColumns("id");
}에서 테이블 이름을 "guestbook"이라고 안 주고 "guest"라고 줌.(긁어쓸걸..)
에러로그 3.
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type ~~
ServiceImpl의 패키지명이 ~~Service.impl로 안 돼 있고 ServiceImpl로 돼 있어서 오류가 났음.
정말 어이x;
jackson 라이브러리를 쓴적이 있었나요? 앞의 강의들 다봤었는데 쓴 기억이 안나네요
감사드립니당
Bean을 등록을 못해 에러가 발생하는데 위 소스코드 내에 componentscan이 맞는건가요?
위의 소스에서 DTO 클래스경우 @component같은 어노테이션을 지정해주지 않았는데 잘 실행이 되는 이유가 무엇인가요? 스프링 컨테이너가 알아서 등록해준건가요?
마지막 강의에서 write 처리는 강의에 나와있나요?? 갑자기 404 떠서 당황했는데 write 처리가 나온 적이 없었는데 밑에 코드에는 또 있네요...
No mapping found for HTTP request with URI [/guestbook/] in DispatcherServlet with name 'mvc'
이렇게 떠서.. 계속 헤매고 있네요. guestbook/list 로 뜨게 하는 부분 하고 있는데. 안되네요;; 같은 오류 겪으신 분 있으신가요.
GuestbookDaoTest하는 부분 잘 따라 한 것 같은데 uncategorized SQLException 에러라면서 Incorrect string value 나오는 분을 위해 글 남깁니다. mySql 초기 charset 설정이 utf8로 되어 있지 않을때 여기서는 utf8 로 보내는데 받을 때는 다른 charset으로 받아서 발생하는 에러고요 해결 방법은 간단합니다.
일단
drop table guestbook;
drop table log;
사용해서 테이블을 지우고요
set names utf8;
하셔서 데이터베이스 기본 charset 설정을 utf8;로 변경하세요.
이후 다시 테이블을 만들면, 테이블 생성시 charset을 명시해주는 경우를 제외하고 테이블의 charset은 데이터베이스의 charset을 따라 생성되기에 utf8로 테이블이 만들어져 문제가 발생하지 않을 겁니다
열공하세요~
참고 : https://bstar36.tistory.com/307
계속 404에러 떴다가 500에러 떴다가 한참 고생했네요. 아래 frontdev님 댓글이 도움이 되었습니다. spring 5.x.x라고 적으셨던 분들 frontdev님 댓글 참고하셔서 붙여넣으시고, WebMvcContextConfiguration.java 파일 잘 확인하신 뒤 해보세요.
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>kr.or.connect</groupId>
<artifactId>guestbook</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>guestbook Maven Webapp</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>4.3.5.RELEASE</spring.version>
<!-- jackson -->
<jackson2.version>2.8.6</jackson2.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Servlet JSP JSTL -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- spring jdbc & jdbc driver & connection pool -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
</dependency>
<!-- basic data source -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.1.1</version>
</dependency>
<!-- Jackson Module -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson2.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
<version>${jackson2.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
GuestbookServiceTest를 하는데 addGuestbook에서 중간에 강의와 똑같이 setIp를 빼놓고 실행했는데 guestbook rollback이 진행되지 않습니다. readonly=false를 했는데두요
@Override
@Transactional(readOnly=false)
public Guestbook addGuestbook(Guestbook guestbook, String ip) {
guestbook.setRegdate(new Date());
Long id = guestbookDao.insert(guestbook);
guestbook.setId(id);
Log log = new Log();
log.setId(id); // 여기서 에러
log.setMethod("insert");
log.setRegdate(new Date());
logDao.insert(log);
return guestbook;
}
rollback을 자동으로 해주어서 좋은 기능이네! 하고 써보니 안돼서 여쭤봅니다
덧붙여서 일부러 RuntimeException을 내도 안되구요, Transctional에다가 rollbackfor=runtimeexception을 해도 rollback이 안되네요ㅠㅠ
GuestbookServiceImpl 에서 getCount의 경우에는 Transactionl 을 해주지 않아도 괜찮은가요?
심각: Error configuring application listener of class org.springframework.web.context.ContextLoaderListener
http://myblog.opendocs.co.kr/archives/1657
혹시 Unable to load authentication plugin 'caching_sha2_password' 에러 나시는분들
pom.xml에서 mysql 버전을 현재 갖고계신 mysql server버전과 같게 맞춰주면 에러가 해결됩니다.