diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/.gitignore" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/.gitignore" new file mode 100644 index 0000000..c2065bc --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/.gitignore" @@ -0,0 +1,37 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/build.gradle" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/build.gradle" new file mode 100644 index 0000000..346aec8 --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/build.gradle" @@ -0,0 +1,33 @@ +plugins { + id 'org.springframework.boot' version '2.5.4' + id 'io.spring.dependency-management' version '1.0.11.RELEASE' + id 'java' +} + +group = 'tobi' +version = '0.0.1-SNAPSHOT' +sourceCompatibility = '11' + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter' + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'javax.inject:javax.inject:1' + implementation group: 'org.springframework', name: 'spring-oxm', version: '3.0.4.RELEASE' + implementation 'org.springframework.boot:spring-boot-starter-mail' + implementation 'org.springframework.boot:spring-boot-starter-jdbc' + implementation 'junit:junit:4.13.1' + implementation 'junit:junit:4.13.1' + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + testCompileOnly 'org.projectlombok:lombok' + testAnnotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} + +test { + useJUnitPlatform() +} diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/gradle/wrapper/gradle-wrapper.jar" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/gradle/wrapper/gradle-wrapper.jar" new file mode 100644 index 0000000..7454180 Binary files /dev/null and "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/gradle/wrapper/gradle-wrapper.jar" differ diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/gradle/wrapper/gradle-wrapper.properties" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/gradle/wrapper/gradle-wrapper.properties" new file mode 100644 index 0000000..05679dc --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/gradle/wrapper/gradle-wrapper.properties" @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/gradlew" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/gradlew" new file mode 100644 index 0000000..744e882 --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/gradlew" @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MSYS* | MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/gradlew.bat" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/gradlew.bat" new file mode 100644 index 0000000..107acd3 --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/gradlew.bat" @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/settings.gradle" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/settings.gradle" new file mode 100644 index 0000000..ebf1ef8 --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/settings.gradle" @@ -0,0 +1 @@ +rootProject.name = 'spring' diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/Application.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/Application.java" new file mode 100644 index 0000000..3bcbfc7 --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/Application.java" @@ -0,0 +1,13 @@ +package tobi.spring; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/aop/NameMatchClassPointcut.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/aop/NameMatchClassPointcut.java" new file mode 100644 index 0000000..b0dc3fa --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/aop/NameMatchClassPointcut.java" @@ -0,0 +1,28 @@ +package tobi.spring.aop; + +import org.springframework.aop.ClassFilter; +import org.springframework.aop.support.NameMatchMethodPointcut; +import org.springframework.util.PatternMatchUtils; + +public class NameMatchClassPointcut extends NameMatchMethodPointcut { + + public void setMappedClassName(String mappedClassName) { + this.setClassFilter(new SimpleClassFilter(mappedClassName)); + } + + static class SimpleClassFilter implements ClassFilter { + + String mappedName; + public SimpleClassFilter(String mappedName) { + // TODO Auto-generated constructor stub + this.mappedName =mappedName; + } + + @Override + public boolean matches(Class clazz) { + // TODO Auto-generated method stub + return PatternMatchUtils.simpleMatch(mappedName, clazz.getSimpleName()); + } + + } +} \ No newline at end of file diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/aop/TransactionAdvice.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/aop/TransactionAdvice.java" new file mode 100644 index 0000000..ce152cf --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/aop/TransactionAdvice.java" @@ -0,0 +1,33 @@ +package tobi.spring.aop; + +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.DefaultTransactionDefinition; + +public class TransactionAdvice implements MethodInterceptor { + + PlatformTransactionManager transactionManager; + + public void setTransactionManager(PlatformTransactionManager transactionManager) { + this.transactionManager = transactionManager; + } + + @Override + public Object invoke(MethodInvocation invocation) throws Throwable { + // TODO Auto-generated method stub + TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition()); + + try { + Object ret = invocation.proceed(); + transactionManager.commit(status); + return ret; + + } catch(RuntimeException e) { + transactionManager.rollback(status); + throw e; + } + } + +} diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/config/Appcontext.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/config/Appcontext.java" new file mode 100644 index 0000000..13fb9d6 --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/config/Appcontext.java" @@ -0,0 +1,102 @@ +package tobi.spring.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.*; +import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.jdbc.datasource.SimpleDriverDataSource; +import org.springframework.mail.MailSender; +import org.springframework.mail.javamail.JavaMailSenderImpl; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.EnableTransactionManagement; +import tobi.spring.domain.UserDaoJdbc; +import tobi.spring.mail.DummyMailSender; +import tobi.spring.service.UserService; + +import javax.sql.DataSource; +import java.sql.Driver; + +@ComponentScan( basePackages = {"com.tobi.domain", "com.tobi.service"}) +@EnableTransactionManagement +@Configuration +@EnableSqlService +@PropertySource("/database.properties") +public class AppContext implements SqlMapConfig{ + + + @Autowired + UserDaoJdbc userDao; + + + @Value("${db.driverClass}") Class driverClass; + @Value("${db.url}") String url; + @Value("${db.username}") String username; + @Value("${db.password}") String password; + + @Bean + public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() { + return new PropertySourcesPlaceholderConfigurer(); + } + + + @Bean + public DataSource dataSource() { + + SimpleDriverDataSource dataSource = new SimpleDriverDataSource(); + dataSource.setDriverClass(driverClass); + dataSource.setUrl(url); + dataSource.setUsername(username); + dataSource.setPassword(password); + + return dataSource; + } + + @Bean + public PlatformTransactionManager transactionManager() { + DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(); + transactionManager.setDataSource(dataSource()); + + return transactionManager; + } + + + + @Configuration + @Profile("production") + public static class ProductionAppContext { + + @Bean + public MailSender mailSender() { + + JavaMailSenderImpl mailSender = new JavaMailSenderImpl(); + mailSender.setHost("mail.mycompany.com"); + return mailSender; + } + } + + @Configuration + @Profile("test") + public static class TestAppContext { + + @Bean + public UserService testUserService() { + return new TestUserServiceImpl(); + } + + @Bean + public MailSender mailSender() { + return new DummyMailSender(); + } + } + + @Override + public Resource getSqlMapResource() { + // TODO Auto-generated method stub + return new ClassPathResource("sqlmap.xml", UserDaoJdbc.class); + } + + +} \ No newline at end of file diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/config/EnableSqlService.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/config/EnableSqlService.java" new file mode 100644 index 0000000..b786dea --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/config/EnableSqlService.java" @@ -0,0 +1,8 @@ +package tobi.spring.config; + +import org.springframework.context.annotation.Import; + +@Import(value = SqlServiceContext.class) +public @interface EnableSqlService { + +} diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/config/SqlMapConfig.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/config/SqlMapConfig.java" new file mode 100644 index 0000000..d828548 --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/config/SqlMapConfig.java" @@ -0,0 +1,7 @@ +package tobi.spring.config; + +import org.springframework.core.io.Resource; + +public interface SqlMapConfig { + Resource getSqlMapResource(); +} diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/config/SqlServiceContext.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/config/SqlServiceContext.java" new file mode 100644 index 0000000..ec05730 --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/config/SqlServiceContext.java" @@ -0,0 +1,53 @@ +package tobi.spring.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; +import org.springframework.oxm.Unmarshaller; +import org.springframework.oxm.jaxb.Jaxb2Marshaller; +import tobi.spring.sql.SqlService; + +import javax.sql.DataSource; + +@Configuration +public class SqlServiceContext { + + @Autowired + SqlMapConfig sqlMapConfig; + + @Bean + public SqlService sqlService() { + OxmSqlService sqlService = new OxmSqlService(); + sqlService.setUnmarshaller(unmarshaller()); + //sqlService.setSqlRegistry(sqlRegistry()); + sqlService.setSqlMapFile(this.sqlMapConfig.getSqlMapResource()); + return sqlService; + } + +// @Bean +// public SqlRegistry sqlRegistry() { +// EmbeddedDbSqlRegistry sqlRegistry = new EmbeddedDbSqlRegistry(); +// sqlRegistry.setDataSource(embeddedDatabase()); +// return sqlRegistry; +// } + + + @Bean + public Unmarshaller unmarshaller() { + Jaxb2Marshaller unmarshaller = new Jaxb2Marshaller(); + unmarshaller.setContextPath("com.tobi.domain.sql.jaxb"); + return unmarshaller; + } + + @Bean + public DataSource embeddedDatabase() { + + return new EmbeddedDatabaseBuilder() + .setName("embeddedDatabase") + .setType(EmbeddedDatabaseType.HSQL) + .addScript("classpath:com/tobi/test/sqlRegistrySchema.sql") + .build(); + } +} diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/domain/Level.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/domain/Level.java" new file mode 100644 index 0000000..7b9a53f --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/domain/Level.java" @@ -0,0 +1,37 @@ +package tobi.spring.domain; + +public enum Level { + //순서 고려 + GOLD(3,null), + SILVER(2,GOLD), + BASIC(1,SILVER); + + + + private final int value; + private final Level next; + + Level(int value, Level next) { + this.value = value; + this.next = next; + } + + public int intValue(){ + return value; + } + + public Level nextLevel() { + return this.next; + } + + public static Level valueOf(int value){ + + switch (value){ + case 1: return BASIC; + case 2: return SILVER; + case 3: return GOLD; + default: + throw new AssertionError("Unknown value : " + value); + } + } +} diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/domain/User.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/domain/User.java" new file mode 100644 index 0000000..c482f9a --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/domain/User.java" @@ -0,0 +1,100 @@ +package tobi.spring.domain; + +import lombok.*; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@ToString +@EqualsAndHashCode +public class User { + + private String id; + private String name; + private String password; + + Level level; + int login; + int recommend; + private String mail; + + public User(String id, String name, String password, Level level, int login, int recommend, String mail) { + this.id = id; + this.name = name; + this.password = password; + this.level = level; + this.login = login; + this.recommend = recommend; + this.mail = mail; + } + + //Level의 순서를 userService가 알아야하는 이유는 없다. + //또한 User에게 업그레이드 하라고 요청하는 편도 괜찮다. + public void upgradeLevel() { + Level nextLevel = this.level.nextLevel(); + + if(nextLevel == null) { + throw new IllegalStateException(this.level + "은 업그레이드가 불가능 합니다."); + } + else{ + this.level =nextLevel; + } + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public Level getLevel() { + return level; + } + + public void setLevel(Level level) { + this.level = level; + } + + public int getLogin() { + return login; + } + + public void setLogin(int login) { + this.login = login; + } + + public int getRecommend() { + return recommend; + } + + public void setRecommend(int recommend) { + this.recommend = recommend; + } + + public String getMail() { + return mail; + } + + public void setMail(String mail) { + this.mail = mail; + } +} diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/domain/UserDao.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/domain/UserDao.java" new file mode 100644 index 0000000..35f22c7 --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/domain/UserDao.java" @@ -0,0 +1,12 @@ +package tobi.spring.domain; + +import java.util.List; + +public interface UserDao { + void add(User user); + User get(String id); + List getAll(); + void deleteAll(); + int getCount(); + int update(User user); +} diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/domain/UserDaoJdbc.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/domain/UserDaoJdbc.java" new file mode 100644 index 0000000..8d4096c --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/domain/UserDaoJdbc.java" @@ -0,0 +1,89 @@ +package tobi.spring.domain; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import tobi.spring.exception.DuplicateUserIdException; +import tobi.spring.sql.SqlService; + +import javax.sql.DataSource; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; + +public class UserDaoJdbc implements UserDao { + + private JdbcTemplate jdbcTemplate; + + public UserDaoJdbc() { + // TODO Auto-generated constructor stub + } + + @Autowired + private SqlService sqlService; + + @Autowired + public void setDataSource(DataSource dataSource) { + + this.jdbcTemplate = new JdbcTemplate(dataSource); + } + + private RowMapper userMapper= new RowMapper() { + + @Override + public User mapRow(ResultSet rs, int arg1) throws SQLException { + // TODO Auto-generated method stub + User user = new User(); + user.setId(rs.getString("id")); + user.setName(rs.getString("name")); + user.setPassword(rs.getString("password")); + user.setLevel(Level.valueOf(rs.getInt("level"))); + user.setLogin(rs.getInt("login")); + user.setRecommend(rs.getInt("recommend")); + user.setMail(rs.getString("email")); + + return user; + } + }; + + @Override + public void add( final User user) { + + this.jdbcTemplate.update(this.sqlService.getSql("userAdd"), + user.getId(), user.getName(), user.getPassword(), user.getLevel().intValue(), user.getLogin(), user.getRecommend(), user.getMail()); + + } + + @Override + public User get(String id) { + + return this.jdbcTemplate.queryForObject( this.sqlService.getSql("userGet"), new Object[] {id}, this.userMapper); + } + + @Override + public void deleteAll() { + this.jdbcTemplate.update(this.sqlService.getSql("userDeleteAll")); + } + + @Override + public int getCount() { + + return this.jdbcTemplate.queryForInt( this.sqlService.getSql("userGetCount")); + + } + + @Override + public List getAll(){ + + return this.jdbcTemplate.query(this.sqlService.getSql("userGetAll"), this.userMapper); + } + + @Override + public int update(User user) { + // TODO Auto-generated method stub + return this.jdbcTemplate.update(this.sqlService.getSql("userUpdate") + , user.getName(), user.getPassword(), user.getLevel().intValue(), user.getLogin(), user.getRecommend(), user.getMail(), user.getId()); + } + + +} diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/exception/DuplicateUserIdException.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/exception/DuplicateUserIdException.java" new file mode 100644 index 0000000..1ac10e4 --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/exception/DuplicateUserIdException.java" @@ -0,0 +1,8 @@ +package tobi.spring.exception; + +public class DuplicateUserIdException extends RuntimeException{ + + public DuplicateUserIdException(Throwable cause) { + super(cause); + } +} \ No newline at end of file diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/mail/DummyMailSender.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/mail/DummyMailSender.java" new file mode 100644 index 0000000..637ea7c --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/mail/DummyMailSender.java" @@ -0,0 +1,21 @@ +package tobi.spring.mail; + +import org.springframework.mail.MailException; +import org.springframework.mail.MailSender; +import org.springframework.mail.SimpleMailMessage; + +public class DummyMailSender implements MailSender { + + @Override + public void send(SimpleMailMessage arg0) throws MailException { + // TODO Auto-generated method stub + + } + + @Override + public void send(SimpleMailMessage[] arg0) throws MailException { + // TODO Auto-generated method stub + + } + +} \ No newline at end of file diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/proxy/Hello.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/proxy/Hello.java" new file mode 100644 index 0000000..b52b511 --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/proxy/Hello.java" @@ -0,0 +1,7 @@ +package tobi.spring.proxy; + +public interface Hello { + String sayHello(String name); + String sayHi(String name); + String sayThankYou(String name); +} diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/proxy/HelloTarget.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/proxy/HelloTarget.java" new file mode 100644 index 0000000..f38629f --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/proxy/HelloTarget.java" @@ -0,0 +1,24 @@ +package tobi.spring.proxy; + +public class HelloTarget implements Hello{ + + @Override + public String sayHello(String name) { + // TODO Auto-generated method stub + return "Hello " + name; + } + + @Override + public String sayHi(String name) { + // TODO Auto-generated method stub + return "Hi " + name; + } + + @Override + public String sayThankYou(String name) { + // TODO Auto-generated method stub + return "Thank You " + name; + } + +} + diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/proxy/TransactionHandler.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/proxy/TransactionHandler.java" new file mode 100644 index 0000000..732c1e4 --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/proxy/TransactionHandler.java" @@ -0,0 +1,59 @@ +package tobi.spring.proxy; + +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.DefaultTransactionDefinition; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class TransactionHandler implements InvocationHandler { + + private Object target; + private PlatformTransactionManager transactionManager; + //트랜잭션을 적용할 메서드 이름 패턴 + private String pattern; + + public void setTarget(Object target) { + this.target = target; + } + + public void setTransactionManager(PlatformTransactionManager transactionManager) { + this.transactionManager = transactionManager; + } + + public void setPattern(String pattern) { + this.pattern = pattern; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] arg) throws Throwable { + + if(method.getName().startsWith(pattern)) { + return invokeInTransaction(method, arg); + } else { + return method.invoke(target, arg); + } + + } + + private Object invokeInTransaction(Method method, Object[] args) throws Throwable { + TransactionStatus status = + this.transactionManager.getTransaction(new DefaultTransactionDefinition()); + + try { + Object object = method.invoke(target, args); + this.transactionManager.commit(status); + return object; + + } catch(InvocationTargetException e) { + + this.transactionManager.rollback(status); + throw e.getTargetException(); + } + + } + + +} diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/proxy/TxProxyFactoryBean.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/proxy/TxProxyFactoryBean.java" new file mode 100644 index 0000000..db31578 --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/proxy/TxProxyFactoryBean.java" @@ -0,0 +1,64 @@ +package tobi.spring.proxy; + +import org.springframework.beans.factory.FactoryBean; +import org.springframework.transaction.PlatformTransactionManager; + +import java.lang.reflect.Proxy; + +public class TxProxyFactoryBean implements FactoryBean { + + Object target; + PlatformTransactionManager transactionManager; + String pattern; + Class serviceInterface; + + public void setTarget(Object target) { + this.target = target; + } + + public void setTransactionManager(PlatformTransactionManager transactionManager) { + this.transactionManager = transactionManager; + } + + public void setPattern(String pattern) { + this.pattern = pattern; + } + + public void setServiceInterface(Class serviceInterface) { + this.serviceInterface = serviceInterface; + } + + @Override + public Object getObject() throws Exception { + // 트랜잭션을 위한 InvocationHandler 생성 + TransactionHandler txHandler = new TransactionHandler(); + // target + txHandler.setTarget(target); + // PlatformTransactionManager의 구현체인 DataSourceTransasctionManager 를 DI 받아 주입시킴 + txHandler.setTransactionManager(transactionManager); + // 메서드 이름 + txHandler.setPattern(pattern); + + // 1. 프록시 팩토리에 다이내믹 프록시 요청 + // 2. serviceInterface 에 따른 구현 다이내믹 프록시 생성 + // 3. txHandler 가 target 을 제어 + return Proxy.newProxyInstance( + getClass().getClassLoader() + , new Class[] {serviceInterface} + , txHandler); + } + + @Override + public Class getObjectType() { + // TODO Auto-generated method stub + return serviceInterface; + } + + @Override + public boolean isSingleton() { + // 싱글톤 빈이 아니라는 뜻이 아님. + // getObject() 가 매번 같은 오브젝트를 리턴하지 않는다는 뜻. + return false; + } + +} diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/proxy/UppercaseHandler.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/proxy/UppercaseHandler.java" new file mode 100644 index 0000000..b492088 --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/proxy/UppercaseHandler.java" @@ -0,0 +1,26 @@ +package tobi.spring.proxy; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; + +public class UppercaseHandler implements InvocationHandler { + + Object target; + + public UppercaseHandler(Object target) { + // TODO Auto-generated constructor stub + this.target = target; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + // TODO Auto-generated method stub + Object object = method.invoke(target, args); + + if(object instanceof String) { + return ((String) object).toUpperCase(); + } + return object; + } + +} diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/service/BaseSqlService.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/service/BaseSqlService.java" new file mode 100644 index 0000000..8a9cf95 --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/service/BaseSqlService.java" @@ -0,0 +1,30 @@ +package tobi.spring.service; + +import tobi.spring.sql.*; + +import javax.annotation.PostConstruct; + +public class BaseSqlService implements SqlService { + protected SqlReader sqlReader; + protected SqlRegistry sqlRegistry; + + public void setSqlReader(SqlReader sqlReader) { + this.sqlReader = sqlReader; + } + public void setSqlRegistry(SqlRegistry sqlRegistry) { + this.sqlRegistry = sqlRegistry; + } + + @PostConstruct + public void loadSql() { + this.sqlReader.read(this.sqlRegistry); + } + + @Override + public String getSql(String key) throws SqlRetirevalFailureException { + // TODO Auto-generated method stub + try { return this.sqlRegistry.findSql(key);} + catch(SqlNotFoundException e) { throw new SqlRetirevalFailureException(e);} + } + +} \ No newline at end of file diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/service/UserService.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/service/UserService.java" new file mode 100644 index 0000000..ec5cbed --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/service/UserService.java" @@ -0,0 +1,20 @@ +package tobi.spring.service; + +import org.springframework.transaction.annotation.Transactional; +import tobi.spring.domain.User; + +import java.util.List; + +public interface UserService { + + void add(User user); + + @Transactional(readOnly = true) + User get(String id); + @Transactional(readOnly = true) + List getAll(); + void deleteAll(); + void update(User user); + + void upgradeLevels(); +} \ No newline at end of file diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/service/UserServiceImpl.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/service/UserServiceImpl.java" new file mode 100644 index 0000000..93e3467 --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/service/UserServiceImpl.java" @@ -0,0 +1,99 @@ +package tobi.spring.service; + +import lombok.Setter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.mail.MailSender; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.stereotype.Service; +import tobi.spring.domain.Level; +import tobi.spring.domain.User; +import tobi.spring.domain.UserDao; +import tobi.spring.domain.UserDaoJdbc; + +import java.util.List; + +@Setter +@Service("userService") +public class UserServiceImpl implements UserService{ + + @Autowired + UserDao userDao; + + @Autowired + MailSender mailSender; + + public static final int MIN_LOGCOUNT_FOR_SILVER = 50; + public static final int MIN_RECOMMEND_FOR_GOLD = 30; + + public void setUserDao(UserDao userDao) { + this.userDao = userDao; + } + public void setMailSender(MailSender mailSender2) { + this.mailSender = mailSender2; + } + + @Override + public void add(User user) { + // TODO Auto-generated method stub + if(user.getLevel() == null) {user.setLevel(Level.BASIC);} + + userDao.add(user); + } + + @Override + public void upgradeLevels() { + List users = userDao.getAll(); + for(User user: users) { + if(canUpgradeLevel(user)) { + upgradeLevel(user); + } + } + } + + private boolean canUpgradeLevel(User user) { + + Level currentLevel = user.getLevel(); + switch (currentLevel) { + case BASIC: return (user.getLogin() >= MIN_LOGCOUNT_FOR_SILVER); + case SILVER: return (user.getRecommend() >= MIN_RECOMMEND_FOR_GOLD); + case GOLD: return false; + default: throw new IllegalArgumentException("Unknown Level: " + currentLevel); + } + } + + protected void upgradeLevel(User user) { + user.upgradeLevel(); + userDao.update(user); + sendUpgradeEmail(user); + } + + private void sendUpgradeEmail(User user) { + SimpleMailMessage mailMessage = new SimpleMailMessage(); + mailMessage.setTo(user.getMail()); + mailMessage.setFrom("useradmin@ksug.org"); + mailMessage.setSubject("Upgrade 안내"); + mailMessage.setText("사용자님의 등급이 " + user.getLevel().name() + "으로 변경되었습니다"); + + this.mailSender.send(mailMessage); + } + @Override + public User get(String id) { + // TODO Auto-generated method stub + return userDao.get(id); + } + @Override + public List getAll() { + // TODO Auto-generated method stub + return userDao.getAll(); + } + @Override + public void deleteAll() { + // TODO Auto-generated method stub + userDao.deleteAll(); + } + @Override + public void update(User user) { + // TODO Auto-generated method stub + userDao.update(user); + } +} \ No newline at end of file diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/service/UserServiceTx.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/service/UserServiceTx.java" new file mode 100644 index 0000000..fc46a3b --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/service/UserServiceTx.java" @@ -0,0 +1,37 @@ +package tobi.spring.service; + +import lombok.Setter; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.DefaultTransactionDefinition; +import tobi.spring.domain.User; + +@Setter +public class UserServiceTx implements UserService { + + UserService userService; + PlatformTransactionManager tr; + + + @Override + public void add(User user) { + userService.add(user); + } + + @Override + public void upgradeLevels() { + TransactionStatus status = this.tr.getTransaction(new DefaultTransactionDefinition()); + + try { + userService.upgradeLevels(); + + this.tr.commit(status); + } catch (RuntimeException e) { + this.tr.rollback(status); + throw e; + } + + + } + +} \ No newline at end of file diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/sql/HashMapSqlRegistry.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/sql/HashMapSqlRegistry.java" new file mode 100644 index 0000000..13b8448 --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/sql/HashMapSqlRegistry.java" @@ -0,0 +1,27 @@ +package tobi.spring.sql; + +import java.util.HashMap; +import java.util.Map; + +public class HashMapSqlRegistry implements SqlRegistry{ + + private Map sqlMap = new HashMap<>(); + + @Override + public void registerSql(String key, String sql) { + // TODO Auto-generated method stub + sqlMap.put(key, sql); + } + + @Override + public String findSql(String key) throws SqlNotFoundException { + // TODO Auto-generated method stub + String sql = this.sqlMap.get(key); + if(sql == null) { + throw new SqlNotFoundException(); + } else { + return sql; + } + } + +} diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/sql/SqlNotFoundException.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/sql/SqlNotFoundException.java" new file mode 100644 index 0000000..877bd58 --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/sql/SqlNotFoundException.java" @@ -0,0 +1,16 @@ +package tobi.spring.sql; + +public class SqlNotFoundException extends RuntimeException{ + + public SqlNotFoundException() { + // TODO Auto-generated constructor stub + } + + public SqlNotFoundException(String msg) { + super(msg); + } + + public SqlNotFoundException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/sql/SqlReader.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/sql/SqlReader.java" new file mode 100644 index 0000000..c09dac6 --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/sql/SqlReader.java" @@ -0,0 +1,6 @@ +package tobi.spring.sql; + +public interface SqlReader { + //SQL 을 외부에서 가져와 SqlRegistry에 등록. + void read(SqlRegistry sqlRegistry); +} diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/sql/SqlRegistry.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/sql/SqlRegistry.java" new file mode 100644 index 0000000..b467fe2 --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/sql/SqlRegistry.java" @@ -0,0 +1,6 @@ +package tobi.spring.sql; + +public interface SqlRegistry { + void registerSql(String key, String sql); + String findSql(String key) throws SqlNotFoundException; +} diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/sql/SqlRetirevalFailureException.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/sql/SqlRetirevalFailureException.java" new file mode 100644 index 0000000..4e5397e --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/sql/SqlRetirevalFailureException.java" @@ -0,0 +1,18 @@ +package tobi.spring.sql; + +public class SqlRetirevalFailureException extends RuntimeException { + public SqlRetirevalFailureException(String msg) { + // TODO Auto-generated constructor stub + super(msg); + + } + + public SqlRetirevalFailureException(String msg, Throwable cause) { + // TODO Auto-generated constructor stub + super(msg, cause); + } + + public SqlRetirevalFailureException(Throwable cause) { + super(cause); + } +} diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/sql/SqlService.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/sql/SqlService.java" new file mode 100644 index 0000000..dcee421 --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/java/tobi/spring/sql/SqlService.java" @@ -0,0 +1,5 @@ +package tobi.spring.sql; + +public interface SqlService { + String getSql(String key) throws SqlRetirevalFailureException; +} diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/resources/application.properties" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/resources/application.properties" new file mode 100644 index 0000000..8b13789 --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/main/resources/application.properties" @@ -0,0 +1 @@ + diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/test/java/tobi/spring/ApplicationTests.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/test/java/tobi/spring/ApplicationTests.java" new file mode 100644 index 0000000..9867392 --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/test/java/tobi/spring/ApplicationTests.java" @@ -0,0 +1,13 @@ +package tobi.spring; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class ApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/test/java/tobi/spring/domain/UserDaoTest.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/test/java/tobi/spring/domain/UserDaoTest.java" new file mode 100644 index 0000000..4f660dd --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/test/java/tobi/spring/domain/UserDaoTest.java" @@ -0,0 +1,168 @@ +package tobi.spring.domain; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.dao.DataAccessException; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.sql.SQLException; +import java.util.List; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +@RunWith(SpringJUnit4ClassRunner.class) +//@ContextConfiguration(classes = AppContext.class) +@ActiveProfiles("test") +public class UserDaoTest { + + @Autowired + private UserDaoJdbc userDao; + + @Autowired + private DefaultListableBeanFactory beanFactory; + + private User user1; + private User user2; + private User user3; + + @Before + public void setUp() { + + user1= new User("first", "첫째", "1234", Level.BASIC, 1 , 0, "first@nacer.com"); + user2= new User("second", "둘째", "12344", Level.SILVER, 55, 10, "second@naver.com"); + user3= new User("third", "셋째", "123456", Level.GOLD, 100, 40, "third@naver.com"); + + } + + @Test + public void bean() { + for(String s : beanFactory.getBeanDefinitionNames()) { + System.out.println(s + " \t " + beanFactory.getBean(s).getClass().getName()); + } + + } + + @Test + public void addAndGet() throws SQLException, ClassNotFoundException { + + userDao.deleteAll(); + assertThat(userDao.getCount(), is(0)); + + userDao.add(user1); + userDao.add(user2); + + User userGet1 = userDao.get(user1.getId()); + checkSameUser(user1, userGet1); + + User userGet2 = userDao.get(user2.getId()); + checkSameUser(user2, userGet2); + } + + @Test + public void count() throws SQLException { + + + userDao.deleteAll(); + assertThat(userDao.getCount(), is(0)); + + userDao.add(user1); + assertThat(userDao.getCount(), is(1)); + + userDao.add(user2); + assertThat(userDao.getCount(), is(2)); + + userDao.add(user3); + assertThat(userDao.getCount(), is(3)); + + + } + + @Test(expected =EmptyResultDataAccessException.class) + public void getUserFailure() throws SQLException{ + + + userDao.deleteAll(); + assertThat(userDao.getCount(), is(0)); + + userDao.get("unknown_id"); + } + + @Test + public void getAll() throws SQLException{ + + userDao.deleteAll(); + assertThat(userDao.getCount(), is(0)); + + List emptyLisy = userDao.getAll(); + assertThat(emptyLisy.size(), is(0)); + + userDao.add(user1); + List userList1 = userDao.getAll(); + assertThat(userDao.getCount(), is(1)); + checkSameUser(user1, userList1.get(0)); + + userDao.add(user2); + List userList2 = userDao.getAll(); + assertThat(userDao.getCount(), is(2)); + checkSameUser(user1, userList2.get(0)); + checkSameUser(user2, userList2.get(1)); + + userDao.add(user3); + List userList3 = userDao.getAll(); + assertThat(userDao.getCount(), is(3)); + checkSameUser(user1, userList3.get(0)); + checkSameUser(user2, userList3.get(1)); + checkSameUser(user3, userList3.get(2)); + + } + + public void checkSameUser(User user1, User user2) { + + assertThat(user1.getId(), is(user2.getId())); + assertThat(user1.getName(), is(user2.getName())); + assertThat(user1.getPassword(), is(user2.getPassword())); + assertThat(user1.getLevel(), is(user2.getLevel())); + assertThat(user1.getLogin(), is(user2.getLogin())); + assertThat(user1.getRecommend(), is(user2.getRecommend())); + } + + @Test(expected = DataAccessException.class) + public void duplicateKeyExceptionTest() { + + userDao.deleteAll(); + assertThat(userDao.getCount(), is(0)); + + userDao.add(user1); + userDao.add(user1); + + } + + @Test + public void update() { + + userDao.deleteAll(); + + userDao.add(user1); + userDao.add(user2); + + user1.setName("new"); + user1.setPassword("1234"); + user1.setLevel(Level.GOLD); + user1.setLogin(1000); + user1.setRecommend(999); + + //assertThat(userDao.update(user1), is(1)); + + User user1Update = userDao.get(user1.getId()); + checkSameUser(user1, user1Update); + + User user2Same = userDao.get(user2.getId()); + checkSameUser(user2, user2Same); + } +} \ No newline at end of file diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/test/java/tobi/spring/domain/UserServiceTest.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/test/java/tobi/spring/domain/UserServiceTest.java" new file mode 100644 index 0000000..d392ca7 --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/test/java/tobi/spring/domain/UserServiceTest.java" @@ -0,0 +1,302 @@ +package tobi.spring.domain; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.TransientDataAccessException; +import org.springframework.dao.TransientDataAccessResourceException; +import org.springframework.mail.MailException; +import org.springframework.mail.MailSender; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.DefaultTransactionDefinition; +import tobi.spring.config.AppContext; +import tobi.spring.domain.Level; +import tobi.spring.domain.User; +import tobi.spring.domain.UserDao; +import tobi.spring.service.UserService; +import tobi.spring.service.UserServiceImpl; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes= AppContext.class) +@ActiveProfiles("test") +public class UserServiceTest { + + @Autowired + private UserService userService; + + @Autowired + private UserDao userDao; + + @Autowired + private UserService testUserService; + + @Autowired + private PlatformTransactionManager transactionManager; + + List users; + + @Before + public void setUp() { + users = Arrays.asList( + new User("aa", "AA", "p1", Level.BASIC, UserServiceImpl.MIN_LOGCOUNT_FOR_SILVER -1, 0, "aa@n.com"), + new User("bb", "BB", "p2", Level.BASIC, UserServiceImpl.MIN_LOGCOUNT_FOR_SILVER, 0, "bb@n.com"), + new User("cc", "CC", "p3", Level.SILVER, 60, UserServiceImpl.MIN_RECOMMEND_FOR_GOLD -1 ,"cc@n.com"), + new User("dd", "DD", "p4", Level.SILVER, 60, UserServiceImpl.MIN_RECOMMEND_FOR_GOLD , "dd@n.com"), + new User("ee", "EE", "p5", Level.GOLD, 100, 100, "ee@n.com") + ); + } + + @Test + public void beans() { + + assertThat(userDao, is(notNullValue())); + assertThat(userService, is(notNullValue())); + } + + @Test + @DirtiesContext + public void upgradeLevels() throws Exception { + + UserServiceImpl userServiceImpl = new UserServiceImpl(); + + MockMailSender mockMailSender = new MockMailSender(); + userServiceImpl.setMailSender(mockMailSender); + + MockUserDao mockUserDao = new MockUserDao(users); + userServiceImpl.setUserDao(mockUserDao); + + userServiceImpl.upgradeLevels(); + + List updated = mockUserDao.getUpdated(); + assertThat(updated.size(), is(2) ); + assertThat(updated.get(0), is(users.get(1))); + assertThat(updated.get(1), is(users.get(3))); + + List request= mockMailSender.getRequest(); + assertThat(request.size(), is(2) ); + assertThat(request.get(0), is(users.get(1).getMail())); + assertThat(request.get(1), is(users.get(3).getMail())); + + } + + @Test + public void add() { + + userDao.deleteAll(); + + User userWithLevel = users.get(4); + User userWithoutLevel = users.get(0); + userWithoutLevel.setLevel(null); + + userService.add(userWithLevel); + userService.add(userWithoutLevel); + + User userWithLevelRead = userDao.get(userWithLevel.getId()); + User userWithoutLevelRead = userDao.get(userWithoutLevel.getId()); + + assertThat(userWithLevelRead.getLevel(), is(userWithLevel.getLevel())); + assertThat(userWithoutLevelRead.getLevel(), is(Level.BASIC)); + } + + private void checkLevel(User user, boolean upgraded) { + + User userUpgraded = userDao.get(user.getId()); + if(upgraded) { + assertThat(userUpgraded.getLevel(), is(user.getLevel().nextLevel())); + } else { + assertThat(userUpgraded.getLevel(), is(user.getLevel())); + } + } + + + static class TestUserServiceException extends RuntimeException{} + + public static class TestUserServiceImpl extends UserServiceImpl{ + private String id = "dd"; + + @Override + protected void upgradeLevel(User user) { + if(user.getId().equals(id)) throw new TestUserServiceException(); + super.upgradeLevel(user); + } + + @Override + public List getAll() { + // TODO Auto-generated method stub + for(User user : super.getAll()) { + + super.update(user); + } + + return null; + } + } + + @Test + public void upgradeAllOrNothing() throws Exception { + + userDao.deleteAll(); + + for(User user: users) userDao.add(user); + + try { + testUserService.upgradeLevels(); + fail("TestUserServiceExecption Expected"); + } catch(TestUserServiceException e) { + + } + + checkLevel(users.get(1), false); + } + + static class MockUserDao implements UserDao { + + private List users; + private List updated = new ArrayList<>(); + + private MockUserDao(List users) { + this.users = users; + } + + public List getUpdated(){ + return this.updated; + } + @Override + public List getAll() { + // TODO Auto-generated method stub + return this.users; + } + + @Override + public int update(User user) { + // TODO Auto-generated method stub + this.updated.add(user); + return 1; + } + + @Override + public void add(User user) {throw new UnsupportedOperationException();} + + @Override + public User get(String id) {throw new UnsupportedOperationException();} + + @Override + public void deleteAll() {throw new UnsupportedOperationException();} + + @Override + public int getCount() {throw new UnsupportedOperationException();} + + + + } + + static class MockMailSender implements MailSender{ + + private List request = new ArrayList<>(); + + public List getRequest() { + return request; + } + + @Override + public void send(SimpleMailMessage message) throws MailException { + + request.add(message.getTo()[0]); + } + + @Override + public void send(SimpleMailMessage[] message) throws MailException { + } + + } + + @Test + public void mockUpgradeLevels() throws Exception{ + + UserServiceImpl userServiceImpl = new UserServiceImpl(); + + //mock 오브젝트 생성 + UserDaoJdbc mockUserDao = mock(UserDaoJdbc.class); + //getAll() 메서드를 호출하면 users 를 반환 + when(mockUserDao.getAll()).thenReturn(this.users); + userServiceImpl.setUserDao(mockUserDao); + //mock 오브젝트 생성 + MailSender mockMailSender = mock(MailSender.class); + userServiceImpl.setMailSender(mockMailSender); + + userServiceImpl.upgradeLevels(); + + //update 메서드가 2번 호출됏나 + verify(mockUserDao, times(2)).update(any(User.class)); + //users.get(1) 을 파라미터로 받는 update 메서드가 호출됏나 + verify(mockUserDao).update(users.get(1)); + //level 확인 + assertThat(users.get(1).getLevel(), is(Level.SILVER) ); + verify(mockUserDao).update(users.get(3)); + assertThat(users.get(3).getLevel(), is(Level.GOLD)); + + ArgumentCaptor mailMessageArg = + ArgumentCaptor.forClass(SimpleMailMessage.class); + verify(mockMailSender, times(2)).send(mailMessageArg.capture()); + List mailMessages = mailMessageArg.getAllValues(); + assertThat(mailMessages.get(0).getTo()[0], is(users.get(1).getMail())); + assertThat(mailMessages.get(1).getTo()[0], is(users.get(3).getMail())); + + + } + + @Test(expected = TransientDataAccessResourceException.class) + public void readOnlyTransactionAttribute() { + testUserService.getAll(); + } + + @Test(expected = TransientDataAccessException.class) + @Transactional(readOnly = true) + public void transactionSync() { + + userService.deleteAll(); + + + } + + @Test + public void rollbackTEst() { + + //트랜잭션 시작 + TransactionStatus txStatus = transactionManager.getTransaction(new DefaultTransactionDefinition()); + + try { + userService.deleteAll(); + userService.add(users.get(1)); + userService.add(users.get(2)); + + } finally { + transactionManager.rollback(txStatus); + } + } + + +} diff --git "a/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/test/java/tobi/spring/domain/UserTest.java" "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/test/java/tobi/spring/domain/UserTest.java" new file mode 100644 index 0000000..a1a26a0 --- /dev/null +++ "b/cherry/Spring-Boot.hello-spring/\352\260\234\354\235\270 \355\224\204\353\241\234\354\240\235\355\212\270/spring/src/test/java/tobi/spring/domain/UserTest.java" @@ -0,0 +1,27 @@ +package tobi.spring.domain; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import org.junit.Test; + +public class UserTest { + User user = new User("aa", "AA", "p1", Level.BASIC, 0, 0, "asd"); + + @Test + public void upgradeLevel() { + + user.upgradeLevel(); + assertThat(user.getLevel(), is(Level.SILVER)); + user.upgradeLevel(); + assertThat(user.getLevel(), is(Level.GOLD)); + } + + @Test(expected = IllegalStateException.class) + public void upgradeLevelException() { + + user.setLevel(Level.GOLD); + + user.upgradeLevel(); + } +}