├── .gitignore
├── .mvn
└── wrapper
│ └── maven-wrapper.properties
├── README.md
├── mvnw
├── mvnw.cmd
├── pom.xml
├── spring-boot-multidatasource-parent
└── pom.xml
├── spring-boot-multidatasource-sample
├── pom.xml
└── spring-boot-multidatasource-sample-default
│ ├── pom.xml
│ └── src
│ ├── main
│ ├── java
│ │ └── org
│ │ │ └── sbcoba
│ │ │ └── springboot
│ │ │ └── multidatasource
│ │ │ └── sample
│ │ │ ├── DataSourceName.java
│ │ │ ├── SampleApplication.java
│ │ │ ├── annotation
│ │ │ ├── Db1DataSource.java
│ │ │ ├── Db2DataSource.java
│ │ │ ├── Db3DataSource.java
│ │ │ └── Db4DataSource.java
│ │ │ ├── repository
│ │ │ └── TestDao.java
│ │ │ ├── service
│ │ │ ├── TestService.java
│ │ │ ├── TestServiceByClassImpl.java
│ │ │ ├── TestServiceByCustomAnnotationImpl.java
│ │ │ ├── TestServiceByMethodmpl.java
│ │ │ ├── TestServiceImpl.java
│ │ │ └── packagetest
│ │ │ │ ├── TestServiceByPackageImpl.java
│ │ │ │ └── package-info.java
│ │ │ └── web
│ │ │ ├── TestByClassController.java
│ │ │ ├── TestByCustomAnnotationController.java
│ │ │ ├── TestByMethodController.java
│ │ │ ├── TestByPackageController.java
│ │ │ └── TestController.java
│ └── resources
│ │ ├── application.yml
│ │ ├── db1Data.sql
│ │ ├── db2Data.sql
│ │ ├── db3Data.sql
│ │ ├── db4Data.sql
│ │ └── schema.sql
│ └── test
│ └── java
│ └── org
│ └── sbcoba
│ └── springboot
│ └── multidatasource
│ └── sample
│ ├── DataSourceTests.java
│ └── MultiDataSourceTests.java
└── spring-boot-multidatasource
├── pom.xml
└── src
├── main
├── java
│ └── org
│ │ └── sbcoba
│ │ └── springboot
│ │ └── multidatasource
│ │ └── autoconfigure
│ │ ├── DataSource.java
│ │ ├── DataSourceFactory.java
│ │ ├── DataSourceNameContextHolder.java
│ │ ├── DataSourceSet.java
│ │ ├── MultiDataSourceAutoConfiguration.java
│ │ ├── MultiDataSourceInitializedEvent.java
│ │ ├── MultiDataSourceInitializer.java
│ │ ├── MultiDataSourceProperties.java
│ │ ├── MultiDataSourceRegistrar.java
│ │ ├── MultiRoutingDataSource.java
│ │ └── aop
│ │ ├── AnnotationDeepMethodMatcher.java
│ │ ├── DataSourceAdvice.java
│ │ └── DataSourceRouterAspect.java
└── resources
│ └── META-INF
│ └── spring.factories
└── test
└── java
└── org
└── sbcoba
└── springboot
└── multidatasource
└── autoconfigure
└── MultiDataSourceTests.java
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | *.sw?
3 | .#*
4 | *#
5 | *~
6 | /build
7 | /code
8 | .classpath
9 | .project
10 | .settings
11 | .metadata
12 | .factorypath
13 | .recommenders
14 | bin
15 | build
16 | lib/
17 | target
18 | .springBeans
19 | interpolated*.xml
20 | dependency-reduced-pom.xml
21 | build.log
22 | _site/
23 | .*.md.html
24 | manifest.yml
25 | MANIFEST.MF
26 | settings.xml
27 | activemq-data
28 | overridedb.*
29 | *.iml
30 | *.ipr
31 | *.iws
32 | .idea
33 | *.jar
34 | .DS_Store
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.3/apache-maven-3.3.3-bin.zip
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Spring Boot Multi Datasource
2 |
3 | ## 버전
4 | - Spring Boot 1.3.0.RELEASE 기준
5 |
6 | ## 소개
7 | - Spring Boot 에서 DataSource 한개 이상 사용시 쉽게 설정할 수 있도록 지원
8 | - 간단히 Dependency 추가와 어노테이션 만으로도 쉽게 사용 가능
9 | - @DataSource 어노테이션을 통한 DataSource Routing
10 | - 커스텀 DataSource를 사용하여 이용 가능
11 |
12 | ## 실행조건
13 | - JDK 1.6 이상 환경 ( JDK 1.7 이상 추천 )
14 | - Maven 설치
15 | - Spring Boot 개발 환경
16 |
17 | ## 소스 내려받기
18 | ```sh
19 | $ git clone https://github.com/sbcoba/spring-boot-multidatasource.git
20 | ```
21 |
22 | ## 샘플 실행
23 | ```sh
24 | $ cd spring-boot-multidatasource
25 | $ mvn clean install
26 | # 샘플 실행
27 | $ cd spring-boot-multidatasource-sample
28 | $ mvn -pl spring-boot-multidatasource-sample-default spring-boot:run
29 | ```
30 |
31 | ## Maven Dependency 설정
32 | ```xml
33 |
34 | org.sbcoba.springboot
35 | spring-boot-multidatasource
36 | 0.3.0.BUILD-SNAPSHOT
37 |
38 | ```
39 |
40 | ## 예제설명
41 | - spring-boot-multidatasource-sample-default
42 | - 다양한 데이타 소스를 변경하며 테스트 가능
43 | - H2 DB를 이용하여 쉽게 확인 가능
44 |
--------------------------------------------------------------------------------
/mvnw:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # ----------------------------------------------------------------------------
3 | # Licensed to the Apache Software Foundation (ASF) under one
4 | # or more contributor license agreements. See the NOTICE file
5 | # distributed with this work for additional information
6 | # regarding copyright ownership. The ASF licenses this file
7 | # to you under the Apache License, Version 2.0 (the
8 | # "License"); you may not use this file except in compliance
9 | # with the License. You may obtain a copy of the License at
10 | #
11 | # http://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing,
14 | # software distributed under the License is distributed on an
15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | # KIND, either express or implied. See the License for the
17 | # specific language governing permissions and limitations
18 | # under the License.
19 | # ----------------------------------------------------------------------------
20 |
21 | # ----------------------------------------------------------------------------
22 | # Maven2 Start Up Batch script
23 | #
24 | # Required ENV vars:
25 | # ------------------
26 | # JAVA_HOME - location of a JDK home dir
27 | #
28 | # Optional ENV vars
29 | # -----------------
30 | # M2_HOME - location of maven2's installed home dir
31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven
32 | # e.g. to debug Maven itself, use
33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files
35 | # ----------------------------------------------------------------------------
36 |
37 | if [ -z "$MAVEN_SKIP_RC" ] ; then
38 |
39 | if [ -f /etc/mavenrc ] ; then
40 | . /etc/mavenrc
41 | fi
42 |
43 | if [ -f "$HOME/.mavenrc" ] ; then
44 | . "$HOME/.mavenrc"
45 | fi
46 |
47 | fi
48 |
49 | # OS specific support. $var _must_ be set to either true or false.
50 | cygwin=false;
51 | darwin=false;
52 | mingw=false
53 | case "`uname`" in
54 | CYGWIN*) cygwin=true ;;
55 | MINGW*) mingw=true;;
56 | Darwin*) darwin=true
57 | #
58 | # Look for the Apple JDKs first to preserve the existing behaviour, and then look
59 | # for the new JDKs provided by Oracle.
60 | #
61 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then
62 | #
63 | # Apple JDKs
64 | #
65 | export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home
66 | fi
67 |
68 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then
69 | #
70 | # Apple JDKs
71 | #
72 | export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
73 | fi
74 |
75 | if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then
76 | #
77 | # Oracle JDKs
78 | #
79 | export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
80 | fi
81 |
82 | if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then
83 | #
84 | # Apple JDKs
85 | #
86 | export JAVA_HOME=`/usr/libexec/java_home`
87 | fi
88 | ;;
89 | esac
90 |
91 | if [ -z "$JAVA_HOME" ] ; then
92 | if [ -r /etc/gentoo-release ] ; then
93 | JAVA_HOME=`java-config --jre-home`
94 | fi
95 | fi
96 |
97 | if [ -z "$M2_HOME" ] ; then
98 | ## resolve links - $0 may be a link to maven's home
99 | PRG="$0"
100 |
101 | # need this for relative symlinks
102 | while [ -h "$PRG" ] ; do
103 | ls=`ls -ld "$PRG"`
104 | link=`expr "$ls" : '.*-> \(.*\)$'`
105 | if expr "$link" : '/.*' > /dev/null; then
106 | PRG="$link"
107 | else
108 | PRG="`dirname "$PRG"`/$link"
109 | fi
110 | done
111 |
112 | saveddir=`pwd`
113 |
114 | M2_HOME=`dirname "$PRG"`/..
115 |
116 | # make it fully qualified
117 | M2_HOME=`cd "$M2_HOME" && pwd`
118 |
119 | cd "$saveddir"
120 | # echo Using m2 at $M2_HOME
121 | fi
122 |
123 | # For Cygwin, ensure paths are in UNIX format before anything is touched
124 | if $cygwin ; then
125 | [ -n "$M2_HOME" ] &&
126 | M2_HOME=`cygpath --unix "$M2_HOME"`
127 | [ -n "$JAVA_HOME" ] &&
128 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
129 | [ -n "$CLASSPATH" ] &&
130 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
131 | fi
132 |
133 | # For Migwn, ensure paths are in UNIX format before anything is touched
134 | if $mingw ; then
135 | [ -n "$M2_HOME" ] &&
136 | M2_HOME="`(cd "$M2_HOME"; pwd)`"
137 | [ -n "$JAVA_HOME" ] &&
138 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
139 | # TODO classpath?
140 | fi
141 |
142 | if [ -z "$JAVA_HOME" ]; then
143 | javaExecutable="`which javac`"
144 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
145 | # readlink(1) is not available as standard on Solaris 10.
146 | readLink=`which readlink`
147 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
148 | if $darwin ; then
149 | javaHome="`dirname \"$javaExecutable\"`"
150 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
151 | else
152 | javaExecutable="`readlink -f \"$javaExecutable\"`"
153 | fi
154 | javaHome="`dirname \"$javaExecutable\"`"
155 | javaHome=`expr "$javaHome" : '\(.*\)/bin'`
156 | JAVA_HOME="$javaHome"
157 | export JAVA_HOME
158 | fi
159 | fi
160 | fi
161 |
162 | if [ -z "$JAVACMD" ] ; then
163 | if [ -n "$JAVA_HOME" ] ; then
164 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
165 | # IBM's JDK on AIX uses strange locations for the executables
166 | JAVACMD="$JAVA_HOME/jre/sh/java"
167 | else
168 | JAVACMD="$JAVA_HOME/bin/java"
169 | fi
170 | else
171 | JAVACMD="`which java`"
172 | fi
173 | fi
174 |
175 | if [ ! -x "$JAVACMD" ] ; then
176 | echo "Error: JAVA_HOME is not defined correctly." >&2
177 | echo " We cannot execute $JAVACMD" >&2
178 | exit 1
179 | fi
180 |
181 | if [ -z "$JAVA_HOME" ] ; then
182 | echo "Warning: JAVA_HOME environment variable is not set."
183 | fi
184 |
185 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
186 |
187 | # For Cygwin, switch paths to Windows format before running java
188 | if $cygwin; then
189 | [ -n "$M2_HOME" ] &&
190 | M2_HOME=`cygpath --path --windows "$M2_HOME"`
191 | [ -n "$JAVA_HOME" ] &&
192 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
193 | [ -n "$CLASSPATH" ] &&
194 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
195 | fi
196 |
197 | # traverses directory structure from process work directory to filesystem root
198 | # first directory with .mvn subdirectory is considered project base directory
199 | find_maven_basedir() {
200 | local basedir=$(pwd)
201 | local wdir=$(pwd)
202 | while [ "$wdir" != '/' ] ; do
203 | if [ -d "$wdir"/.mvn ] ; then
204 | basedir=$wdir
205 | break
206 | fi
207 | wdir=$(cd "$wdir/.."; pwd)
208 | done
209 | echo "${basedir}"
210 | }
211 |
212 | # concatenates all lines of a file
213 | concat_lines() {
214 | if [ -f "$1" ]; then
215 | echo "$(tr -s '\n' ' ' < "$1")"
216 | fi
217 | }
218 |
219 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)}
220 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
221 |
222 | # Provide a "standardized" way to retrieve the CLI args that will
223 | # work with both Windows and non-Windows executions.
224 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
225 | export MAVEN_CMD_LINE_ARGS
226 |
227 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
228 |
229 | exec "$JAVACMD" \
230 | $MAVEN_OPTS \
231 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
232 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
233 | ${WRAPPER_LAUNCHER} "$@"
234 |
--------------------------------------------------------------------------------
/mvnw.cmd:
--------------------------------------------------------------------------------
1 | @REM ----------------------------------------------------------------------------
2 | @REM Licensed to the Apache Software Foundation (ASF) under one
3 | @REM or more contributor license agreements. See the NOTICE file
4 | @REM distributed with this work for additional information
5 | @REM regarding copyright ownership. The ASF licenses this file
6 | @REM to you under the Apache License, Version 2.0 (the
7 | @REM "License"); you may not use this file except in compliance
8 | @REM with the License. You may obtain a copy of the License at
9 | @REM
10 | @REM http://www.apache.org/licenses/LICENSE-2.0
11 | @REM
12 | @REM Unless required by applicable law or agreed to in writing,
13 | @REM software distributed under the License is distributed on an
14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | @REM KIND, either express or implied. See the License for the
16 | @REM specific language governing permissions and limitations
17 | @REM under the License.
18 | @REM ----------------------------------------------------------------------------
19 |
20 | @REM ----------------------------------------------------------------------------
21 | @REM Maven2 Start Up Batch script
22 | @REM
23 | @REM Required ENV vars:
24 | @REM JAVA_HOME - location of a JDK home dir
25 | @REM
26 | @REM Optional ENV vars
27 | @REM M2_HOME - location of maven2's installed home dir
28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
31 | @REM e.g. to debug Maven itself, use
32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
34 | @REM ----------------------------------------------------------------------------
35 |
36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
37 | @echo off
38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
40 |
41 | @REM set %HOME% to equivalent of $HOME
42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
43 |
44 | @REM Execute a user defined script before this one
45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending
47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
49 | :skipRcPre
50 |
51 | @setlocal
52 |
53 | set ERROR_CODE=0
54 |
55 | @REM To isolate internal variables from possible post scripts, we use another setlocal
56 | @setlocal
57 |
58 | @REM ==== START VALIDATION ====
59 | if not "%JAVA_HOME%" == "" goto OkJHome
60 |
61 | echo.
62 | echo Error: JAVA_HOME not found in your environment. >&2
63 | echo Please set the JAVA_HOME variable in your environment to match the >&2
64 | echo location of your Java installation. >&2
65 | echo.
66 | goto error
67 |
68 | :OkJHome
69 | if exist "%JAVA_HOME%\bin\java.exe" goto init
70 |
71 | echo.
72 | echo Error: JAVA_HOME is set to an invalid directory. >&2
73 | echo JAVA_HOME = "%JAVA_HOME%" >&2
74 | echo Please set the JAVA_HOME variable in your environment to match the >&2
75 | echo location of your Java installation. >&2
76 | echo.
77 | goto error
78 |
79 | @REM ==== END VALIDATION ====
80 |
81 | :init
82 |
83 | set MAVEN_CMD_LINE_ARGS=%*
84 |
85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
86 | @REM Fallback to current working directory if not found.
87 |
88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
90 |
91 | set EXEC_DIR=%CD%
92 | set WDIR=%EXEC_DIR%
93 | :findBaseDir
94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound
95 | cd ..
96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound
97 | set WDIR=%CD%
98 | goto findBaseDir
99 |
100 | :baseDirFound
101 | set MAVEN_PROJECTBASEDIR=%WDIR%
102 | cd "%EXEC_DIR%"
103 | goto endDetectBaseDir
104 |
105 | :baseDirNotFound
106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
107 | cd "%EXEC_DIR%"
108 |
109 | :endDetectBaseDir
110 |
111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
112 |
113 | @setlocal EnableExtensions EnableDelayedExpansion
114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
116 |
117 | :endReadAdditionalConfig
118 |
119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
120 |
121 | set WRAPPER_JAR="".\.mvn\wrapper\maven-wrapper.jar""
122 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
123 |
124 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS%
125 | if ERRORLEVEL 1 goto error
126 | goto end
127 |
128 | :error
129 | set ERROR_CODE=1
130 |
131 | :end
132 | @endlocal & set ERROR_CODE=%ERROR_CODE%
133 |
134 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
135 | @REM check for post script, once with legacy .bat ending and once with .cmd ending
136 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
137 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
138 | :skipRcPost
139 |
140 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
141 | if "%MAVEN_BATCH_PAUSE%" == "on" pause
142 |
143 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
144 |
145 | exit /B %ERROR_CODE%
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | org.sbcoba.springboot
5 | spring-boot-multidatasource-build
6 | 0.3.0.BUILD-SNAPSHOT
7 | pom
8 | spring-boot-multidatasource-build
9 | Spring Boot Multi Datasource
10 | https://github.com/sbcoba
11 |
12 | Lee su hong
13 | https://github.com/sbcoba
14 |
15 |
16 |
17 | Apache License, Version 2.0
18 | http://www.apache.org/licenses/LICENSE-2.0
19 |
20 |
21 |
22 |
23 | sbcoba
24 | Lee su hong
25 | sbcoba@gmail.com
26 |
27 | Project Lead
28 |
29 |
30 |
31 |
32 | 3.2.1
33 |
34 |
35 | ${basedir}
36 |
37 |
38 |
39 |
40 | org.apache.maven.plugins
41 | maven-deploy-plugin
42 |
43 | true
44 |
45 |
46 |
47 |
48 |
49 |
50 | default
51 |
52 | true
53 |
54 |
55 | spring-boot-multidatasource-parent
56 | spring-boot-multidatasource
57 | spring-boot-multidatasource-sample
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource-parent/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | org.springframework.boot
6 | spring-boot-starter-parent
7 | 1.3.0.RELEASE
8 |
9 |
10 |
11 | org.sbcoba.springboot
12 | spring-boot-multidatasource-parent
13 | 0.3.0.BUILD-SNAPSHOT
14 | pom
15 | spring-boot-multidatasource-parent
16 |
17 |
18 | UTF-8
19 |
20 |
21 |
22 |
23 |
24 | org.sbcoba.springboot
25 | spring-boot-multidatasource
26 | ${project.version}
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource-sample/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | org.sbcoba.springboot
6 | spring-boot-multidatasource-parent
7 | 0.3.0.BUILD-SNAPSHOT
8 | ../spring-boot-multidatasource-parent
9 |
10 |
11 | org.sbcoba.springboot
12 | spring-boot-multidatasource-sample
13 | pom
14 | spring-boot-multidatasource-sample
15 |
16 |
17 | spring-boot-multidatasource-sample-default
18 |
19 |
20 |
21 | UTF-8
22 |
23 |
24 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource-sample/spring-boot-multidatasource-sample-default/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.sbcoba.springboot
7 | spring-boot-multidatasource-sample
8 | 0.3.0.BUILD-SNAPSHOT
9 | ../
10 |
11 |
12 | spring-boot-multidatasource-sample-default
13 | war
14 |
15 | spring-boot-multidatasource-sample-default
16 | Spring Boot Multi Datasource sample
17 |
18 |
19 | ${basedir}/../..
20 |
21 |
22 |
23 |
24 | org.springframework.boot
25 | spring-boot-starter-web
26 |
27 |
28 |
29 | org.sbcoba.springboot
30 | spring-boot-multidatasource
31 |
32 |
33 |
34 | mysql
35 | mysql-connector-java
36 | runtime
37 |
38 |
39 |
40 | com.h2database
41 | h2
42 | runtime
43 |
44 |
45 |
46 | org.springframework.boot
47 | spring-boot-starter-test
48 | test
49 |
50 |
51 |
52 | com.jayway.jsonpath
53 | json-path
54 | test
55 |
56 |
57 |
58 |
59 |
60 |
61 | org.springframework.boot
62 | spring-boot-maven-plugin
63 |
64 |
65 | org.apache.maven.plugins
66 | maven-surefire-plugin
67 |
68 | false
69 |
70 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource-sample/spring-boot-multidatasource-sample-default/src/main/java/org/sbcoba/springboot/multidatasource/sample/DataSourceName.java:
--------------------------------------------------------------------------------
1 | package org.sbcoba.springboot.multidatasource.sample;
2 |
3 | /**
4 | * DataSource 명 리스트
5 | * ( 문자열로 바로 사용할 수 있으나
6 | * 오타 등의 문제로 인해 상수로 사용하는 것을 추천 )
7 | *
8 | * @author sbcoba
9 | */
10 | public final class DataSourceName {
11 | /**
12 | * 첫번째 DB1
13 | */
14 | public static final String DB1 = "db1";
15 | /**
16 | * 두번째 DB2
17 | */
18 | public static final String DB2 ="db2";
19 | /**
20 | * 세번째 DB3
21 | */
22 | public static final String DB3 = "db3";
23 | /**
24 | * 네번째 DB4
25 | */
26 | public static final String DB4 = "db4";
27 | }
28 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource-sample/spring-boot-multidatasource-sample-default/src/main/java/org/sbcoba/springboot/multidatasource/sample/SampleApplication.java:
--------------------------------------------------------------------------------
1 | package org.sbcoba.springboot.multidatasource.sample;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.boot.builder.SpringApplicationBuilder;
6 | import org.springframework.boot.context.web.SpringBootServletInitializer;
7 |
8 | /**
9 | * 샘플 실행 진입점
10 | * @author sbcoba
11 | */
12 | @SpringBootApplication
13 | public class SampleApplication extends SpringBootServletInitializer {
14 | public static void main(String[] args) {
15 | SpringApplication.run(SampleApplication.class, args);
16 | }
17 |
18 | @Override
19 | protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
20 | return application.sources(SampleApplication.class);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource-sample/spring-boot-multidatasource-sample-default/src/main/java/org/sbcoba/springboot/multidatasource/sample/annotation/Db1DataSource.java:
--------------------------------------------------------------------------------
1 | package org.sbcoba.springboot.multidatasource.sample.annotation;
2 |
3 | import org.sbcoba.springboot.multidatasource.autoconfigure.DataSource;
4 |
5 | import java.lang.annotation.*;
6 |
7 | import static org.sbcoba.springboot.multidatasource.sample.DataSourceName.DB1;
8 |
9 | /**
10 | * DB1 DataSource
11 | *
12 | * @author sbcoba
13 | */
14 | @Target({ElementType.PACKAGE, ElementType.METHOD, ElementType.TYPE})
15 | @Retention(RetentionPolicy.RUNTIME)
16 | @Documented
17 | @DataSource(DB1)
18 | public @interface Db1DataSource {
19 | }
20 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource-sample/spring-boot-multidatasource-sample-default/src/main/java/org/sbcoba/springboot/multidatasource/sample/annotation/Db2DataSource.java:
--------------------------------------------------------------------------------
1 | package org.sbcoba.springboot.multidatasource.sample.annotation;
2 |
3 | import org.sbcoba.springboot.multidatasource.autoconfigure.DataSource;
4 |
5 | import java.lang.annotation.*;
6 |
7 | import static org.sbcoba.springboot.multidatasource.sample.DataSourceName.DB2;
8 |
9 | /**
10 | * DB2 DataSource
11 | *
12 | * @author sbcoba
13 | */
14 | @Target({ElementType.PACKAGE, ElementType.METHOD, ElementType.TYPE})
15 | @Retention(RetentionPolicy.RUNTIME)
16 | @Documented
17 | @DataSource(DB2)
18 | public @interface Db2DataSource {
19 | }
20 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource-sample/spring-boot-multidatasource-sample-default/src/main/java/org/sbcoba/springboot/multidatasource/sample/annotation/Db3DataSource.java:
--------------------------------------------------------------------------------
1 | package org.sbcoba.springboot.multidatasource.sample.annotation;
2 |
3 | import org.sbcoba.springboot.multidatasource.autoconfigure.DataSource;
4 |
5 | import java.lang.annotation.*;
6 |
7 | import static org.sbcoba.springboot.multidatasource.sample.DataSourceName.DB3;
8 |
9 | /**
10 | * DB3 DataSource
11 | *
12 | * @author sbcoba
13 | */
14 | @Target({ElementType.PACKAGE, ElementType.METHOD, ElementType.TYPE})
15 | @Retention(RetentionPolicy.RUNTIME)
16 | @Documented
17 | @DataSource(DB3)
18 | public @interface Db3DataSource {
19 | }
20 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource-sample/spring-boot-multidatasource-sample-default/src/main/java/org/sbcoba/springboot/multidatasource/sample/annotation/Db4DataSource.java:
--------------------------------------------------------------------------------
1 | package org.sbcoba.springboot.multidatasource.sample.annotation;
2 |
3 | import org.sbcoba.springboot.multidatasource.autoconfigure.DataSource;
4 |
5 | import java.lang.annotation.*;
6 |
7 | import static org.sbcoba.springboot.multidatasource.sample.DataSourceName.DB4;
8 |
9 | /**
10 | * DB4 DataSource
11 | *
12 | * @author sbcoba
13 | */
14 | @Target({ElementType.PACKAGE, ElementType.METHOD, ElementType.TYPE})
15 | @Retention(RetentionPolicy.RUNTIME)
16 | @Documented
17 | @DataSource(DB4)
18 | public @interface Db4DataSource {
19 | }
20 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource-sample/spring-boot-multidatasource-sample-default/src/main/java/org/sbcoba/springboot/multidatasource/sample/repository/TestDao.java:
--------------------------------------------------------------------------------
1 | package org.sbcoba.springboot.multidatasource.sample.repository;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.jdbc.core.JdbcTemplate;
5 | import org.springframework.stereotype.Repository;
6 |
7 | import java.util.List;
8 |
9 | /**
10 | * 테스트 데이타 조회용 DAO
11 | *
12 | * @author sbcoba
13 | */
14 | @Repository
15 | public class TestDao {
16 | @Autowired
17 | private JdbcTemplate jdbcTemplate;
18 |
19 | public List getNames() {
20 | return jdbcTemplate.queryForList("SELECT name FROM TEST", String.class);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource-sample/spring-boot-multidatasource-sample-default/src/main/java/org/sbcoba/springboot/multidatasource/sample/service/TestService.java:
--------------------------------------------------------------------------------
1 | package org.sbcoba.springboot.multidatasource.sample.service;
2 |
3 | import java.util.List;
4 |
5 | /**
6 | * 테스트에 사용할 인터페이스
7 | *
8 | * @author sbcoba
9 | */
10 | public interface TestService {
11 | List db1Names();
12 |
13 | List db2Names();
14 |
15 | List db3Names();
16 |
17 | List db4Names();
18 | }
19 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource-sample/spring-boot-multidatasource-sample-default/src/main/java/org/sbcoba/springboot/multidatasource/sample/service/TestServiceByClassImpl.java:
--------------------------------------------------------------------------------
1 | package org.sbcoba.springboot.multidatasource.sample.service;
2 |
3 | import org.sbcoba.springboot.multidatasource.autoconfigure.DataSource;
4 | import org.sbcoba.springboot.multidatasource.sample.repository.TestDao;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.beans.factory.annotation.Qualifier;
7 | import org.springframework.stereotype.Service;
8 | import org.springframework.transaction.annotation.Transactional;
9 |
10 | import java.util.List;
11 |
12 | import static org.sbcoba.springboot.multidatasource.sample.DataSourceName.DB2;
13 |
14 | /**
15 | * Class 위에 @DataSource 어노테이션 사용으로
16 | * 모든 메소드에서 해당 DataSource를 사용
17 | *
18 | * @author sbcoba
19 | */
20 | @Service
21 | @DataSource(DB2)
22 | @Qualifier("class")
23 | public class TestServiceByClassImpl implements TestService {
24 |
25 | @Autowired
26 | private TestDao testDao;
27 |
28 | @Override
29 | @Transactional(readOnly = true)
30 | public List db1Names() {
31 | return testDao.getNames();
32 | }
33 |
34 | @Override
35 | @Transactional(readOnly = true)
36 | public List db2Names() {
37 | return testDao.getNames();
38 | }
39 |
40 | @Override
41 | @Transactional(readOnly = true)
42 | public List db3Names() {
43 | return testDao.getNames();
44 | }
45 |
46 | @Override
47 | @Transactional(readOnly = true)
48 | public List db4Names() {
49 | return testDao.getNames();
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource-sample/spring-boot-multidatasource-sample-default/src/main/java/org/sbcoba/springboot/multidatasource/sample/service/TestServiceByCustomAnnotationImpl.java:
--------------------------------------------------------------------------------
1 | package org.sbcoba.springboot.multidatasource.sample.service;
2 |
3 | import org.sbcoba.springboot.multidatasource.sample.annotation.Db1DataSource;
4 | import org.sbcoba.springboot.multidatasource.sample.annotation.Db4DataSource;
5 | import org.sbcoba.springboot.multidatasource.sample.annotation.Db3DataSource;
6 | import org.sbcoba.springboot.multidatasource.sample.annotation.Db2DataSource;
7 | import org.sbcoba.springboot.multidatasource.sample.repository.TestDao;
8 | import org.springframework.beans.factory.annotation.Autowired;
9 | import org.springframework.beans.factory.annotation.Qualifier;
10 | import org.springframework.stereotype.Service;
11 | import org.springframework.transaction.annotation.Transactional;
12 |
13 | import java.util.List;
14 |
15 | /**
16 | * 커스터 어노테이션으로 @DataSource 랩핑한 형태로 가능
17 | *
18 | * @see Db1DataSource
19 | * @see Db2DataSource
20 | * @see Db3DataSource
21 | * @see Db4DataSource
22 | * @author sbcoba
23 | *
24 | */
25 | @Service
26 | @Qualifier("custom")
27 | public class TestServiceByCustomAnnotationImpl implements TestService {
28 |
29 | @Autowired
30 | private TestDao testDao;
31 |
32 | @Override
33 | @Db1DataSource
34 | @Transactional(readOnly = true)
35 | public List db1Names() {
36 | return testDao.getNames();
37 | }
38 |
39 | @Override
40 | @Db2DataSource
41 | @Transactional(readOnly = true)
42 | public List db2Names() {
43 | return testDao.getNames();
44 | }
45 |
46 | @Override
47 | @Db3DataSource
48 | @Transactional(readOnly = true)
49 | public List db3Names() {
50 | return testDao.getNames();
51 | }
52 |
53 | @Override
54 | @Db4DataSource
55 | @Transactional(readOnly = true)
56 | public List db4Names() {
57 | return testDao.getNames();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource-sample/spring-boot-multidatasource-sample-default/src/main/java/org/sbcoba/springboot/multidatasource/sample/service/TestServiceByMethodmpl.java:
--------------------------------------------------------------------------------
1 | package org.sbcoba.springboot.multidatasource.sample.service;
2 |
3 | import org.sbcoba.springboot.multidatasource.autoconfigure.DataSource;
4 | import org.sbcoba.springboot.multidatasource.sample.repository.TestDao;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.beans.factory.annotation.Qualifier;
7 | import org.springframework.stereotype.Service;
8 | import org.springframework.transaction.annotation.Transactional;
9 |
10 | import java.util.List;
11 |
12 | import static org.sbcoba.springboot.multidatasource.sample.DataSourceName.*;
13 |
14 | /**
15 | * 메소드별 개별의 DataSource를 따로 사용한 서비스
16 | *
17 | * @author sbcoba
18 | */
19 | @Service
20 | @Qualifier("method")
21 | public class TestServiceByMethodmpl implements TestService {
22 |
23 | @Autowired
24 | private TestDao testDao;
25 |
26 | @Override
27 | @DataSource(DB1)
28 | @Transactional(readOnly = true)
29 | public List db1Names() {
30 | return testDao.getNames();
31 | }
32 |
33 | @Override
34 | @DataSource(DB2)
35 | @Transactional(readOnly = true)
36 | public List db2Names() {
37 | return testDao.getNames();
38 | }
39 |
40 | @Override
41 | @DataSource(DB3)
42 | @Transactional(readOnly = true)
43 | public List db3Names() {
44 | return testDao.getNames();
45 | }
46 |
47 | @Override
48 | @DataSource(DB4)
49 | @Transactional(readOnly = true)
50 | public List db4Names() {
51 | return testDao.getNames();
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource-sample/spring-boot-multidatasource-sample-default/src/main/java/org/sbcoba/springboot/multidatasource/sample/service/TestServiceImpl.java:
--------------------------------------------------------------------------------
1 | package org.sbcoba.springboot.multidatasource.sample.service;
2 |
3 | import org.sbcoba.springboot.multidatasource.sample.repository.TestDao;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.beans.factory.annotation.Qualifier;
6 | import org.springframework.context.annotation.Primary;
7 | import org.springframework.stereotype.Service;
8 | import org.springframework.transaction.annotation.Transactional;
9 |
10 | import java.util.List;
11 |
12 | /**
13 | * 디폴트 @DataSource 를 사용한다.
14 | * application.xml "spring.multi-datasource.default-datasource-name:" 속성의 영향
15 | *
16 | * @see org.sbcoba.springboot.multidatasource.sample.service
17 | * @author sbcoba
18 | */
19 | @Primary
20 | @Service
21 | public class TestServiceImpl implements TestService {
22 |
23 | @Autowired
24 | private TestDao testDao;
25 |
26 | @Override
27 | @Transactional(readOnly = true)
28 | public List db1Names() {
29 | return testDao.getNames();
30 | }
31 |
32 | @Override
33 | @Transactional(readOnly = true)
34 | public List db2Names() {
35 | return testDao.getNames();
36 | }
37 |
38 | @Override
39 | @Transactional(readOnly = true)
40 | public List db3Names() {
41 | return testDao.getNames();
42 | }
43 |
44 | @Override
45 | @Transactional(readOnly = true)
46 | public List db4Names() {
47 | return testDao.getNames();
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource-sample/spring-boot-multidatasource-sample-default/src/main/java/org/sbcoba/springboot/multidatasource/sample/service/packagetest/TestServiceByPackageImpl.java:
--------------------------------------------------------------------------------
1 | package org.sbcoba.springboot.multidatasource.sample.service.packagetest;
2 |
3 | import org.sbcoba.springboot.multidatasource.sample.repository.TestDao;
4 | import org.sbcoba.springboot.multidatasource.sample.service.TestService;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.beans.factory.annotation.Qualifier;
7 | import org.springframework.stereotype.Service;
8 | import org.springframework.transaction.annotation.Transactional;
9 |
10 | import java.util.List;
11 |
12 | /**
13 | * package-info.java 내부에 있는 @DataSource 를 사용한다.
14 | *
15 | * @see org.sbcoba.springboot.multidatasource.sample.service.packagetest
16 | * @author sbcoba
17 | */
18 | @Service
19 | @Qualifier("package")
20 | public class TestServiceByPackageImpl implements TestService {
21 |
22 | @Autowired
23 | private TestDao testDao;
24 |
25 | @Override
26 | @Transactional(readOnly = true)
27 | public List db1Names() {
28 | return testDao.getNames();
29 | }
30 |
31 | @Override
32 | @Transactional(readOnly = true)
33 | public List db2Names() {
34 | return testDao.getNames();
35 | }
36 |
37 | @Override
38 | @Transactional(readOnly = true)
39 | public List db3Names() {
40 | return testDao.getNames();
41 | }
42 |
43 | @Override
44 | @Transactional(readOnly = true)
45 | public List db4Names() {
46 | return testDao.getNames();
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource-sample/spring-boot-multidatasource-sample-default/src/main/java/org/sbcoba/springboot/multidatasource/sample/service/packagetest/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * 해당 페키지 안의 모든 클래스에 동일한 DataSource를 적용
3 | * ( 범위가 넓기 때문에 사용시 주의 필요 )
4 | *
5 | * @author sbcoba
6 | */
7 | @DataSource(DB4)
8 | package org.sbcoba.springboot.multidatasource.sample.service.packagetest;
9 |
10 | import org.sbcoba.springboot.multidatasource.autoconfigure.DataSource;
11 |
12 | import static org.sbcoba.springboot.multidatasource.sample.DataSourceName.*;
--------------------------------------------------------------------------------
/spring-boot-multidatasource-sample/spring-boot-multidatasource-sample-default/src/main/java/org/sbcoba/springboot/multidatasource/sample/web/TestByClassController.java:
--------------------------------------------------------------------------------
1 | package org.sbcoba.springboot.multidatasource.sample.web;
2 |
3 | import org.sbcoba.springboot.multidatasource.sample.service.TestService;
4 | import org.sbcoba.springboot.multidatasource.sample.service.TestServiceByClassImpl;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.beans.factory.annotation.Qualifier;
7 | import org.springframework.web.bind.annotation.RequestMapping;
8 | import org.springframework.web.bind.annotation.RestController;
9 |
10 | import java.util.List;
11 | /**
12 | * Class 위에 @DataSource 어노테이션 사용한 서비스를 호출
13 | *
14 | * @see TestServiceByClassImpl
15 | * @author sbcoba
16 | */
17 | @RestController
18 | @RequestMapping("class")
19 | public class TestByClassController {
20 | @Autowired
21 | @Qualifier("class")
22 | private TestService testService;
23 |
24 | @RequestMapping("db1")
25 | public List db1() {
26 | return testService.db1Names();
27 | }
28 |
29 | @RequestMapping("db2")
30 | public List db2() {
31 | return testService.db2Names();
32 | }
33 |
34 | @RequestMapping("db3")
35 | public List db3() {
36 | return testService.db3Names();
37 | }
38 |
39 | @RequestMapping("db4")
40 | public List db4() {
41 | return testService.db4Names();
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource-sample/spring-boot-multidatasource-sample-default/src/main/java/org/sbcoba/springboot/multidatasource/sample/web/TestByCustomAnnotationController.java:
--------------------------------------------------------------------------------
1 | package org.sbcoba.springboot.multidatasource.sample.web;
2 |
3 | import org.sbcoba.springboot.multidatasource.sample.service.TestService;
4 | import org.sbcoba.springboot.multidatasource.sample.service.TestServiceByCustomAnnotationImpl;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.beans.factory.annotation.Qualifier;
7 | import org.springframework.web.bind.annotation.RequestMapping;
8 | import org.springframework.web.bind.annotation.RestController;
9 |
10 | import java.util.List;
11 |
12 | /**
13 | * 커스터 어노테이션으로 @DataSource 랩핑한 형태의 서비스를 호출
14 | *
15 | * @see TestServiceByCustomAnnotationImpl
16 | * @author sbcoba
17 | */
18 | @RestController
19 | @RequestMapping("custom")
20 | public class TestByCustomAnnotationController {
21 |
22 | @Autowired
23 | @Qualifier("custom")
24 | private TestService testService;
25 |
26 | @RequestMapping("db1")
27 | public List db1() {
28 | return testService.db1Names();
29 | }
30 |
31 | @RequestMapping("db2")
32 | public List db2() {
33 | return testService.db2Names();
34 | }
35 |
36 | @RequestMapping("db3")
37 | public List db3() {
38 | return testService.db3Names();
39 | }
40 |
41 | @RequestMapping("db4")
42 | public List db4() {
43 | return testService.db4Names();
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource-sample/spring-boot-multidatasource-sample-default/src/main/java/org/sbcoba/springboot/multidatasource/sample/web/TestByMethodController.java:
--------------------------------------------------------------------------------
1 | package org.sbcoba.springboot.multidatasource.sample.web;
2 |
3 | import org.sbcoba.springboot.multidatasource.autoconfigure.DataSource;
4 | import org.sbcoba.springboot.multidatasource.sample.service.TestService;
5 | import org.sbcoba.springboot.multidatasource.sample.service.TestServiceByMethodmpl;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.beans.factory.annotation.Qualifier;
8 | import org.springframework.web.bind.annotation.RequestMapping;
9 | import org.springframework.web.bind.annotation.RestController;
10 |
11 | import java.util.List;
12 | /**
13 | * 메소드별 DataSource를 따로 사용한 서비스를 호출
14 | *
15 | * @see TestServiceByMethodmpl
16 | * @author sbcoba
17 | */
18 | @RestController
19 | @RequestMapping("method")
20 | public class TestByMethodController {
21 |
22 | @Autowired
23 | @Qualifier("method")
24 | private TestService testService;
25 |
26 | @RequestMapping("db1")
27 | public List db1() {
28 | return testService.db1Names();
29 | }
30 |
31 | @RequestMapping("db2")
32 | @DataSource("db4")
33 | public List db2() {
34 | return testService.db2Names();
35 | }
36 |
37 | @RequestMapping("db3")
38 | public List db3() {
39 | return testService.db3Names();
40 | }
41 |
42 | @RequestMapping("db4")
43 | public List db4() {
44 | return testService.db4Names();
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource-sample/spring-boot-multidatasource-sample-default/src/main/java/org/sbcoba/springboot/multidatasource/sample/web/TestByPackageController.java:
--------------------------------------------------------------------------------
1 | package org.sbcoba.springboot.multidatasource.sample.web;
2 |
3 | import org.sbcoba.springboot.multidatasource.sample.service.TestService;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.beans.factory.annotation.Qualifier;
6 | import org.springframework.web.bind.annotation.RequestMapping;
7 | import org.springframework.web.bind.annotation.RestController;
8 |
9 | import java.util.List;
10 |
11 | /**
12 | * package-info.java 내부에 있는 @DataSource 를 사용하는 Service를 호출하는 예제
13 | *
14 | * @see org.sbcoba.springboot.multidatasource.sample.service.packagetest.TestServiceByPackageImpl
15 | * @author sbcoba
16 | */
17 | @RestController
18 | @RequestMapping("package")
19 | public class TestByPackageController {
20 | @Autowired
21 | @Qualifier("package")
22 | private TestService testService;
23 |
24 | @RequestMapping("db1")
25 | public List db1() {
26 | return testService.db1Names();
27 | }
28 |
29 | @RequestMapping("db2")
30 | public List db2() {
31 | return testService.db2Names();
32 | }
33 |
34 | @RequestMapping("db3")
35 | public List db3() {
36 | return testService.db3Names();
37 | }
38 |
39 | @RequestMapping("db4")
40 | public List db4() {
41 | return testService.db4Names();
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource-sample/spring-boot-multidatasource-sample-default/src/main/java/org/sbcoba/springboot/multidatasource/sample/web/TestController.java:
--------------------------------------------------------------------------------
1 | package org.sbcoba.springboot.multidatasource.sample.web;
2 |
3 | import org.sbcoba.springboot.multidatasource.sample.service.TestService;
4 | import org.sbcoba.springboot.multidatasource.sample.service.TestServiceImpl;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.web.bind.annotation.RequestMapping;
7 | import org.springframework.web.bind.annotation.RestController;
8 |
9 | import java.util.List;
10 |
11 | /**
12 | * DataSource 어노테이션을 설정하지 않았을때 Default DataSource를 사용하는 예제
13 | *
14 | * @see TestServiceImpl
15 | * @author sbcoba
16 | */
17 | @RestController
18 | @RequestMapping("default")
19 | public class TestController {
20 | @Autowired
21 | private TestService testService;
22 |
23 | @RequestMapping("db1")
24 | public List db1() {
25 | return testService.db1Names();
26 | }
27 |
28 | @RequestMapping("db2")
29 | public List db2() {
30 | return testService.db2Names();
31 | }
32 |
33 | @RequestMapping("db3")
34 | public List db3() {
35 | return testService.db3Names();
36 | }
37 |
38 | @RequestMapping("db4")
39 | public List db4() {
40 | return testService.db4Names();
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource-sample/spring-boot-multidatasource-sample-default/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | h2:
3 | console:
4 | path: /console
5 | enabled: true
6 |
7 | multi-datasource:
8 | basePackages: org.sbcoba.springboot.multidatasource.sample.service
9 | default-datasource-name: db1
10 | datasources:
11 | db1:
12 | url: jdbc:h2:~/db1;
13 | data: classpath:db1Data.sql
14 | validationQuery: select 1
15 | db2:
16 | url: jdbc:h2:~/db2;
17 | data: classpath:db2Data.sql
18 | validationQuery: select 1
19 | db3:
20 | url: jdbc:h2:~/db3;
21 | data: classpath:db3Data.sql
22 | validationQuery: select 1
23 | db4:
24 | url: jdbc:h2:~/db4;
25 | data: classpath:db4Data.sql
26 | validationQuery: select 1
27 |
28 | logging:
29 | level:
30 | org.springframework:
31 | boot: DEBUG
32 | transaction: DEBUG
33 | org.sbcoba.springboot.multidatasource: DEBUG
34 |
35 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource-sample/spring-boot-multidatasource-sample-default/src/main/resources/db1Data.sql:
--------------------------------------------------------------------------------
1 | INSERT INTO TEST (name) VALUES
2 | ('db1 dataSource를'),
3 | ('선택했습니다'),
4 | ('변경이 잘되었습니다');
5 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource-sample/spring-boot-multidatasource-sample-default/src/main/resources/db2Data.sql:
--------------------------------------------------------------------------------
1 | INSERT INTO TEST (name) VALUES
2 | ('db2 dataSource를'),
3 | ('선택했습니다'),
4 | ('변경이 잘되었습니다');
5 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource-sample/spring-boot-multidatasource-sample-default/src/main/resources/db3Data.sql:
--------------------------------------------------------------------------------
1 | INSERT INTO TEST (name) VALUES
2 | ('db3 dataSource를'),
3 | ('선택했습니다'),
4 | ('변경이 잘되었습니다');
5 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource-sample/spring-boot-multidatasource-sample-default/src/main/resources/db4Data.sql:
--------------------------------------------------------------------------------
1 | INSERT INTO TEST (name) VALUES
2 | ('db4 dataSource를'),
3 | ('선택했습니다'),
4 | ('변경이 잘되었습니다');
5 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource-sample/spring-boot-multidatasource-sample-default/src/main/resources/schema.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE TEST if exists;
2 | create table if not exists TEST (id bigint not null auto_increment, name varchar(255), primary key (id));
--------------------------------------------------------------------------------
/spring-boot-multidatasource-sample/spring-boot-multidatasource-sample-default/src/test/java/org/sbcoba/springboot/multidatasource/sample/DataSourceTests.java:
--------------------------------------------------------------------------------
1 | package org.sbcoba.springboot.multidatasource.sample;
2 |
3 | import org.junit.Test;
4 | import org.junit.runner.RunWith;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.beans.factory.annotation.Qualifier;
7 | import org.springframework.boot.test.SpringApplicationConfiguration;
8 | import org.springframework.jdbc.core.JdbcTemplate;
9 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
10 |
11 | import javax.sql.DataSource;
12 | import java.util.List;
13 |
14 | import static org.hamcrest.core.StringStartsWith.startsWith;
15 | import static org.junit.Assert.assertThat;
16 | import static org.sbcoba.springboot.multidatasource.sample.DataSourceName.*;
17 |
18 | @RunWith(SpringJUnit4ClassRunner.class)
19 | @SpringApplicationConfiguration(classes = SampleApplication.class)
20 | public class DataSourceTests {
21 | //private static final Logger log = LoggerFactory.getLogger(DataSourceTests.class);
22 | private static final String TEST_QUERY = "SELECT name FROM TEST";
23 |
24 | @Autowired @Qualifier(DB1)
25 | private DataSource db1DataSource;
26 |
27 | @Autowired @Qualifier(DB2)
28 | private DataSource db2DataSource;
29 |
30 | @Autowired @Qualifier(DB3)
31 | private DataSource db3DataSource;
32 |
33 | @Autowired @Qualifier(DB4)
34 | private DataSource db4DataSource;
35 |
36 | @Test
37 | public void db1DataSourceTest() {
38 | List result = getTests(db1DataSource);
39 | System.out.println(DB1 + " result " + result);
40 | assertThat(result.get(0), startsWith(DB1));
41 | }
42 |
43 | @Test
44 | public void db2DataSourceTest() {
45 | List result = getTests(db2DataSource);
46 | System.out.println(DB2 + " result " + result);
47 | assertThat(result.get(0), startsWith(DB2));
48 | }
49 |
50 | @Test
51 | public void db3DataSourceTest() {
52 | List result = getTests(db3DataSource);
53 | System.out.println(DB3 + " result " + result);
54 | assertThat(result.get(0), startsWith(DB3));
55 | }
56 |
57 | @Test
58 | public void db4DataSourceTest() {
59 | List result = getTests(db4DataSource);
60 | System.out.println(DB4 + " result " + result);
61 | assertThat(result.get(0), startsWith(DB4));
62 | }
63 |
64 | private List getTests(DataSource dataSource) {
65 | return new JdbcTemplate(dataSource)
66 | .queryForList(TEST_QUERY, String.class);
67 | }
68 | }
--------------------------------------------------------------------------------
/spring-boot-multidatasource-sample/spring-boot-multidatasource-sample-default/src/test/java/org/sbcoba/springboot/multidatasource/sample/MultiDataSourceTests.java:
--------------------------------------------------------------------------------
1 | package org.sbcoba.springboot.multidatasource.sample;
2 |
3 | import org.junit.Before;
4 | import org.junit.ClassRule;
5 | import org.junit.Rule;
6 | import org.junit.Test;
7 | import org.junit.runner.RunWith;
8 | import org.junit.runners.Parameterized;
9 | import org.sbcoba.springboot.multidatasource.sample.web.*;
10 | import org.slf4j.Logger;
11 | import org.slf4j.LoggerFactory;
12 | import org.springframework.beans.factory.annotation.Autowired;
13 | import org.springframework.boot.test.SpringApplicationConfiguration;
14 | import org.springframework.http.MediaType;
15 | import org.springframework.test.context.junit4.rules.SpringClassRule;
16 | import org.springframework.test.context.junit4.rules.SpringMethodRule;
17 | import org.springframework.test.context.web.WebAppConfiguration;
18 | import org.springframework.test.web.servlet.MockMvc;
19 | import org.springframework.test.web.servlet.setup.MockMvcBuilders;
20 | import org.springframework.web.context.WebApplicationContext;
21 |
22 | import java.util.HashMap;
23 | import java.util.Map;
24 |
25 | import static org.junit.runners.Parameterized.*;
26 | import static org.hamcrest.core.StringStartsWith.startsWith;
27 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
28 | import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
29 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
30 |
31 | //@RunWith(SpringJUnit4ClassRunner.class)
32 | @RunWith(Parameterized.class)
33 | @SpringApplicationConfiguration(classes = SampleApplication.class)
34 | @WebAppConfiguration
35 | public class MultiDataSourceTests {
36 | private static final Logger log = LoggerFactory.getLogger(MultiDataSourceTests.class);
37 |
38 | @ClassRule
39 | public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule();
40 |
41 | @Rule
42 | public final SpringMethodRule springMethodRule = new SpringMethodRule();
43 |
44 | @Autowired
45 | WebApplicationContext wac;
46 |
47 | MockMvc mockMvc;
48 |
49 | @Parameter(0)
50 | public String testType;
51 |
52 | @Parameter(1)
53 | public String methodName;
54 |
55 | @Parameter(2)
56 | public String dataSourceName;
57 |
58 | @Parameters(name = "testType [{0}], methodName [{1}], dataSourceName [{2}]")
59 | public static String[][] datasourceData() {
60 | return new String[][] {
61 | { "default", "db1", "db1" },
62 | { "default", "db2", "db1" },
63 | { "default", "db3", "db1" },
64 | { "default", "db4", "db1" },
65 |
66 | { "method", "db1", "db1" },
67 | { "method", "db2", "db2" },
68 | { "method", "db3", "db3" },
69 | { "method", "db4", "db4" },
70 |
71 | { "custom", "db1", "db1" },
72 | { "custom", "db2", "db2" },
73 | { "custom", "db3", "db3" },
74 | { "custom", "db4", "db4" },
75 |
76 | { "class", "db1", "db2" },
77 | { "class", "db2", "db2" },
78 | { "class", "db3", "db2" },
79 | { "class", "db4", "db2" },
80 |
81 | { "package", "db1", "db4" },
82 | { "package", "db2", "db4" },
83 | { "package", "db3", "db4" },
84 | { "package", "db4", "db4" }
85 | };
86 | }
87 |
88 | private static Map> controllerMap = new HashMap>() {{
89 | put("default", TestController.class);
90 | put("method", TestByMethodController.class);
91 | put("custom", TestByCustomAnnotationController.class);
92 | put("class", TestByClassController.class);
93 | put("package", TestByPackageController.class);
94 | }};
95 |
96 |
97 | @Before
98 | public void init() {
99 | mockMvc = MockMvcBuilders.webAppContextSetup(wac)
100 | .alwaysDo(print())
101 | .alwaysExpect(status().is2xxSuccessful())
102 | .build();
103 | }
104 |
105 | @Test
106 | public void defaultDataSourceTest() throws Exception {
107 | log.debug("{} {} {}", testType, methodName, dataSourceName);
108 | mockMvc.perform(get("/" + testType + "/" + methodName).accept(MediaType.APPLICATION_JSON))
109 | .andExpect(handler().handlerType(controllerMap.get(testType)))
110 | .andExpect(handler().methodName(methodName))
111 | .andExpect(jsonPath("$[0]").value(startsWith(dataSourceName + " dataSource")));
112 | }
113 | }
--------------------------------------------------------------------------------
/spring-boot-multidatasource/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.sbcoba.springboot
7 | spring-boot-multidatasource-parent
8 | 0.3.0.BUILD-SNAPSHOT
9 | ../spring-boot-multidatasource-parent
10 |
11 |
12 | spring-boot-multidatasource
13 | jar
14 |
15 | spring-boot-multidatasource
16 | http://maven.apache.org
17 |
18 |
19 |
20 | org.springframework.boot
21 | spring-boot
22 |
23 |
24 |
25 | org.springframework.boot
26 | spring-boot-starter
27 |
28 |
29 |
30 | org.springframework.boot
31 | spring-boot-configuration-processor
32 | true
33 |
34 |
35 |
36 | org.springframework.boot
37 | spring-boot-starter-web
38 |
39 |
40 |
41 | org.springframework.boot
42 | spring-boot-starter-aop
43 |
44 |
45 |
46 | org.springframework.boot
47 | spring-boot-starter-jdbc
48 |
49 |
50 |
51 | org.springframework.boot
52 | spring-boot-starter-test
53 | test
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource/src/main/java/org/sbcoba/springboot/multidatasource/autoconfigure/DataSource.java:
--------------------------------------------------------------------------------
1 | package org.sbcoba.springboot.multidatasource.autoconfigure;
2 |
3 | import java.lang.annotation.*;
4 |
5 | /**
6 | * Choice DataSource annotation
7 | *
8 | * @author sbcoba
9 | *
10 | */
11 | @Target({ElementType.PACKAGE, ElementType.METHOD, ElementType.TYPE})
12 | @Retention(RetentionPolicy.RUNTIME)
13 | @Documented
14 | public @interface DataSource {
15 | /**
16 | * @return datasource name
17 | */
18 | String value();
19 | }
20 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource/src/main/java/org/sbcoba/springboot/multidatasource/autoconfigure/DataSourceFactory.java:
--------------------------------------------------------------------------------
1 | package org.sbcoba.springboot.multidatasource.autoconfigure;
2 |
3 | import org.springframework.beans.factory.FactoryBean;
4 | import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
5 | import org.springframework.core.env.Environment;
6 |
7 | import javax.sql.DataSource;
8 |
9 | import static org.sbcoba.springboot.multidatasource.autoconfigure.MultiDataSourceRegistrar.*;
10 |
11 | /**
12 | * DataSourceBuilder를 랩핑하여 DateSource를 생성하는 FactoryBean
13 | *
14 | * @author sbcoba
15 | */
16 | public class DataSourceFactory implements FactoryBean {
17 |
18 | private Class extends DataSource> type;
19 | private String url;
20 | private String driverClassName;
21 | private String username;
22 | private String password;
23 | private ClassLoader classLoader;
24 |
25 | private Environment environment;
26 | private String environmentPrefix;
27 |
28 |
29 | public DataSourceFactory() {
30 | }
31 |
32 | public void setClassLoader(ClassLoader classLoader) {
33 | this.classLoader = classLoader;
34 | }
35 |
36 | public void setType(Class extends DataSource> type) {
37 | this.type = type;
38 | }
39 |
40 | public void setUrl(String url) {
41 | this.url = url;
42 | }
43 |
44 | public void setDriverClassName(String driverClassName) {
45 | this.driverClassName = driverClassName;
46 | }
47 |
48 | public void setUsername(String username) {
49 | this.username = username;
50 | }
51 |
52 | public void setPassword(String password) {
53 | this.password = password;
54 | }
55 |
56 | public void setEnvironment(Environment environment) {
57 | this.environment = environment;
58 | }
59 |
60 | public void setEnvironmentPrefix(String environmentPrefix) {
61 | this.environmentPrefix = environmentPrefix;
62 | }
63 |
64 | @Override
65 | public DataSource getObject() throws Exception {
66 | DataSourceBuilder builder = DataSourceBuilder
67 | .create(classLoader)
68 | .driverClassName(driverClassName)
69 | .url(url)
70 | .username(username)
71 | .password(password);
72 | if (type != null) {
73 | builder.type(type);
74 | }
75 | DataSource dataSource = builder.build();
76 | bindProperties(dataSource, environmentPrefix, environment);
77 | return dataSource;
78 | }
79 |
80 | @Override
81 | public Class> getObjectType() {
82 | return javax.sql.DataSource.class;
83 | }
84 |
85 | @Override
86 | public boolean isSingleton() {
87 | return true;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource/src/main/java/org/sbcoba/springboot/multidatasource/autoconfigure/DataSourceNameContextHolder.java:
--------------------------------------------------------------------------------
1 | package org.sbcoba.springboot.multidatasource.autoconfigure;
2 |
3 | import org.springframework.util.Assert;
4 |
5 | /**
6 | * ThreadLocal에 DataSource명을 저장
7 | *
8 | * @author sbcoba
9 | */
10 | public class DataSourceNameContextHolder {
11 |
12 | private static final ThreadLocal contextHolder = new ThreadLocal();
13 |
14 | public static void setDataSourceName(String dataSourceName) {
15 | Assert.hasText(dataSourceName, "DataSource name must has text");
16 | contextHolder.set(dataSourceName);
17 | }
18 |
19 | public static String getDataSourceName() {
20 | return contextHolder.get();
21 | }
22 |
23 | public static void clear() {
24 | contextHolder.remove();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource/src/main/java/org/sbcoba/springboot/multidatasource/autoconfigure/DataSourceSet.java:
--------------------------------------------------------------------------------
1 | package org.sbcoba.springboot.multidatasource.autoconfigure;
2 |
3 | import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
4 |
5 | import javax.sql.DataSource;
6 |
7 | /**
8 | * DataSource and Properties set
9 | *
10 | * @author sbcoba
11 | */
12 | public class DataSourceSet {
13 |
14 | private javax.sql.DataSource dataSource;
15 | private DataSourceProperties dataSourceProperties;
16 |
17 | public DataSourceSet(DataSource dataSource, DataSourceProperties dataSourceProperties) {
18 | this.dataSource = dataSource;
19 | this.dataSourceProperties = dataSourceProperties;
20 | }
21 |
22 | public DataSourceSet(DataSource dataSource) {
23 | this.dataSource = dataSource;
24 | }
25 |
26 | public DataSource getDataSource() {
27 | return dataSource;
28 | }
29 |
30 | public DataSourceProperties getDataSourceProperties() {
31 | return dataSourceProperties;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource/src/main/java/org/sbcoba/springboot/multidatasource/autoconfigure/MultiDataSourceAutoConfiguration.java:
--------------------------------------------------------------------------------
1 | package org.sbcoba.springboot.multidatasource.autoconfigure;
2 |
3 | import org.aopalliance.intercept.MethodInterceptor;
4 | import org.sbcoba.springboot.multidatasource.autoconfigure.aop.AnnotationDeepMethodMatcher;
5 | import org.sbcoba.springboot.multidatasource.autoconfigure.aop.DataSourceAdvice;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 | import org.springframework.aop.ClassFilter;
9 | import org.springframework.aop.MethodMatcher;
10 | import org.springframework.aop.Pointcut;
11 | import org.springframework.aop.PointcutAdvisor;
12 | import org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor;
13 | import org.springframework.beans.factory.BeanFactoryUtils;
14 | import org.springframework.beans.factory.annotation.Autowired;
15 | import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
16 | import org.springframework.boot.autoconfigure.AutoConfigureAfter;
17 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
18 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
19 | import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
20 | import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
21 | import org.springframework.boot.context.properties.EnableConfigurationProperties;
22 | import org.springframework.context.ApplicationContext;
23 | import org.springframework.context.annotation.*;
24 | import org.springframework.core.Ordered;
25 | import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
26 | import org.springframework.util.Assert;
27 | import org.springframework.util.StringUtils;
28 |
29 | import java.util.ArrayList;
30 | import java.util.HashMap;
31 | import java.util.List;
32 | import java.util.Map;
33 |
34 | /**
35 | * Spring Boot AutoConfiguration 설정값
36 | *
37 | * @author sbcoba
38 | */
39 | @EnableConfigurationProperties(MultiDataSourceProperties.class)
40 | @ConditionalOnMissingBean(MultiDataSourceAutoConfiguration.class)
41 | @ConditionalOnProperty(prefix = "spring.multiDatasource", name = "enabled", havingValue = "true", matchIfMissing = true)
42 | @EnableAspectJAutoProxy(proxyTargetClass = true)
43 | @AutoConfigureAfter(DataSourceAutoConfiguration.class)
44 | @Import(MultiDataSourceRegistrar.class)
45 | @Configuration
46 | public class MultiDataSourceAutoConfiguration {
47 | private static final Logger log = LoggerFactory.getLogger(MultiDataSourceAutoConfiguration.class);
48 |
49 | @Autowired
50 | private ApplicationContext applicationContext;
51 |
52 | @Autowired
53 | private MultiDataSourceProperties multiDataSourceProperties;
54 |
55 | @Bean
56 | @Primary
57 | @ConditionalOnMissingBean(AbstractRoutingDataSource.class)
58 | public javax.sql.DataSource routeDataSource() {
59 | Map targetDataSourceSet = new HashMap();
60 | Map dataSourceProperties =
61 | multiDataSourceProperties.getDatasources();
62 | Map dataSourceMap =
63 | BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, javax.sql.DataSource.class);
64 | for (Map.Entry entry : dataSourceMap.entrySet()) {
65 | String datasourceName = entry.getKey();
66 | targetDataSourceSet.put(datasourceName,
67 | new DataSourceSet(entry.getValue(), dataSourceProperties.get(datasourceName)));
68 | }
69 | Assert.isTrue(targetDataSourceSet.size() != 0, "Datasource not found");
70 |
71 | MultiRoutingDataSource routingDataSource = new MultiRoutingDataSource();
72 | routingDataSource.setTargetDataSourceSet(targetDataSourceSet);
73 | routingDataSource.setDefaultTargetDataSource(getDefaultTargetDatasource(targetDataSourceSet));
74 | return routingDataSource;
75 | }
76 |
77 | private Object getDefaultTargetDatasource(Map targetDataSourceSet) {
78 | String datasourceName = multiDataSourceProperties.getDefaultDatasourceName();
79 | if (!StringUtils.hasText(datasourceName)) {
80 | datasourceName = multiDataSourceProperties.getDatasources().keySet().toArray(new String[]{})[0];
81 | }
82 | DataSourceSet dataSourceSet = targetDataSourceSet.get(datasourceName);
83 | Assert.notNull(dataSourceSet, "DataSource ("+ datasourceName + ") is not found!");
84 | log.info("Default DataSource : {}", datasourceName);
85 | return dataSourceSet.getDataSource();
86 | }
87 |
88 | @Bean
89 | @DependsOn("routeDataSource")
90 | public MultiDataSourceInitializer multiDataSourceInitializer() {
91 | return new MultiDataSourceInitializer();
92 | }
93 |
94 | @Bean
95 | @ConditionalOnMissingBean
96 | public MethodInterceptor dataSourceAdvice() {
97 | DataSourceAdvice dataSourceAdvice = new DataSourceAdvice();
98 | dataSourceAdvice.setOrder(Ordered.LOWEST_PRECEDENCE);
99 | return dataSourceAdvice;
100 | }
101 |
102 | @Bean
103 | @ConditionalOnMissingBean
104 | public PointcutAdvisor potincutAdviser(MethodInterceptor dataSourceAdvice) {
105 | DefaultBeanFactoryPointcutAdvisor advisor = new DefaultBeanFactoryPointcutAdvisor();
106 | advisor.setPointcut(dataSourcePointcut());
107 | advisor.setAdvice(dataSourceAdvice);
108 | return advisor;
109 | }
110 |
111 | @Bean
112 | @ConditionalOnMissingBean
113 | public Pointcut dataSourcePointcut() {
114 | List basePackages = multiDataSourceProperties.getBasePackages();
115 | if (basePackages == null || basePackages.size() == 0) {
116 | basePackages = AutoConfigurationPackages.get(applicationContext);
117 | }
118 | return new DataSourcePointcut(basePackages);
119 | }
120 |
121 | static class DataSourcePointcut implements Pointcut {
122 | private List packages = new ArrayList();
123 |
124 | public DataSourcePointcut(List packages) {
125 | this.packages = packages;
126 | }
127 |
128 | @Override
129 | public ClassFilter getClassFilter() {
130 | return new ClassFilter() {
131 | @Override
132 | public boolean matches(Class> clazz) {
133 | for (String packageName : packages) {
134 | log.debug("{} | {} : {}", packageName, clazz.getName(), packageName.startsWith(clazz.getName()));
135 | if (clazz.getName().startsWith(packageName)){
136 | return true;
137 | }
138 | }
139 | return false;
140 | }
141 | };
142 | }
143 |
144 | @Override
145 | public MethodMatcher getMethodMatcher() {
146 | return new AnnotationDeepMethodMatcher(DataSource.class);
147 | }
148 |
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource/src/main/java/org/sbcoba/springboot/multidatasource/autoconfigure/MultiDataSourceInitializedEvent.java:
--------------------------------------------------------------------------------
1 | package org.sbcoba.springboot.multidatasource.autoconfigure;
2 |
3 | import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
4 | import org.springframework.context.ApplicationEvent;
5 |
6 | public class MultiDataSourceInitializedEvent extends ApplicationEvent {
7 | private String dataSourceName;
8 | private DataSourceProperties dataSourceProperties;
9 | /**
10 | * Create a new {@link MultiDataSourceInitializedEvent}.
11 | * @param source the source {@link javax.sql.DataSource}.
12 | */
13 | public MultiDataSourceInitializedEvent(javax.sql.DataSource source,
14 | String dataSourceName,
15 | DataSourceProperties dataSourceProperties) {
16 | super(source);
17 | this.dataSourceName = dataSourceName;
18 | this.dataSourceProperties = dataSourceProperties;
19 | }
20 |
21 | public DataSourceProperties getDataSourceProperties() {
22 | return dataSourceProperties;
23 | }
24 |
25 | public String getDataSourceName() {
26 | return dataSourceName;
27 | }
28 | }
--------------------------------------------------------------------------------
/spring-boot-multidatasource/src/main/java/org/sbcoba/springboot/multidatasource/autoconfigure/MultiDataSourceInitializer.java:
--------------------------------------------------------------------------------
1 | package org.sbcoba.springboot.multidatasource.autoconfigure;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
7 | import org.springframework.context.ApplicationListener;
8 | import org.springframework.context.ConfigurableApplicationContext;
9 | import org.springframework.core.io.Resource;
10 | import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils;
11 | import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
12 | import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
13 | import org.springframework.util.StringUtils;
14 |
15 | import javax.annotation.PostConstruct;
16 | import javax.sql.DataSource;
17 | import java.io.IOException;
18 | import java.util.ArrayList;
19 | import java.util.List;
20 | import java.util.Map;
21 | import java.util.concurrent.ConcurrentHashMap;
22 |
23 | /**
24 | * DataSource 각각 초기화
25 | * @author sbcoba
26 | */
27 | class MultiDataSourceInitializer implements ApplicationListener {
28 | private final Logger log = LoggerFactory.getLogger(MultiDataSourceInitializer.class);
29 |
30 | @Autowired
31 | private ConfigurableApplicationContext applicationContext;
32 | private MultiRoutingDataSource multiRoutingDataSource;
33 | private Map dataSourceInitializedMap = new ConcurrentHashMap();
34 |
35 | @PostConstruct
36 | public void init() {
37 | if (this.applicationContext.getBeanNamesForType(AbstractRoutingDataSource.class, false,
38 | false).length > 0) {
39 | this.multiRoutingDataSource = this.applicationContext.getBean(MultiRoutingDataSource.class);
40 | }
41 | if (this.multiRoutingDataSource == null) {
42 | log.debug("No DataSource found so not initializing");
43 | return;
44 | }
45 | runSchemaScripts();
46 | }
47 |
48 | private void runSchemaScripts() {
49 | Map targetDataSourceSet = multiRoutingDataSource.getTargetDataSourceSet();
50 | for (Map.Entry entry : targetDataSourceSet.entrySet()) {
51 | String dataSourceName = entry.getKey();
52 | DataSourceSet dataSourceSet = entry.getValue();
53 | DataSource dataSource = dataSourceSet.getDataSource();
54 | DataSourceProperties dataSourceProperties = dataSourceSet.getDataSourceProperties();
55 | if (dataSourceProperties == null) {
56 | // ignore
57 | continue;
58 | }
59 | if (!dataSourceProperties.isInitialize()) {
60 | log.debug("Initialization disabled (not running DDL scripts)");
61 | continue;
62 | }
63 | List scripts = getScripts(dataSourceProperties.getSchema(), "schema", dataSourceProperties);
64 | if (!scripts.isEmpty()) {
65 | runScripts(scripts, dataSource, dataSourceProperties);
66 | try {
67 | this.applicationContext.publishEvent(
68 | new MultiDataSourceInitializedEvent(dataSource, dataSourceName, dataSourceProperties));
69 | // The listener might not be registered yet, so don't rely on it.
70 | Boolean initialized = dataSourceInitializedMap.get(dataSourceName);
71 | if (initialized == null || initialized == false) {
72 | runDataScripts(dataSource, dataSourceProperties);
73 | dataSourceInitializedMap.put(dataSourceName, true);
74 | }
75 |
76 | } catch (IllegalStateException ex) {
77 | log.warn("Could not send event to complete DataSource initialization ("
78 | + ex.getMessage() + ")");
79 | }
80 | }
81 | }
82 | }
83 |
84 | @Override
85 | public void onApplicationEvent(MultiDataSourceInitializedEvent event) {
86 | DataSourceProperties dataSourceProperties = event.getDataSourceProperties();
87 | if (!dataSourceProperties.isInitialize()) {
88 | log.debug("Initialization disabled (not running data scripts)");
89 | return;
90 | }
91 | String dataSourceName = event.getDataSourceName();
92 | Boolean initialized = dataSourceInitializedMap.get(dataSourceName);
93 | if (initialized == null || initialized == false) {
94 | runDataScripts((javax.sql.DataSource) event.getSource(), dataSourceProperties);
95 | dataSourceInitializedMap.put(dataSourceName, true);
96 | }
97 | }
98 |
99 | private void runDataScripts(javax.sql.DataSource dataSource,
100 | DataSourceProperties dataSourceProperties) {
101 | List scripts = getScripts(dataSourceProperties.getData(), "data", dataSourceProperties);
102 | runScripts(scripts, dataSource, dataSourceProperties);
103 | }
104 |
105 | private List getScripts(String locations, String fallback, DataSourceProperties dataSourceProperties) {
106 | if (locations == null) {
107 | String platform = dataSourceProperties.getPlatform();
108 | locations = "classpath*:" + fallback + "-" + platform + ".sql,";
109 | locations += "classpath*:" + fallback + ".sql";
110 | }
111 | return getResources(locations);
112 | }
113 |
114 | private List getResources(String locations) {
115 | List resources = new ArrayList();
116 | for (String location : StringUtils.commaDelimitedListToStringArray(locations)) {
117 | try {
118 | for (Resource resource : this.applicationContext.getResources(location)) {
119 | if (resource.exists()) {
120 | resources.add(resource);
121 | }
122 | }
123 | } catch (IOException ex) {
124 | throw new IllegalStateException(
125 | "Unable to load resource from " + location, ex);
126 | }
127 | }
128 | return resources;
129 | }
130 |
131 | private void runScripts(List resources, DataSource dataSource,
132 | DataSourceProperties dataSourceProperties) {
133 | if (resources.isEmpty()) {
134 | return;
135 | }
136 | ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
137 | populator.setContinueOnError(dataSourceProperties.isContinueOnError());
138 | populator.setSeparator(dataSourceProperties.getSeparator());
139 | if (dataSourceProperties.getSqlScriptEncoding() != null) {
140 | populator.setSqlScriptEncoding(dataSourceProperties.getSqlScriptEncoding().name());
141 | }
142 | for (Resource resource : resources) {
143 | populator.addScript(resource);
144 | }
145 | DatabasePopulatorUtils.execute(populator, dataSource);
146 | }
147 |
148 | }
149 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource/src/main/java/org/sbcoba/springboot/multidatasource/autoconfigure/MultiDataSourceProperties.java:
--------------------------------------------------------------------------------
1 | package org.sbcoba.springboot.multidatasource.autoconfigure;
2 |
3 | import org.springframework.beans.factory.BeanClassLoaderAware;
4 | import org.springframework.beans.factory.InitializingBean;
5 | import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
6 | import org.springframework.boot.context.properties.ConfigurationProperties;
7 | import org.springframework.context.EnvironmentAware;
8 | import org.springframework.core.env.Environment;
9 |
10 | import java.util.ArrayList;
11 | import java.util.HashMap;
12 | import java.util.List;
13 | import java.util.Map;
14 |
15 | /**
16 | * 다중의 datasource 정보를 설정하기 위한 Properties
17 | *
18 | * @author sbcoba
19 | */
20 | @ConfigurationProperties(MultiDataSourceProperties.PREFIX)
21 | public class MultiDataSourceProperties
22 | implements BeanClassLoaderAware, EnvironmentAware, InitializingBean {
23 |
24 | public static final String PREFIX = "spring.multiDatasource";
25 |
26 | private List basePackages = new ArrayList();
27 | private String defaultDatasourceName;
28 | private Map datasources = new HashMap();
29 |
30 | public List getBasePackages() {
31 | return basePackages;
32 | }
33 |
34 | public void setBasePackages(List basePackages) {
35 | this.basePackages = basePackages;
36 | }
37 |
38 | public String getDefaultDatasourceName() {
39 | return defaultDatasourceName;
40 | }
41 |
42 | public void setDefaultDatasourceName(String defaultDatasourceName) {
43 | this.defaultDatasourceName = defaultDatasourceName;
44 | }
45 |
46 | public Map getDatasources() {
47 | return datasources;
48 | }
49 |
50 | public void setDatasources(Map datasources) {
51 | this.datasources = datasources;
52 | }
53 |
54 | @Override
55 | public void setBeanClassLoader(ClassLoader classLoader) {
56 | for (DataSourceProperties dataSource : datasources.values()) {
57 | dataSource.setBeanClassLoader(classLoader);
58 | }
59 | }
60 |
61 | @Override
62 | public void setEnvironment(Environment environment) {
63 | for (DataSourceProperties dataSource : datasources.values()) {
64 | dataSource.setEnvironment(environment);
65 | }
66 | }
67 |
68 | @Override
69 | public void afterPropertiesSet() throws Exception {
70 | for (DataSourceProperties dataSource : datasources.values()) {
71 | dataSource.afterPropertiesSet();
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource/src/main/java/org/sbcoba/springboot/multidatasource/autoconfigure/MultiDataSourceRegistrar.java:
--------------------------------------------------------------------------------
1 | package org.sbcoba.springboot.multidatasource.autoconfigure;
2 |
3 | import org.springframework.beans.factory.BeanCreationException;
4 | import org.springframework.beans.factory.annotation.Qualifier;
5 | import org.springframework.beans.factory.support.AbstractBeanDefinition;
6 | import org.springframework.beans.factory.support.AutowireCandidateQualifier;
7 | import org.springframework.beans.factory.support.BeanDefinitionBuilder;
8 | import org.springframework.beans.factory.support.BeanDefinitionRegistry;
9 | import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
10 | import org.springframework.boot.bind.PropertiesConfigurationFactory;
11 | import org.springframework.boot.context.properties.ConfigurationProperties;
12 | import org.springframework.context.EnvironmentAware;
13 | import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
14 | import org.springframework.core.annotation.AnnotationUtils;
15 | import org.springframework.core.convert.support.DefaultConversionService;
16 | import org.springframework.core.env.ConfigurableEnvironment;
17 | import org.springframework.core.env.Environment;
18 | import org.springframework.core.env.MutablePropertySources;
19 | import org.springframework.core.type.AnnotationMetadata;
20 | import org.springframework.util.StringUtils;
21 | import org.springframework.validation.BindException;
22 |
23 | import java.util.Map;
24 |
25 | /**
26 | * properties로 부터 가져온 Multi DataSource DB 정보를 기초로
27 | * DataSource 객체생성 후 Spring 컨테이너로 등록
28 | *
29 | * @author sbcoba
30 | */
31 | public class MultiDataSourceRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {
32 |
33 | private Environment environment;
34 |
35 | @Override
36 | public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
37 |
38 | ConfigurationProperties annotation =
39 | AnnotationUtils.findAnnotation(MultiDataSourceProperties.class, ConfigurationProperties.class);
40 | String prefix = StringUtils.hasText(annotation.value()) ? annotation.value() : annotation.prefix();
41 |
42 | MultiDataSourceProperties multiDataSourceProperties = new MultiDataSourceProperties();
43 | MultiDataSourceRegistrar.bindProperties(multiDataSourceProperties, prefix, environment);
44 |
45 | Map dataSourceProperties = multiDataSourceProperties.getDatasources();
46 | for (Map.Entry entry : dataSourceProperties.entrySet()) {
47 | String datasourceName = entry.getKey();
48 | DataSourceProperties properties = entry.getValue();
49 | BeanDefinitionBuilder bdb = BeanDefinitionBuilder.rootBeanDefinition(DataSourceFactory.class)
50 | .addPropertyValue("classLoader", properties.getClassLoader())
51 | .addPropertyValue("driverClassName", properties.getDriverClassName())
52 | .addPropertyValue("url", properties.getUrl())
53 | .addPropertyValue("username", properties.getUsername())
54 | .addPropertyValue("password", properties.getPassword())
55 | .addPropertyValue("environment", environment)
56 | .addPropertyValue("environmentPrefix", prefix + ".datasources." + datasourceName);
57 | if (properties.getType() != null) {
58 | bdb.addPropertyValue("type", properties.getType());
59 | }
60 | AbstractBeanDefinition beanDefinition = bdb.getBeanDefinition();
61 | beanDefinition.addQualifier(new AutowireCandidateQualifier(Qualifier.class, datasourceName));
62 | registry.registerBeanDefinition(datasourceName, beanDefinition);
63 | }
64 | }
65 |
66 | /**
67 | * 객체와 environment 정보를 Binding
68 | *
69 | * @param t Binding 대상 객체
70 | * @param prefix environment 의 prefix
71 | * @param environment Spring 에서 관리하는 환경정보 Bean
72 | * @param Binding 대상 타입
73 | * @return Binding 완료된 객체
74 | */
75 | static T bindProperties(T t, String prefix, Environment environment) {
76 | PropertiesConfigurationFactory factory = new PropertiesConfigurationFactory(t);
77 | factory.setConversionService(new DefaultConversionService());
78 | factory.setTargetName(prefix);
79 |
80 | if (environment instanceof ConfigurableEnvironment) {
81 | ConfigurableEnvironment configurableEnvironment = (ConfigurableEnvironment) environment;
82 | MutablePropertySources propertySources = configurableEnvironment.getPropertySources();
83 | factory.setPropertySources(propertySources);
84 | }
85 |
86 | try {
87 | factory.bindPropertiesToTarget();
88 | } catch (BindException e) {
89 | throw new BeanCreationException("MultiDataSourceProperties", "Could not bind properties to "
90 | + t.getClass() + " (" + prefix + ")", e);
91 | }
92 | return t;
93 | }
94 |
95 | @Override
96 | public void setEnvironment(Environment environment) {
97 | this.environment = environment;
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/spring-boot-multidatasource/src/main/java/org/sbcoba/springboot/multidatasource/autoconfigure/MultiRoutingDataSource.java:
--------------------------------------------------------------------------------
1 | package org.sbcoba.springboot.multidatasource.autoconfigure;
2 |
3 | import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
4 |
5 | import java.util.HashMap;
6 | import java.util.Map;
7 |
8 | /**
9 | * DataSource Routing
10 | *
11 | * @author sbcoba
12 | */
13 | public class MultiRoutingDataSource extends AbstractRoutingDataSource {
14 | private Map targetDataSourceSet;
15 |
16 | @Override
17 | protected Object determineCurrentLookupKey() {
18 | return DataSourceNameContextHolder.getDataSourceName();
19 | }
20 |
21 | public void setTargetDataSourceSet(Map targetDataSourceSet) {
22 | Map