├── .github
└── workflows
│ └── maven.yml
├── .gitignore
├── LICENSE.txt
├── README.md
├── TODO.txt
├── pom.xml
└── src
├── main
└── java
│ └── ch
│ └── digitalfondue
│ └── npjt
│ ├── AffectedRowCountAndKey.java
│ ├── AutoGeneratedKey.java
│ ├── Bind.java
│ ├── ConstructorAnnotationRowMapper.java
│ ├── EnableNpjt.java
│ ├── QueriesOverride.java
│ ├── Query.java
│ ├── QueryFactory.java
│ ├── QueryOverride.java
│ ├── QueryRepository.java
│ ├── QueryType.java
│ ├── RepositoriesDefinitionRegistrar.java
│ └── mapper
│ ├── ColumnMapper.java
│ ├── ColumnMapperFactory.java
│ ├── DefaultMapper.java
│ ├── EnumMapper.java
│ ├── InstantMapper.java
│ ├── LocalDateMapper.java
│ ├── LocalDateTimeMapper.java
│ ├── ParameterConverter.java
│ └── ZonedDateTimeMapper.java
└── test
└── java
└── ch
└── digitalfondue
└── npjt
├── ConstructorAnnotationRowMapperTest.java
├── QueryFactoryTest.java
├── QueryRepositoryScannerTest.java
├── QueryScannerConfiguration.java
├── TestJdbcConfiguration.java
├── mapper
├── DefaultMapperTest.java
├── EnumMapperTest.java
└── ZonedDateTimeMapperTest.java
└── query
├── AutogeneratedKeyQueriesTest.java
├── BooleanQueriesTest.java
├── CustomJSONQueriesTest.java
├── DateTimeQueriesTest.java
├── EnumQueriesTest.java
├── QueryRepo.java
├── SimpleQueriesTest.java
├── customfactory
└── CustomJSONQueriesWithCustomQueryFactoryTest.java
└── deeper
└── QueryRepo2.java
/.github/workflows/maven.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a Java project with Maven
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
3 |
4 | name: Java CI with Maven
5 |
6 | on:
7 | push:
8 | branches: [ master ]
9 | pull_request:
10 | branches: [ master ]
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: ubuntu-latest
16 | strategy:
17 | matrix:
18 | java: ['1.8','11','12','13', '14']
19 |
20 | steps:
21 | - uses: actions/checkout@v2
22 | - name: Set up JDK ${{matrix.java}}
23 | uses: actions/setup-java@v1
24 | with:
25 | java-version: ${{matrix.java}}
26 | - name: Maven -v
27 | run: mvn -v
28 | - name: Cache Maven packages
29 | uses: actions/cache@v2
30 | with:
31 | path: ~/.m2
32 | key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
33 | restore-keys: ${{ runner.os }}-m2
34 | - name: Build with Maven
35 | run: mvn -B package --file pom.xml
36 | # don't work at the moment it seems, see https://github.com/trautonen/coveralls-maven-plugin/issues/136
37 | # - name: Coverage
38 | # run: mvn jacoco:report coveralls:report
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | .classpath
3 | .project
4 | .settings/
5 | .idea/
6 | .gradle/
7 | build/
8 | *.iml
9 | *.ipr
10 | *.iws
11 | /build/
12 | .gradle/
13 | deploy*.sh
14 | custom.jvmargs
15 | classes/
16 | /target/
17 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | npjt-extra
2 | ==========
3 |
4 | [](https://github.com/digitalfondue/npjt-extra/actions?query=workflow%3A%22Java+CI+with+Maven%22)
5 | [](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22ch.digitalfondue.npjt-extra%22%20AND%20a%3A%22npjt-extra%22)
6 |
7 | A small layer over Spring's NamedParameterJdbcTemplate, it provide a similar interface as:
8 |
9 | - the jdbi object api (http://jdbi.org/#__sqlquery)
10 | - the spring-data-jpa named parameters api (http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.named-parameters)
11 |
12 | With some extra goodies. Requires java8 or later.
13 |
14 | Note: this project predates spring-data-jdbc.
15 |
16 | See the Use and examples section.
17 |
18 | ## Download
19 |
20 | ### Maven
21 |
22 | ```xml
23 |
24 | ch.digitalfondue.npjt-extra
25 | npjt-extra
26 | 2.0.4
27 |
28 | ```
29 |
30 | ### Gradle
31 |
32 | ```
33 | compile "ch.digitalfondue.npjt-extra:npjt-extra:2.0.4"
34 | ```
35 |
36 | ## Use and examples
37 |
38 | npjt-extra is composed of 3 parts:
39 |
40 | - a default RowMapper (annotation based)
41 | - an interface based query repository
42 | - the configuration classes
43 |
44 | ### RowMapping definition
45 |
46 | For mapping a row to a class, npjt-extra offer a default row mapper that require the following restriction:
47 |
48 | - the class must have only **one public constructor** (this restriction could be lifted off).
49 | - each of the constructor argument must have a @Column annotation that map the column name to the parameter.
50 |
51 | This constructor approach is for promoting an immutable model.
52 |
53 | *IF* you cannot map using this strategy, you can specify the mapper class in the @Query and @QueryOverride annotation.
54 | Your class must implement `org.springframework.jdbc.core.RowMapper`.
55 | See below in the "Basic use" section.
56 |
57 | Example:
58 |
59 | ```java
60 |
61 | import ch.digitalfondue.npjt.ConstructorAnnotationRowMapper.Column;
62 |
63 | public static class Conf {
64 |
65 | final String key;
66 | final String value;
67 |
68 | public Conf(@Column("CONF_KEY") String key, @Column("CONF_VALUE") String value) {
69 | this.key = key;
70 | this.value = value;
71 | }
72 | }
73 |
74 | ```
75 |
76 | As you can probably guess, this class will map a ResultSet containing 2 column named "CONF_KEY" and "CONF_VALUE".
77 |
78 | ### Query repository definition
79 |
80 | npjt-extra generate a proxy from the interface defined by the user.
81 |
82 | The rules are simple:
83 |
84 | - you can define a method without parameter that return NamedParameterJdbcTemplate: the proxy will return the underlying NamedParameterJdbcTemplate.
85 | - you can define default methods
86 | - all the others methods must contain the @Query(...) annotation
87 |
88 | #### Basic use
89 |
90 | A basic "query repository" will be similar to (using the Conf class defined before):
91 |
92 | ```java
93 |
94 | import java.util.List;
95 | import ch.digitalfondue.npjt.Bind;
96 | import ch.digitalfondue.npjt.Query;
97 |
98 | public interface MySimpleQueries {
99 |
100 | /** insert a key,value pair, return the number of affected rows */
101 | @Query("INSERT INTO LA_CONF(CONF_KEY, CONF_VALUE) VALUES(:key, :value)")
102 | int insertValue(@Bind("key") String key, @Bind("value") String value);
103 |
104 | /**
105 | * find a single element, as the underlying NamedParameterJdbcTemplate,
106 | * it will launch an exception if there are 0 or more than 1 object
107 | */
108 | @Query("SELECT * FROM LA_CONF WHERE CONF_KEY = :key")
109 | Conf findByKey(@Bind("key") String key);
110 |
111 | /** It will map multiple values too */
112 | @Query("SELECT * FROM LA_CONF")
113 | List findAll();
114 |
115 |
116 | /** You can override the default mapper and specify your own */
117 | @Query(value = "SELECT * FROM LA_CONF", mapper = MyConfMapper.class)
118 | List findAllCustomMapper();
119 |
120 |
121 | /** You can search "simple" types too if they are supported by spring jdbc */
122 | @Query("SELECT CONF_VALUE FROM LA_CONF WHERE CONF_KEY = 'BLA'")
123 | String findBla();
124 |
125 | /** You can search Lists of "simple" types too if they are supported by spring jdbc */
126 | @Query("SELECT CONF_KEY FROM LA_CONF")
127 | List findAllKeys();
128 |
129 | }
130 |
131 | ```
132 |
133 | The custom mapper specified in the annotation of the method findAllCustomMapper must implement `org.springframework.jdbc.core.RowMapper`.
134 |
135 | #### Query override
136 |
137 | If you want to support multiple DB which have some slightly different syntax you can override a query:
138 |
139 | For example:
140 |
141 | ```java
142 |
143 | import ch.digitalfondue.npjt.Query;
144 | import ch.digitalfondue.npjt.QueryOverride;
145 | import ch.digitalfondue.npjt.QueriesOverride;
146 |
147 | public interface QueryTest {
148 | @Query("SELECT * FROM LA_CONF")
149 | @QueriesOverride({
150 | @QueryOverride(db = "MYSQL", value = "SELECT * FROM LA_CONF_MYSQL"),
151 | @QueryOverride(db = "PGSQL", value = "SELECT * FROM LA_CONF_PGSQL")
152 | })
153 | List findAll();
154 | }
155 | ```
156 |
157 | When creating the ch.digitalfondue.npjt.QueryFactory that will generate the repositories, you must specify
158 | 2 parameters: first the **DB** name and second the DataSource.
159 |
160 |
161 | If the db name match the db parameter specified in a @QueryOverride, the associated String value will be used.
162 |
163 | *Note*: if you are defining a custom mapper in the @Query annotation, _you_ must specify it in the @QueryOverride too!
164 |
165 | #### Fetch generated keys
166 |
167 | If you have a table with an auto generated key and you want to get the value after your insert, you can set the return
168 | type to `AffectedRowCountAndKey`.
169 |
170 | Example:
171 |
172 | You have a table (HSQLDB) defined as:
173 |
174 | ```sql
175 | CREATE TABLE LA_AUTO (ID INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY NOT NULL, VALUE CLOB NOT NULL);
176 | ```
177 |
178 | In your QueryRepository you define:
179 |
180 | ```java
181 |
182 | public interface AutogeneratedKeyQueries {
183 |
184 | @Query("INSERT INTO LA_AUTO(VALUE) VALUES (:value)")
185 | AffectedRowCountAndKey insert(@Bind("value") String value);
186 |
187 | }
188 | ```
189 |
190 | The returned AffectedRowCountAndKey will contain the ID of the inserted element. You can see in action in the test AutogeneratedKeyQueriesTest.java
191 |
192 | Notes:
193 |
194 | - in some cases (for example if you are using pgsql), you must define the column name of the generated identifier with the annotation `@AutoGeneratedKey("COLUMN_NAME")` as the DB even though there is a single generated key it will return more than one.
195 | - it's currently a basic implementation that support the simplest use cases: pull request or test cases that highlight a missing feature are welcome!
196 |
197 | #### Query templates
198 |
199 | If you only require to generate a query string which depend from the db type, you can define a query template.
200 |
201 | You need to define the type in the @Query annotation to QueryType.TEMPLATE and the return type of the query as String.
202 |
203 | For example:
204 |
205 | ```java
206 |
207 | public interface QueryTest {
208 |
209 | @Query(type = QueryType.TEMPLATE, value = "MY_TEMPLATE")
210 | @QueriesOverride({
211 | @QueryOverride(db = "MYSQL", value = "SELECT * FROM MY_TEMPLATE_MYSQL"),
212 | @QueryOverride(db = "PGSQL", value = "SELECT * FROM MY_TEMPLATE_PGSQL")
213 | })
214 | String template();
215 | }
216 | ```
217 |
218 | Calling template() will return "MY_TEMPLATE" (or the overridden values).
219 |
220 | ##### Optional
221 |
222 | You can wrap the returned object in a Optional. For example:
223 |
224 | ```java
225 | @Query("SELECT * FROM LA_CONF WHERE CONF_KEY = :key")
226 | Optional findByKey(@Bind("key") String key);
227 | ```
228 |
229 | Will work as expected. If the query return more than one object it will launch an exception like the unwrapped
230 | version.
231 |
232 | ##### Default methods in the interface
233 |
234 | You can add default methods too, for example, if you need some custom query directly with
235 | the NamedParameterJdbcTemplate:
236 |
237 | ```java
238 | public interface MySimpleQueries {
239 |
240 |
241 | @Query("INSERT INTO LA_CONF(CONF_KEY, CONF_VALUE) VALUES(:key, :value)")
242 | int insertValue(@Bind("key") String key, @Bind("value") String value);
243 |
244 | /** any method that return NamedParameterJdbcTemplate and has 0 arguments will return the underlying NamedParameterJdbcTemplate*/
245 | NamedParameterJdbcTemplate getNamedParameterJdbcTemplate();
246 |
247 | /** here your default method */
248 | default String defaultMethod(String key) {
249 | return getNamedParameterJdbcTemplate()
250 | .queryForObject("SELECT CONF_VALUE FROM LA_CONF WHERE CONF_KEY = :key", Collections.singletonMap("key", key), String.class);
251 | }
252 | }
253 | ```
254 |
255 | ##### LocalDate, LocalDateTime, Instant support
256 |
257 | npjt-extra support out of the box LocalDate, LocalDateTime and Instant both as a parameter of a interface method and as a mapped value.
258 |
259 |
260 | ### Configuration
261 |
262 | You can configure it in 2 ways.
263 |
264 | #### Using @EnableNpjt annotation
265 |
266 | First you need to annotate your query repository with the ch.digitalfondue.npjt.QueryRepository annotation.
267 |
268 | Note, you will need a DataSource configured.
269 |
270 | Then you only need to configure the packages to scan:
271 |
272 | ```java
273 |
274 | /** scan the packages "ch.digitalfondue.npjt.query" and "ch.digitalfondue.npjt.columnmapper" */
275 | @EnableNpjt(basePackages = {"ch.digitalfondue.npjt.query", "ch.digitalfondue.npjt.columnmapper"})
276 | public class MyConfig2 {
277 | }
278 |
279 | ```
280 |
281 | #### Manual instantiation
282 |
283 | Using the JavaConfig:
284 |
285 | ```java
286 |
287 | public class MyConfig {
288 |
289 | /** instantiate the interface MySimpleQueries */
290 | /** the db type could be another parameter :) */
291 | @Bean
292 | public MySimpleQueries getMySimpleQueries(DataSource dataSource) {
293 | return QueryFactory.from(MySimpleQueries.class, "HSQLDB", dataSource);
294 | }
295 |
296 | }
297 |
298 | ```
299 |
300 |
301 |
302 | All the annotated interfaces will be available in your spring context.
303 |
304 | ### Data type mapping
305 |
306 | By default, npjt-extra has the following input parameters/result set mapping facilities enabled:
307 |
308 | - a default parameter/result set mapper which use the same logic as the one from the jdbctemplate
309 | - a enum mapper that convert from/to a string representation
310 | - support ZonedDateTimeMapper, LocalDate, LocalDateTime and Instant
311 |
312 | You can add new mappers by exposing as a bean a `List` and a `List` (see example at: https://github.com/digitalfondue/npjt-extra/blob/master/src/test/java/ch/digitalfondue/npjt/query/CustomJSONQueriesTest.java#L125).
313 |
314 | If you want to configure from an empty list, you can define your custom `QueryFactory`: see full example at https://github.com/digitalfondue/npjt-extra/blob/master/src/test/java/ch/digitalfondue/npjt/query/customfactory/CustomJSONQueriesWithCustomQueryFactoryTest.java#L131
315 | where instead of calling `super.getDefaultFactories();` you can begin from an empty List.
316 |
317 | Then when using the @EnableNpjt you will need to specify the Factory:
318 |
319 | `@EnableNpjt(queryFactory = CustomQueryFactory.class, basePackages = {"ch.digitalfondue.npjt.query.customfactory"})`
320 |
321 |
322 | For both mappers, the application order is defined by the `order()` method. The smallest the `int` returned, the higher the priority.
323 |
324 |
325 | #### Input parameters mapping
326 |
327 | You must implement the `ch.digitalfondue.npjt.mapper.ParameterConverter` interface and register the converter in the queryFactory calling the `QueryFactory.addParameterConverters(ParameterConverter parameterConverter)` method.
328 |
329 | #### Result set objects mapping
330 |
331 | You must implement the `ch.digitalfondue.npjt.mapper.ColumnMapperFactory` interface and register the converter in the queryFactory calling the `addColumnMapperFactory(ColumnMapperFactory columnMapperFactory)` method.
332 |
333 |
334 | ## Javadoc
335 |
336 | - http://javadoc.io/doc/ch.digitalfondue.npjt-extra/npjt-extra/
337 |
338 | ## License
339 |
340 | The library is under The Apache Software License, Version 2.0
341 |
--------------------------------------------------------------------------------
/TODO.txt:
--------------------------------------------------------------------------------
1 | TODO
2 | ====
3 |
4 | - Expose the type mapping infrastructure for the custom mappers (if they implement a specific subtype of RowMapper)
5 | - a refactor on this part will be necessary
6 |
7 | - @AutoGeneratedKey has some shortcoming:
8 | - no automatic type conversion with the current infrastructure of ColumnMapper
9 | - single field name for all DB
10 |
11 | - Support stored procedures in the annotation(?)
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | 4.0.0
3 |
4 |
5 | org.sonatype.oss
6 | oss-parent
7 | 9
8 |
9 |
10 | ch.digitalfondue.npjt-extra
11 | npjt-extra
12 | 2.0.5-SNAPSHOT
13 | jar
14 | 2015
15 |
16 | npjt-extra
17 | https://github.com/digitalfondue/npjt-extra
18 |
19 |
20 |
21 | The Apache Software License, Version 2.0
22 | http://www.apache.org/licenses/LICENSE-2.0.txt
23 | repo
24 |
25 |
26 |
27 |
28 | scm:git:git@github.com:digitalfondue/npjt-extra.git
29 | scm:git:git@github.com:digitalfondue/npjt-extra.git
30 | https://github.com/digitalfondue/npjt-extra.git
31 | HEAD
32 |
33 |
34 |
35 |
36 | Sylvain Jermini
37 | sylvain.jermini@syjer.com
38 | digitalfondue
39 | http://digitalfondue.ch
40 |
41 |
42 |
43 |
44 | UTF-8
45 | 5.2.22.RELEASE
46 |
47 |
48 |
49 |
50 | org.springframework
51 | spring-jdbc
52 | ${org.springframework.version}
53 |
54 |
55 | org.springframework
56 | spring-context
57 | ${org.springframework.version}
58 |
59 |
60 |
61 |
62 |
63 | junit
64 | junit
65 | 4.13.1
66 | test
67 |
68 |
69 | org.mockito
70 | mockito-all
71 | 1.10.18
72 | test
73 |
74 |
75 | org.hsqldb
76 | hsqldb
77 | 2.3.1
78 | test
79 |
80 |
81 | org.postgresql
82 | postgresql
83 | 42.4.3
84 | test
85 |
86 |
87 | org.springframework
88 | spring-test
89 | ${org.springframework.version}
90 | test
91 |
92 |
93 | com.google.code.gson
94 | gson
95 | 2.8.9
96 | test
97 |
98 |
99 |
100 |
101 |
102 |
103 | org.apache.maven.plugins
104 | maven-javadoc-plugin
105 | 2.10.3
106 |
107 | 8
108 | -Xdoclint:none
109 |
110 |
111 |
112 | org.apache.maven.plugins
113 | maven-compiler-plugin
114 | 3.2
115 |
116 | 1.8
117 | 1.8
118 | 1.8
119 | 1.8
120 |
121 | -Xlint:deprecation
122 | -Xlint:unchecked
123 |
124 |
125 |
126 |
148 |
149 | org.eluder.coveralls
150 | coveralls-maven-plugin
151 | 4.3.0
152 |
153 |
154 | javax.xml.bind
155 | jaxb-api
156 | 2.3.1
157 |
158 |
159 |
160 |
161 | org.jacoco
162 | jacoco-maven-plugin
163 | 0.8.5
164 |
165 |
166 | prepare-agent
167 |
168 | prepare-agent
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 | org.apache.maven.plugins
180 | maven-pmd-plugin
181 | 3.3
182 |
183 |
184 | org.codehaus.mojo
185 | findbugs-maven-plugin
186 | 3.0.0
187 |
188 |
189 | org.codehaus.mojo
190 | jdepend-maven-plugin
191 | 2.0
192 |
193 |
194 |
195 |
196 |
197 |
198 | sign-artifacts
199 |
200 |
201 | sign
202 | true
203 |
204 |
205 |
206 |
207 |
208 | org.apache.maven.plugins
209 | maven-gpg-plugin
210 |
211 |
212 | sign-artifacts
213 | verify
214 |
215 | sign
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
--------------------------------------------------------------------------------
/src/main/java/ch/digitalfondue/npjt/AffectedRowCountAndKey.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt;
17 |
18 | public class AffectedRowCountAndKey {
19 |
20 | private final int affectedRowCount;
21 | private final T key;
22 |
23 | public AffectedRowCountAndKey(int affectedRowCount, T key) {
24 | this.affectedRowCount = affectedRowCount;
25 | this.key = key;
26 | }
27 |
28 | public int getAffectedRowCount() {
29 | return affectedRowCount;
30 | }
31 | public T getKey() {
32 | return key;
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/ch/digitalfondue/npjt/AutoGeneratedKey.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt;
17 |
18 | import java.lang.annotation.ElementType;
19 | import java.lang.annotation.Retention;
20 | import java.lang.annotation.RetentionPolicy;
21 | import java.lang.annotation.Target;
22 |
23 | /**
24 | * Marker annotation for retrieval of the latest auto-generated key.
25 | * All the method annotated with @AutoGeneratedKey must return a {@code AffectedRowCountAndKey}.
26 | */
27 | @Target(ElementType.METHOD)
28 | @Retention(RetentionPolicy.RUNTIME)
29 | public @interface AutoGeneratedKey {
30 | String value();
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/ch/digitalfondue/npjt/Bind.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt;
17 |
18 | import java.lang.annotation.ElementType;
19 | import java.lang.annotation.Retention;
20 | import java.lang.annotation.RetentionPolicy;
21 | import java.lang.annotation.Target;
22 |
23 | @Retention(RetentionPolicy.RUNTIME)
24 | @Target(ElementType.PARAMETER)
25 | public @interface Bind {
26 | String value();
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/ch/digitalfondue/npjt/ConstructorAnnotationRowMapper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt;
17 |
18 | import java.lang.annotation.Annotation;
19 | import java.lang.annotation.ElementType;
20 | import java.lang.annotation.Retention;
21 | import java.lang.annotation.RetentionPolicy;
22 | import java.lang.annotation.Target;
23 | import java.lang.reflect.Constructor;
24 | import java.sql.ResultSet;
25 | import java.sql.SQLException;
26 | import java.util.Collection;
27 |
28 | import org.springframework.jdbc.core.RowMapper;
29 | import org.springframework.util.Assert;
30 |
31 | import ch.digitalfondue.npjt.mapper.ColumnMapper;
32 | import ch.digitalfondue.npjt.mapper.ColumnMapperFactory;
33 |
34 | public class ConstructorAnnotationRowMapper implements RowMapper {
35 |
36 | private final Constructor con;
37 | private final ColumnMapper[] mappedColumn;
38 |
39 | /**
40 | * Check if the given class has the correct form.
41 | *
42 | *
43 | * must have exactly one public constructor.
44 | * must at least have one parameter.
45 | * all the parameters must be annotated with @Column annotation.
46 | *
47 | *
48 | * @param clazz
49 | * @return
50 | */
51 | public static boolean hasConstructorInTheCorrectForm(Class> clazz) {
52 |
53 | if (clazz.getConstructors().length != 1) {
54 | return false;
55 | }
56 |
57 | Constructor> con = clazz.getConstructors()[0];
58 |
59 | if (con.getParameterTypes().length == 0) {
60 | return false;
61 | }
62 |
63 | Annotation[][] parameterAnnotations = con.getParameterAnnotations();
64 | for (Annotation[] as : parameterAnnotations) {
65 | if (!hasColumnAnnotation(as)) {
66 | return false;
67 | }
68 | }
69 |
70 | return true;
71 | }
72 |
73 | private static boolean hasColumnAnnotation(Annotation[] as) {
74 | if (as == null || as.length == 0) {
75 | return false;
76 | }
77 | for (Annotation a : as) {
78 | if (a.annotationType().isAssignableFrom(Column.class)) {
79 | return true;
80 | }
81 | }
82 |
83 | return false;
84 | }
85 |
86 | @SuppressWarnings("unchecked")
87 | public ConstructorAnnotationRowMapper(Class clazz, Collection columnMapperFactories) {
88 | int constructorCount = clazz.getConstructors().length;
89 | Assert.isTrue(constructorCount == 1, "The class " + clazz.getName()
90 | + " must have exactly one public constructor, "
91 | + constructorCount + " are present");
92 |
93 | con = (Constructor) clazz.getConstructors()[0];
94 | mappedColumn = from(clazz, con.getParameterAnnotations(), con.getParameterTypes(), columnMapperFactories);
95 | }
96 |
97 | @Override
98 | public T mapRow(ResultSet rs, int rowNum) throws SQLException {
99 | Object[] vals = new Object[mappedColumn.length];
100 |
101 | for(int i = 0; i < mappedColumn.length; i++) {
102 | vals[i] = mappedColumn[i].getObject(rs);
103 | }
104 |
105 |
106 | try {
107 | return con.newInstance(vals);
108 | } catch (ReflectiveOperationException e) {
109 | throw new SQLException(e);
110 | } catch (IllegalArgumentException e) {
111 | throw new SQLException(
112 | "type mismatch between the expected one from the construct and the one passed,"
113 | + " check 1: some values are null and passed to primitive types 2: incompatible numeric types",
114 | e);
115 | }
116 | }
117 |
118 | private static ColumnMapper[] from(Class> clazz, Annotation[][] annotations, Class>[] paramTypes, Collection columnMapperFactories) {
119 | ColumnMapper[] res = new ColumnMapper[annotations.length];
120 | for (int i = 0; i < annotations.length; i++) {
121 | res[i] = findColumnAnnotationValue(clazz, i, annotations[i], paramTypes[i], columnMapperFactories);
122 | }
123 | return res;
124 | }
125 |
126 | private static ColumnMapper findColumnAnnotationValue(Class> clazz,
127 | int position, Annotation[] annotations, Class> paramType, Collection columnMapperFactories) {
128 |
129 | for (Annotation a : annotations) {
130 | if (Column.class.isAssignableFrom(a.annotationType())) {
131 | String name = ((Column) a).value();
132 | for(ColumnMapperFactory factory : columnMapperFactories) {
133 | if(factory.accept(paramType, annotations)) {
134 | return factory.build(name, paramType);
135 | }
136 | }
137 | throw new IllegalStateException(
138 | "Did not found any matching ColumnMapperFactory for class: "
139 | + clazz.getName()
140 | + " in constructor at position " + position);
141 | }
142 | }
143 |
144 | throw new IllegalStateException(
145 | "No annotation @Column found for class: " + clazz.getName()
146 | + " in constructor at position " + position);
147 | }
148 |
149 |
150 |
151 | @Retention(RetentionPolicy.RUNTIME)
152 | @Target(ElementType.PARAMETER)
153 | public @interface Column {
154 | /**
155 | * Column name
156 | *
157 | * @return
158 | */
159 | String value();
160 | }
161 |
162 | }
163 |
--------------------------------------------------------------------------------
/src/main/java/ch/digitalfondue/npjt/EnableNpjt.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2019 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt;
17 |
18 | import org.springframework.context.annotation.Import;
19 |
20 | import java.lang.annotation.*;
21 |
22 | @Target(ElementType.TYPE)
23 | @Retention(value = RetentionPolicy.RUNTIME)
24 | @Inherited
25 | @Import(RepositoriesDefinitionRegistrar.class)
26 | public @interface EnableNpjt {
27 | String activeDB() default "";
28 | String[] basePackages() default {};
29 | Class extends QueryFactory> queryFactory() default QueryFactory.class;
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/ch/digitalfondue/npjt/QueriesOverride.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt;
17 |
18 | import java.lang.annotation.ElementType;
19 | import java.lang.annotation.Retention;
20 | import java.lang.annotation.RetentionPolicy;
21 | import java.lang.annotation.Target;
22 |
23 | @Retention(RetentionPolicy.RUNTIME)
24 | @Target(ElementType.METHOD)
25 | public @interface QueriesOverride {
26 | QueryOverride[] value();
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/ch/digitalfondue/npjt/Query.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt;
17 |
18 | import java.lang.annotation.ElementType;
19 | import java.lang.annotation.Retention;
20 | import java.lang.annotation.RetentionPolicy;
21 | import java.lang.annotation.Target;
22 |
23 | import org.springframework.jdbc.core.RowMapper;
24 |
25 | @Retention(RetentionPolicy.RUNTIME)
26 | @Target(ElementType.METHOD)
27 | public @interface Query {
28 | String value();
29 | QueryType type() default QueryType.EXECUTE;
30 |
31 | @SuppressWarnings("rawtypes")
32 | Class extends RowMapper> mapper() default ConstructorAnnotationRowMapper.class;
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/ch/digitalfondue/npjt/QueryFactory.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2019 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt;
17 |
18 | import ch.digitalfondue.npjt.mapper.*;
19 | import org.springframework.beans.factory.FactoryBean;
20 | import org.springframework.beans.factory.annotation.Autowired;
21 | import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
22 | import org.springframework.util.ReflectionUtils;
23 |
24 | import javax.sql.DataSource;
25 | import java.lang.invoke.MethodHandle;
26 | import java.lang.invoke.MethodHandles;
27 | import java.lang.invoke.MethodType;
28 | import java.lang.reflect.Constructor;
29 | import java.lang.reflect.Method;
30 | import java.lang.reflect.Proxy;
31 | import java.util.*;
32 |
33 | public class QueryFactory implements FactoryBean {
34 |
35 | private final Class targetInterface;
36 | private final String activeDB;
37 |
38 | private DataSource dataSource;
39 | private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
40 | private List additionalColumnMapperFactories;
41 | private List additionalParameterConverters;
42 |
43 | public QueryFactory(Class targetInterface, String activeDB) {
44 | this.targetInterface = targetInterface;
45 | this.activeDB = activeDB;
46 | }
47 |
48 | public List getDefaultFactories() {
49 | return new ArrayList<>(Arrays.asList(
50 | new DefaultMapper.Factory(),
51 | new EnumMapper.Factory(),
52 | new LocalDateMapper.Factory(),
53 | new LocalDateTimeMapper.Factory(),
54 | new InstantMapper.Factory(),
55 | new ZonedDateTimeMapper.Factory())
56 | );
57 | }
58 |
59 | public List getDefaultParameterConverters() {
60 | return new ArrayList<>(Arrays.asList(
61 | new DefaultMapper.Converter(),
62 | new EnumMapper.Converter(),
63 | new LocalDateMapper.Converter(),
64 | new LocalDateTimeMapper.Converter(),
65 | new InstantMapper.Converter(),
66 | new ZonedDateTimeMapper.Converter())
67 | );
68 | }
69 |
70 | public static T from(Class clazz, String activeDB, DataSource dataSource) {
71 | return from(clazz, activeDB, dataSource, null, null);
72 | }
73 |
74 | public static T from(Class clazz, String activeDB,
75 | DataSource dataSource,
76 | List additionalColumnMappers, List additionalParameterConverters) {
77 | QueryFactory qf = new QueryFactory<>(clazz, activeDB);
78 | qf.setAdditionalColumnMapperFactories(additionalColumnMappers);
79 | qf.setAdditionalParameterConverters(additionalParameterConverters);
80 | qf.setDataSource(dataSource);
81 | return qf.getObject();
82 | }
83 |
84 | @Override
85 | public T getObject() {
86 | return from(targetInterface);
87 | }
88 |
89 | @Override
90 | public Class getObjectType() {
91 | return targetInterface;
92 | }
93 |
94 |
95 | //
96 | @Autowired
97 | public void setDataSource(DataSource dataSource) {
98 | this.dataSource = dataSource;
99 | }
100 |
101 | @Autowired(required = false)
102 | public void setJdbc(NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
103 | this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
104 | }
105 |
106 | @Autowired(required = false)
107 | public void setAdditionalColumnMapperFactories(List additionalColumnMapperFactories) {
108 | this.additionalColumnMapperFactories = additionalColumnMapperFactories;
109 | }
110 |
111 | @Autowired(required = false)
112 | public void setAdditionalParameterConverters(List additionalParameterConverters) {
113 | this.additionalParameterConverters = additionalParameterConverters;
114 | }
115 | //
116 |
117 | static class QueryTypeAndQuery {
118 | final QueryType type;
119 | final String query;
120 | final Class> rowMapperClass;
121 |
122 | QueryTypeAndQuery(QueryType type, String query, Class> rowMapperClass) {
123 | this.type = type;
124 | this.query = query;
125 | this.rowMapperClass = rowMapperClass;
126 | }
127 | }
128 |
129 |
130 | private QueryTypeAndQuery extractQueryAnnotation(Class> clazz, Method method) {
131 |
132 | Query q = method.getAnnotation(Query.class);
133 | QueriesOverride qs = method.getAnnotation(QueriesOverride.class);
134 |
135 | // only one @Query annotation, thus we return the value without checking the database
136 | if (qs == null) {
137 | return new QueryTypeAndQuery(q.type(), q.value(), q.mapper());
138 | }
139 |
140 | for (QueryOverride query : qs.value()) {
141 | if (query.db().equals(activeDB)) {
142 | return new QueryTypeAndQuery(q.type(), query.value(), query.mapper());
143 | }
144 | }
145 |
146 | return new QueryTypeAndQuery(q.type(), q.value(), q.mapper());
147 | }
148 |
149 | //from https://rmannibucau.wordpress.com/2014/03/27/java-8-default-interface-methods-and-jdk-dynamic-proxies/
150 | private static final Constructor LOOKUP_CONSTRUCTOR;
151 | private static final Method PRIVATE_LOOKUP_IN = ReflectionUtils.findMethod(MethodHandles.class, "privateLookupIn", Class.class, MethodHandles.Lookup.class);
152 |
153 | static {
154 | try {
155 | if(PRIVATE_LOOKUP_IN == null) {
156 | LOOKUP_CONSTRUCTOR = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
157 | if (!LOOKUP_CONSTRUCTOR.isAccessible()) { //TODO: is deprecated
158 | LOOKUP_CONSTRUCTOR.setAccessible(true);
159 | }
160 | } else {
161 | LOOKUP_CONSTRUCTOR = null;
162 | }
163 | } catch (NoSuchMethodException | SecurityException e) {
164 | throw new IllegalStateException(e);
165 | }
166 | }
167 |
168 |
169 | @SuppressWarnings("unchecked")
170 | private T from(final Class clazz) {
171 |
172 | SortedSet columnMapperFactories = new TreeSet<>(Comparator.comparingInt(ColumnMapperFactory::order).thenComparing(Objects::hashCode));
173 | columnMapperFactories.addAll(getDefaultFactories());
174 | if (additionalColumnMapperFactories != null) {
175 | columnMapperFactories.addAll(additionalColumnMapperFactories);
176 | }
177 |
178 | SortedSet parameterConverters = new TreeSet<>(Comparator.comparingInt(ParameterConverter::order).thenComparing(Objects::hashCode));
179 | parameterConverters.addAll(getDefaultParameterConverters());
180 | if (additionalParameterConverters != null) {
181 | parameterConverters.addAll(additionalParameterConverters);
182 | }
183 |
184 | NamedParameterJdbcTemplate jdbc = namedParameterJdbcTemplate == null ? new NamedParameterJdbcTemplate(dataSource) : namedParameterJdbcTemplate;
185 |
186 | return (T) Proxy.newProxyInstance(clazz.getClassLoader(),
187 | new Class[] { clazz }, (proxy, method, args) -> {
188 | boolean hasAnnotation = method.getAnnotation(Query.class) != null;
189 | if(hasAnnotation) {
190 | QueryTypeAndQuery qs = extractQueryAnnotation(clazz, method);
191 | return qs.type.apply(qs, jdbc, method, args, columnMapperFactories, parameterConverters);
192 | } else if(method.getReturnType().equals(NamedParameterJdbcTemplate.class) && args == null) {
193 | return jdbc;
194 | } else if(method.isDefault()) {
195 | final Class> declaringClass = method.getDeclaringClass();
196 | final MethodHandle handle;
197 | if(PRIVATE_LOOKUP_IN != null) {
198 | MethodType methodType = MethodType.methodType(method.getReturnType(), method.getParameterTypes());
199 | handle = MethodHandles.lookup().findSpecial(declaringClass, method.getName(), methodType, declaringClass);
200 | } else {
201 | handle = LOOKUP_CONSTRUCTOR.newInstance(declaringClass, MethodHandles.Lookup.PRIVATE).unreflectSpecial(method, declaringClass);
202 | }
203 | return handle.bindTo(proxy).invokeWithArguments(args);
204 | } else if (method.getDeclaringClass().equals(Object.class)) {
205 | String name = method.getName();
206 | switch (name) {
207 | case "equals": return proxy == args[0];
208 | case "hashCode": return System.identityHashCode(proxy);
209 | case "toString": return proxy.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(proxy));
210 | default: throw new IllegalStateException(String.valueOf(method));
211 | }
212 | } else {
213 | throw new IllegalArgumentException(String.format("missing @Query annotation for method %s in interface %s", method.getName(), clazz.getSimpleName()));
214 | }
215 | }
216 | );
217 | }
218 |
219 | }
220 |
--------------------------------------------------------------------------------
/src/main/java/ch/digitalfondue/npjt/QueryOverride.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt;
17 |
18 | import java.lang.annotation.ElementType;
19 | import java.lang.annotation.Retention;
20 | import java.lang.annotation.RetentionPolicy;
21 | import java.lang.annotation.Target;
22 |
23 | import org.springframework.jdbc.core.RowMapper;
24 |
25 | @Retention(RetentionPolicy.RUNTIME)
26 | @Target(ElementType.METHOD)
27 | public @interface QueryOverride {
28 | String value();
29 |
30 | String db();
31 |
32 | @SuppressWarnings("rawtypes")
33 | Class extends RowMapper> mapper() default ConstructorAnnotationRowMapper.class;
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/ch/digitalfondue/npjt/QueryRepository.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt;
17 |
18 | import java.lang.annotation.ElementType;
19 | import java.lang.annotation.Retention;
20 | import java.lang.annotation.RetentionPolicy;
21 | import java.lang.annotation.Target;
22 |
23 | @Retention(RetentionPolicy.RUNTIME)
24 | @Target(ElementType.TYPE)
25 | public @interface QueryRepository {
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/ch/digitalfondue/npjt/QueryType.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt;
17 |
18 | import java.lang.annotation.Annotation;
19 | import java.lang.reflect.InvocationTargetException;
20 | import java.lang.reflect.Method;
21 | import java.lang.reflect.ParameterizedType;
22 | import java.util.*;
23 | import java.util.concurrent.ConcurrentHashMap;
24 |
25 | import org.springframework.dao.IncorrectResultSizeDataAccessException;
26 | import org.springframework.jdbc.core.RowMapper;
27 | import org.springframework.jdbc.core.namedparam.EmptySqlParameterSource;
28 | import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
29 | import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
30 | import org.springframework.jdbc.core.namedparam.SqlParameterSource;
31 | import org.springframework.jdbc.support.GeneratedKeyHolder;
32 | import org.springframework.jdbc.support.KeyHolder;
33 | import org.springframework.util.NumberUtils;
34 | import org.springframework.util.StringUtils;
35 |
36 | import ch.digitalfondue.npjt.QueryFactory.QueryTypeAndQuery;
37 | import ch.digitalfondue.npjt.mapper.ColumnMapperFactory;
38 | import ch.digitalfondue.npjt.mapper.ParameterConverter;
39 |
40 | /**
41 | * Query Type:
42 | *
43 | *
44 | * TEMPLATE : we receive the string defined in @Query/@QueryOverride
45 | * annotation.
46 | * EXECUTE : the query will be executed. If it's a select, the result will
47 | * be mapped with a ConstructorAnnotationRowMapper if it has the correct form.
48 | *
49 | *
50 | */
51 | public enum QueryType {
52 |
53 | /**
54 | * Receive the string defined in @Query/@QueryOverride annotation.
55 | */
56 | TEMPLATE {
57 | @Override
58 | String apply(QueryTypeAndQuery queryTypeAndQuery, NamedParameterJdbcTemplate jdbc,
59 | Method method, Object[] args,
60 | SortedSet columnMapperFactories, SortedSet parameterConverters) {
61 | return queryTypeAndQuery.query;
62 | }
63 | },
64 |
65 | /**
66 | */
67 | EXECUTE {
68 |
69 | /**
70 | * Keep a mapping between a given class and a possible RowMapper.
71 | *
72 | * If the Class has the correct form, a ConstructorAnnotationRowMapper
73 | * will be built and the boolean set to true in the pair. If the class
74 | * has not the correct form, the boolean will be false and the class
75 | * will be used as it is in the jdbc template.
76 | */
77 | private final Map, HasRowmapper> cachedClassToMapper = new ConcurrentHashMap<>();
78 |
79 | @Override
80 | Object apply(QueryTypeAndQuery queryTypeAndQuery, NamedParameterJdbcTemplate jdbc,
81 | Method method, Object[] args,
82 | SortedSet columnMapperFactories, SortedSet parameterConverters) {
83 | JdbcAction action = actionFromContext(method, queryTypeAndQuery);
84 | SqlParameterSource parameters = extractParameters(method, args, parameterConverters, jdbc);
85 | switch (action) {
86 | case QUERY:
87 | return doQuery(queryTypeAndQuery.query, queryTypeAndQuery.rowMapperClass, jdbc, method, parameters, columnMapperFactories);
88 | case UPDATE:
89 | return jdbc.update(queryTypeAndQuery.query, parameters);
90 | case INSERT_W_AUTO_GENERATED_KEY:
91 | return executeUpdateAndKeepKeys(queryTypeAndQuery.query, method, jdbc, parameters);
92 | default:
93 | throw new IllegalArgumentException("unknown value for action: " + action);
94 | }
95 | }
96 |
97 |
98 | @SuppressWarnings("unchecked")
99 | private Object doQuery(String template, Class> rowMapper,
100 | NamedParameterJdbcTemplate jdbc, Method method,
101 | SqlParameterSource parameters, SortedSet columnMapperFactories) {
102 | boolean isReturnOptional = isReturnOptional(method);
103 | if (method.getReturnType().isAssignableFrom(List.class) || isReturnOptional) {
104 | Class c = extractGenericMethod(method);
105 |
106 | HasRowmapper r = getRowMapper(c, rowMapper, columnMapperFactories);
107 |
108 | List res = handleList(template, jdbc, parameters, columnMapperFactories, c, r, method);
109 | if(isReturnOptional) {
110 | return buildOptional(res);
111 | } else {
112 | return res;
113 | }
114 | } else {
115 | Class c = (Class) method.getReturnType();
116 | HasRowmapper r = getRowMapper(c, rowMapper, columnMapperFactories);
117 | return handleSingleObject(template, jdbc, parameters, columnMapperFactories, c, r, method);
118 | }
119 | }
120 |
121 | @SuppressWarnings("unchecked")
122 | private HasRowmapper getRowMapper(Class c, Class> rowMapper, SortedSet columnMapperFactories) {
123 |
124 | if(rowMapper != ConstructorAnnotationRowMapper.class) {
125 | try {
126 | return new HasRowmapper(true, (RowMapper) rowMapper.getConstructor().newInstance());
127 | } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
128 | throw new IllegalArgumentException("Was not able to create a new instance of " + rowMapper + ". It require a 0 args constructor.", e);
129 | }
130 | } else if (!cachedClassToMapper.containsKey(c)) {
131 | cachedClassToMapper.put(c, handleClass(c, columnMapperFactories));
132 | }
133 | return cachedClassToMapper.get(c);
134 | }
135 | },
136 | /**
137 | * Specialized EXECUTE, will bypass the heuristic to determine if a query is a insert/update/delete or a select, will always treat the query as a select.
138 | */
139 | SELECT {
140 | @Override
141 | Object apply(QueryTypeAndQuery queryTypeAndQuery, NamedParameterJdbcTemplate jdbc, Method method, Object[] args, SortedSet columnMapperFactories, SortedSet parameterConverters) {
142 | return EXECUTE.apply(queryTypeAndQuery, jdbc, method, args, columnMapperFactories, parameterConverters);
143 | }
144 | },
145 | /**
146 | * Specialized EXECUTE, will bypass the heuristic to determine if a query is a insert/update/delete or a select, will always treat the query as a insert/update/delete.
147 | */
148 | MODIFYING {
149 | @Override
150 | Object apply(QueryTypeAndQuery queryTypeAndQuery, NamedParameterJdbcTemplate jdbc, Method method, Object[] args, SortedSet columnMapperFactories, SortedSet parameterConverters) {
151 | return EXECUTE.apply(queryTypeAndQuery, jdbc, method, args, columnMapperFactories, parameterConverters);
152 | }
153 | },
154 | /**
155 | * Specialized EXECUTE, will bypass the heuristic to determine if a query is a insert/update/delete or a select, will always treat the query as a select.
156 | */
157 | MODIFYING_WITH_RETURN {
158 | @Override
159 | Object apply(QueryTypeAndQuery queryTypeAndQuery, NamedParameterJdbcTemplate jdbc, Method method, Object[] args, SortedSet columnMapperFactories, SortedSet parameterConverters) {
160 | return EXECUTE.apply(queryTypeAndQuery, jdbc, method, args, columnMapperFactories, parameterConverters);
161 | }
162 | };
163 |
164 | abstract Object apply(QueryTypeAndQuery queryTypeAndQuery, NamedParameterJdbcTemplate jdbc,
165 | Method method, Object[] args,
166 | SortedSet columnMapperFactories, SortedSet parameterConverters);
167 |
168 | private static Object handleSingleObject(String template,
169 | NamedParameterJdbcTemplate jdbc, SqlParameterSource parameters,
170 | SortedSet columnMapperFactories,
171 | Class c, HasRowmapper r, Method method) {
172 | if (r.present) {
173 | return jdbc.queryForObject(template, parameters, r.rowMapper);
174 | } else {
175 | RowMapper rowMapper = matchToOutput(columnMapperFactories, c, method.getAnnotations());
176 | if(rowMapper != null) {
177 | return jdbc.queryForObject(template, parameters, rowMapper);
178 | } else {
179 | return jdbc.queryForObject(template, parameters, c);
180 | }
181 |
182 | }
183 | }
184 |
185 | @SuppressWarnings("unchecked")
186 | private static Class extractGenericMethod(Method method) {
187 | return (Class) ((ParameterizedType) method.getGenericReturnType()).getActualTypeArguments()[0];
188 | }
189 |
190 | protected Object buildOptional(List res) {
191 | if (res.size() > 1) {
192 | throw new IncorrectResultSizeDataAccessException(1, res.size());
193 | }
194 |
195 | if(res.isEmpty()) {
196 | return Optional.empty();
197 | } else {
198 | return Optional.ofNullable(res.iterator().next());
199 | }
200 | }
201 |
202 |
203 | private static boolean isReturnOptional(Method method) {
204 | return method.getReturnType().isAssignableFrom(Optional.class);
205 | }
206 |
207 | private static RowMapper matchToOutput(SortedSet columnMapperFactories, Class o, Annotation[] annotations) {
208 |
209 | for(ColumnMapperFactory mapper : columnMapperFactories) {
210 | if(mapper.accept(o, annotations)) {
211 | return mapper.getSingleColumnRowMapper(o);
212 | }
213 | }
214 | return null;
215 | }
216 |
217 | private static JdbcAction actionFromContext(Method method, QueryTypeAndQuery queryTypeAndQuery) {
218 |
219 | if (method.getReturnType().isAssignableFrom(AffectedRowCountAndKey.class)) {
220 | return JdbcAction.INSERT_W_AUTO_GENERATED_KEY;
221 | } else if (queryTypeAndQuery.type == SELECT || queryTypeAndQuery.type == MODIFYING_WITH_RETURN) {
222 | return JdbcAction.QUERY;
223 | } else if (queryTypeAndQuery.type == MODIFYING) {
224 | return JdbcAction.UPDATE;
225 | } else {
226 | return actionFromTemplate(queryTypeAndQuery.query);
227 | }
228 | }
229 |
230 | private static JdbcAction actionFromTemplate(String template) {
231 | String tmpl = StringUtils.deleteAny(template.toLowerCase(Locale.ENGLISH), "() ").trim();
232 | return tmpl.indexOf("select") == 0 ? JdbcAction.QUERY : JdbcAction.UPDATE;
233 | }
234 |
235 | private enum JdbcAction {
236 | QUERY, UPDATE, INSERT_W_AUTO_GENERATED_KEY
237 | }
238 |
239 | private static class HasRowmapper {
240 | private final boolean present;
241 | private final RowMapper rowMapper;
242 |
243 | HasRowmapper(boolean present, RowMapper rowMapper) {
244 | this.present = present;
245 | this.rowMapper = rowMapper;
246 | }
247 | }
248 |
249 |
250 |
251 | private static HasRowmapper handleClass(Class c, SortedSet columnMapperFactories) {
252 | if (ConstructorAnnotationRowMapper.hasConstructorInTheCorrectForm(c)) {
253 | return new HasRowmapper(true, new ConstructorAnnotationRowMapper<>(c, columnMapperFactories));
254 | } else {
255 | return new HasRowmapper(false, null);
256 | }
257 | }
258 |
259 | private static SqlParameterSource extractParameters(Method m, Object[] args, SortedSet parameterConverters, NamedParameterJdbcTemplate jdbc) {
260 |
261 | Annotation[][] parameterAnnotations = m.getParameterAnnotations();
262 | if (parameterAnnotations == null || parameterAnnotations.length == 0) {
263 | return new EmptySqlParameterSource();
264 | }
265 |
266 | MapSqlParameterSource ps = new MapSqlParameterSource();
267 | Class>[] parameterTypes = m.getParameterTypes();
268 | for (int i = 0; i < args.length; i++) {
269 | String name = parameterName(parameterAnnotations[i]);
270 | if (name != null) {
271 | Object arg = args[i];
272 | Class> parameterType = parameterTypes[i];
273 |
274 | boolean hasAccepted = false;
275 | for (ParameterConverter parameterConverter : parameterConverters) {
276 | if (parameterConverter.accept(parameterType, parameterAnnotations[i])) {
277 | hasAccepted = true;
278 | if (parameterConverter instanceof ParameterConverter.AdvancedParameterConverter) {
279 | ((ParameterConverter.AdvancedParameterConverter) parameterConverter).processParameter(new ParameterConverter.ProcessParameterContext(jdbc, name, arg, parameterType, parameterAnnotations[i], ps));
280 | } else {
281 | parameterConverter.processParameter(name, arg, parameterType, ps);
282 | }
283 |
284 | break;
285 | }
286 | }
287 |
288 | if (!hasAccepted) {
289 | throw new IllegalStateException("Was not able to find a ParameterConverter able to process object: " + arg + " with class " + parameterType);
290 | }
291 | }
292 | }
293 |
294 | return ps;
295 | }
296 |
297 | private static String parameterName(Annotation[] annotation) {
298 |
299 | if (annotation == null) {
300 | return null;
301 | }
302 |
303 | for (Annotation a : annotation) {
304 | if (a instanceof Bind) {
305 | return ((Bind) a).value();
306 | }
307 | }
308 | return null;
309 | }
310 |
311 |
312 | @SuppressWarnings("unchecked")
313 | private static AffectedRowCountAndKey executeUpdateAndKeepKeys(
314 | String template, Method method,
315 | NamedParameterJdbcTemplate jdbc, SqlParameterSource parameters) {
316 |
317 | Class keyClass = (Class) ((ParameterizedType) method.getGenericReturnType()).getActualTypeArguments()[0];
318 |
319 | KeyHolder keyHolder = new GeneratedKeyHolder();
320 |
321 | int result = jdbc.update(template, parameters, keyHolder);
322 | Map keys = keyHolder.getKeys();
323 | Object key;
324 | if (keys.size() > 1) {
325 | AutoGeneratedKey spec = Objects.requireNonNull(withType(method.getDeclaredAnnotations(), AutoGeneratedKey.class), "more than one key for query " + template + ": annotation @AutoGeneratedKey required");
326 | key = Objects.requireNonNull(keys.get(spec.value()), "the key with name " + spec.value() + " has returned null for query " + template + ": required a non null key");
327 | } else if (Number.class.isAssignableFrom(keyClass)) {
328 | Class extends Number> c = (Class extends Number>) keyClass;
329 | return new AffectedRowCountAndKey<>(result, (T) NumberUtils.convertNumberToTargetClass(keyHolder.getKey(), c));
330 | } else {
331 | key = keys.values().iterator().next();
332 | }
333 | return new AffectedRowCountAndKey<>(result, keyClass.cast(key));
334 | }
335 |
336 | private static T withType(Annotation[] annotations, Class c) {
337 | if(annotations == null) {
338 | return null;
339 | }
340 |
341 | for(Annotation a : annotations) {
342 | if(a.annotationType() == c) {
343 | return (T) a;
344 | }
345 | }
346 | return null;
347 | }
348 |
349 | private static List handleList(String template,
350 | NamedParameterJdbcTemplate jdbc, SqlParameterSource parameters,
351 | SortedSet columnMapperFactories,
352 | Class c, HasRowmapper r, Method method) {
353 | if (r.present) {
354 | return jdbc.query(template, parameters, r.rowMapper);
355 | } else {
356 | RowMapper rowMapper = matchToOutput(columnMapperFactories, c, method.getAnnotations());
357 | if(rowMapper != null) {
358 | return jdbc.query(template, parameters, rowMapper);
359 | } else {
360 | return jdbc.queryForList(template, parameters, c);
361 | }
362 | }
363 | }
364 | }
365 |
--------------------------------------------------------------------------------
/src/main/java/ch/digitalfondue/npjt/RepositoriesDefinitionRegistrar.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2019 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt;
17 |
18 | import org.apache.commons.logging.LogFactory;
19 | import org.springframework.beans.BeansException;
20 | import org.springframework.beans.factory.BeanFactory;
21 | import org.springframework.beans.factory.BeanFactoryAware;
22 | import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
23 | import org.springframework.beans.factory.config.BeanDefinition;
24 | import org.springframework.beans.factory.config.BeanExpressionContext;
25 | import org.springframework.beans.factory.config.BeanExpressionResolver;
26 | import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
27 | import org.springframework.beans.factory.support.AbstractBeanDefinition;
28 | import org.springframework.beans.factory.support.BeanDefinitionBuilder;
29 | import org.springframework.beans.factory.support.BeanDefinitionRegistry;
30 | import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
31 | import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
32 | import org.springframework.core.log.LogAccessor;
33 | import org.springframework.core.type.AnnotationMetadata;
34 | import org.springframework.core.type.filter.AnnotationTypeFilter;
35 |
36 | import java.util.Map;
37 | import java.util.Set;
38 |
39 | public class RepositoriesDefinitionRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
40 |
41 | private BeanExpressionResolver resolver;
42 | private BeanExpressionContext expressionContext;
43 |
44 | private final LogAccessor logger = new LogAccessor(LogFactory.getLog(getClass()));
45 |
46 | @Override
47 | public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
48 | Map annotationAttributes = annotationMetadata.getAnnotationAttributes(EnableNpjt.class.getCanonicalName());
49 | String[] basePackages = (String[]) annotationAttributes.get("basePackages");
50 | String activeDb = (String) annotationAttributes.get("activeDB");
51 | Class> queryFactoryClass = (Class>) annotationAttributes.get("queryFactory");
52 |
53 | if (this.resolver != null) {
54 | activeDb = (String) this.resolver.evaluate(activeDb, expressionContext);
55 | }
56 |
57 | logger.info("ActiveDb is " + activeDb);
58 |
59 | if (basePackages != null) {
60 | CustomClasspathScanner scanner = new CustomClasspathScanner();
61 | for (String packageToScan : basePackages) {
62 | Set candidates = scanner.findCandidateComponents(packageToScan);
63 | handleCandidates(candidates, beanDefinitionRegistry, activeDb, queryFactoryClass);
64 | }
65 | }
66 | }
67 |
68 | private void handleCandidates(Set candidates, BeanDefinitionRegistry beanDefinitionRegistry,
69 | String activeDB, Class> queryFactoryClass) {
70 | try {
71 | for (BeanDefinition beanDefinition : candidates) {
72 | Class> c = Class.forName(beanDefinition.getBeanClassName());
73 | AbstractBeanDefinition abd = BeanDefinitionBuilder.rootBeanDefinition(queryFactoryClass)
74 | .addConstructorArgValue(c)
75 | .addConstructorArgValue(activeDB)
76 | .getBeanDefinition();
77 | beanDefinitionRegistry.registerBeanDefinition(beanDefinition.getBeanClassName(), abd);
78 | }
79 | } catch (ClassNotFoundException cnf) {
80 | throw new IllegalStateException("Error while loading class", cnf);
81 | }
82 | }
83 |
84 | @Override
85 | public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
86 | if (beanFactory instanceof ConfigurableListableBeanFactory) {
87 | this.resolver = ((ConfigurableListableBeanFactory) beanFactory).getBeanExpressionResolver();
88 | this.expressionContext = new BeanExpressionContext((ConfigurableListableBeanFactory) beanFactory, null);
89 | }
90 | }
91 |
92 |
93 | private static class CustomClasspathScanner extends ClassPathScanningCandidateComponentProvider {
94 |
95 | public CustomClasspathScanner() {
96 | super(false);
97 | addIncludeFilter(new AnnotationTypeFilter(QueryRepository.class, false));
98 | }
99 |
100 | @Override
101 | protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
102 | return beanDefinition.getMetadata().isInterface();
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/main/java/ch/digitalfondue/npjt/mapper/ColumnMapper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt.mapper;
17 |
18 | import java.sql.ResultSet;
19 | import java.sql.SQLException;
20 |
21 | public abstract class ColumnMapper {
22 |
23 | protected final String name;
24 | protected final Class> paramType;
25 |
26 | public ColumnMapper(String name, Class> paramType) {
27 | this.name = name;
28 | this.paramType = paramType;
29 | }
30 |
31 | public abstract Object getObject(ResultSet rs) throws SQLException;
32 | }
--------------------------------------------------------------------------------
/src/main/java/ch/digitalfondue/npjt/mapper/ColumnMapperFactory.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt.mapper;
17 |
18 | import org.springframework.jdbc.core.RowMapper;
19 |
20 | import java.lang.annotation.Annotation;
21 |
22 | public interface ColumnMapperFactory {
23 | ColumnMapper build(String name, Class> paramType);
24 | int order();
25 | boolean accept(Class> paramType, Annotation[] annotations);
26 | RowMapper getSingleColumnRowMapper(Class clzz);
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/ch/digitalfondue/npjt/mapper/DefaultMapper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt.mapper;
17 |
18 | import java.lang.annotation.Annotation;
19 | import java.sql.ResultSet;
20 | import java.sql.SQLException;
21 |
22 | import org.springframework.jdbc.core.RowMapper;
23 | import org.springframework.jdbc.core.SingleColumnRowMapper;
24 | import org.springframework.jdbc.core.StatementCreatorUtils;
25 | import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
26 | import org.springframework.jdbc.support.JdbcUtils;
27 |
28 | public class DefaultMapper extends ColumnMapper {
29 |
30 | private static final int ORDER = Integer.MAX_VALUE;
31 |
32 | public DefaultMapper(String name, Class> paramType) {
33 | super(name, paramType);
34 | }
35 |
36 | public Object getObject(ResultSet rs) throws SQLException {
37 | int columnIdx = rs.findColumn(name);
38 | return JdbcUtils.getResultSetValue(rs, columnIdx, paramType);
39 | }
40 |
41 | public static class Converter implements ParameterConverter {
42 |
43 | @Override
44 | public boolean accept(Class> parameterType, Annotation[] annotations) {
45 | return true;
46 | }
47 |
48 | @Override
49 | public void processParameter(String parameterName, Object arg,
50 | Class> parameterType, MapSqlParameterSource ps) {
51 | ps.addValue(parameterName, arg, StatementCreatorUtils.javaTypeToSqlParameterType(parameterType));
52 | }
53 |
54 | @Override
55 | public int order() {
56 | return ORDER;
57 | }
58 |
59 | }
60 |
61 |
62 | public static class Factory implements ColumnMapperFactory {
63 |
64 | @Override
65 | public ColumnMapper build(String name, Class> paramType) {
66 | return new DefaultMapper(name, paramType);
67 | }
68 |
69 | @Override
70 | public int order() {
71 | return ORDER;
72 | }
73 |
74 | @Override
75 | public boolean accept(Class> paramType, Annotation[] annotations) {
76 | return true;
77 | }
78 |
79 | @Override
80 | public RowMapper getSingleColumnRowMapper(Class clzz) {
81 | return new SingleColumnRowMapper<>(clzz);
82 | }
83 |
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/main/java/ch/digitalfondue/npjt/mapper/EnumMapper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt.mapper;
17 |
18 | import java.lang.annotation.Annotation;
19 | import java.sql.ResultSet;
20 | import java.sql.SQLException;
21 |
22 | import org.springframework.jdbc.core.RowMapper;
23 | import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
24 |
25 | public class EnumMapper extends ColumnMapper {
26 |
27 | private static final int ORDER = Integer.MAX_VALUE - 1;
28 |
29 |
30 | public EnumMapper(String name, Class> paramType) {
31 | super(name, paramType);
32 | }
33 |
34 |
35 | @Override
36 | public Object getObject(ResultSet rs) throws SQLException {
37 | String res = rs.getString(name);
38 | return toEnum(res, paramType);
39 | }
40 |
41 | @SuppressWarnings({ "unchecked", "rawtypes" })
42 | private static Object toEnum(String res, Class> paramType) {
43 | Class extends Enum> enumType = (Class extends Enum>>) paramType;
44 | return res == null ? null : Enum.valueOf(enumType, res.trim());
45 | }
46 |
47 | public static class Converter implements ParameterConverter {
48 |
49 | @Override
50 | public boolean accept(Class> parameterType, Annotation[] annotations) {
51 | return parameterType.isEnum();
52 | }
53 |
54 | @Override
55 | public void processParameter(String parameterName, Object arg, Class> parameterType, MapSqlParameterSource ps) {
56 | ps.addValue(parameterName, arg == null ? null : ((Enum>)arg).name());
57 | }
58 |
59 | @Override
60 | public int order() {
61 | return ORDER;
62 | }
63 |
64 | }
65 |
66 |
67 | public static class Factory implements ColumnMapperFactory {
68 |
69 | @Override
70 | public ColumnMapper build(String name, Class> paramType) {
71 | return new EnumMapper(name, paramType);
72 | }
73 |
74 | @Override
75 | public int order() {
76 | return ORDER;
77 | }
78 |
79 | @Override
80 | public boolean accept(Class> paramType, Annotation[] annotations) {
81 | return paramType.isEnum();
82 | }
83 |
84 | @Override
85 | public RowMapper getSingleColumnRowMapper(final Class clazz) {
86 | return (rs, rowNum) -> toEnum(rs.getString(1), clazz);
87 | }
88 | }
89 | }
--------------------------------------------------------------------------------
/src/main/java/ch/digitalfondue/npjt/mapper/InstantMapper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt.mapper;
17 |
18 | import java.lang.annotation.Annotation;
19 | import java.sql.ResultSet;
20 | import java.sql.SQLException;
21 | import java.sql.Timestamp;
22 | import java.sql.Types;
23 | import java.time.Instant;
24 |
25 | import org.springframework.jdbc.core.RowMapper;
26 | import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
27 |
28 | public class InstantMapper extends ColumnMapper {
29 |
30 | private static final int ORDER = Integer.MAX_VALUE - 5;
31 |
32 | public InstantMapper(String name, Class> paramType) {
33 | super(name, paramType);
34 | }
35 |
36 | @Override
37 | public Object getObject(ResultSet rs) throws SQLException {
38 | return toInstant(rs.getTimestamp(name));
39 | }
40 |
41 | private static Instant toInstant(Timestamp ts) {
42 | return ts != null ? ts.toInstant() : null;
43 | }
44 |
45 | public static class Converter implements ParameterConverter {
46 |
47 | @Override
48 | public boolean accept(Class> parameterType, Annotation[] annotations) {
49 | return Instant.class.equals(parameterType);
50 | }
51 |
52 | @Override
53 | public void processParameter(String parameterName, Object arg,
54 | Class> parameterType, MapSqlParameterSource ps) {
55 | ps.addValue(parameterName, arg != null ? Timestamp.from((Instant) arg) : null, Types.TIMESTAMP);
56 | }
57 |
58 | @Override
59 | public int order() {
60 | return ORDER;
61 | }
62 | }
63 |
64 | public static class Factory implements ColumnMapperFactory {
65 |
66 | @Override
67 | public ColumnMapper build(String name, Class> paramType) {
68 | return new InstantMapper(name, paramType);
69 | }
70 |
71 | @Override
72 | public int order() {
73 | return ORDER;
74 | }
75 |
76 | @Override
77 | public boolean accept(Class> paramType, Annotation[] annotations) {
78 | return Instant.class.equals(paramType);
79 | }
80 |
81 | @Override
82 | public RowMapper getSingleColumnRowMapper(Class clzz) {
83 | return (rs, rowNum) -> toInstant(rs.getTimestamp(1));
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/main/java/ch/digitalfondue/npjt/mapper/LocalDateMapper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt.mapper;
17 |
18 | import java.lang.annotation.Annotation;
19 | import java.sql.Date;
20 | import java.sql.ResultSet;
21 | import java.sql.SQLException;
22 | import java.sql.Types;
23 | import java.time.LocalDate;
24 |
25 | import org.springframework.jdbc.core.RowMapper;
26 | import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
27 |
28 | public class LocalDateMapper extends ColumnMapper {
29 |
30 | private static final int ORDER = Integer.MAX_VALUE - 3;
31 |
32 | public LocalDateMapper(String name, Class> paramType) {
33 | super(name, paramType);
34 | }
35 |
36 | @Override
37 | public Object getObject(ResultSet rs) throws SQLException {
38 | return toLocalDate(rs.getDate(name));
39 | }
40 |
41 | private static LocalDate toLocalDate(Date d) {
42 | return d != null ? d.toLocalDate() : null;
43 | }
44 |
45 | public static class Converter implements ParameterConverter {
46 |
47 | @Override
48 | public boolean accept(Class> parameterType, Annotation[] annotations) {
49 | return LocalDate.class.equals(parameterType);
50 | }
51 |
52 | @Override
53 | public void processParameter(String parameterName, Object arg,
54 | Class> parameterType, MapSqlParameterSource ps) {
55 | ps.addValue(parameterName, arg != null ? Date.valueOf((LocalDate) arg) : null, Types.DATE);
56 | }
57 |
58 | @Override
59 | public int order() {
60 | return ORDER;
61 | }
62 | }
63 |
64 |
65 | public static class Factory implements ColumnMapperFactory {
66 |
67 | @Override
68 | public ColumnMapper build(String name, Class> paramType) {
69 | return new LocalDateMapper(name, paramType);
70 | }
71 |
72 | @Override
73 | public int order() {
74 | return ORDER;
75 | }
76 |
77 | @Override
78 | public boolean accept(Class> paramType, Annotation[] annotations) {
79 | return LocalDate.class.equals(paramType);
80 | }
81 |
82 | @Override
83 | public RowMapper getSingleColumnRowMapper(Class clzz) {
84 | return (rs, rowNum) -> toLocalDate(rs.getDate(1));
85 | }
86 |
87 | }
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/src/main/java/ch/digitalfondue/npjt/mapper/LocalDateTimeMapper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt.mapper;
17 |
18 | import java.lang.annotation.Annotation;
19 | import java.sql.ResultSet;
20 | import java.sql.SQLException;
21 | import java.sql.Timestamp;
22 | import java.sql.Types;
23 | import java.time.LocalDateTime;
24 |
25 | import org.springframework.jdbc.core.RowMapper;
26 | import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
27 |
28 | public class LocalDateTimeMapper extends ColumnMapper {
29 |
30 | private static final int ORDER = Integer.MAX_VALUE - 4;
31 |
32 | public LocalDateTimeMapper(String name, Class> paramType) {
33 | super(name, paramType);
34 | }
35 |
36 | @Override
37 | public Object getObject(ResultSet rs) throws SQLException {
38 | return toLocalDateTime(rs.getTimestamp(name));
39 | }
40 |
41 | private static LocalDateTime toLocalDateTime(Timestamp t) {
42 | return t != null ? t.toLocalDateTime() : null;
43 | }
44 |
45 | public static class Converter implements ParameterConverter {
46 |
47 | @Override
48 | public boolean accept(Class> parameterType, Annotation[] annotations) {
49 | return LocalDateTime.class.equals(parameterType);
50 | }
51 |
52 | @Override
53 | public void processParameter(String parameterName, Object arg,
54 | Class> parameterType, MapSqlParameterSource ps) {
55 | ps.addValue(parameterName, arg != null ? Timestamp.valueOf(((LocalDateTime) arg)) : null, Types.TIMESTAMP);
56 | }
57 |
58 | @Override
59 | public int order() {
60 | return ORDER;
61 | }
62 |
63 | }
64 |
65 | public static class Factory implements ColumnMapperFactory {
66 |
67 | @Override
68 | public ColumnMapper build(String name, Class> paramType) {
69 | return new LocalDateTimeMapper(name, paramType);
70 | }
71 |
72 | @Override
73 | public int order() {
74 | return ORDER;
75 | }
76 |
77 | @Override
78 | public boolean accept(Class> paramType, Annotation[] annotations) {
79 | return LocalDateTime.class.equals(paramType);
80 | }
81 |
82 | @Override
83 | public RowMapper getSingleColumnRowMapper(Class clzz) {
84 | return (rs, rowNum) -> toLocalDateTime(rs.getTimestamp(1));
85 | }
86 |
87 | }
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/src/main/java/ch/digitalfondue/npjt/mapper/ParameterConverter.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt.mapper;
17 |
18 | import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
19 | import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
20 | import org.springframework.jdbc.datasource.DataSourceUtils;
21 |
22 | import java.lang.annotation.Annotation;
23 | import java.sql.Connection;
24 |
25 | public interface ParameterConverter {
26 |
27 | boolean accept(Class> parameterType, Annotation[] annotations);
28 |
29 |
30 | interface AdvancedParameterConverter extends ParameterConverter {
31 | void processParameter(ProcessParameterContext processParameterContext);
32 |
33 | @Override
34 | default void processParameter(String parameterName, Object arg, Class> parameterType, MapSqlParameterSource ps) {
35 | throw new IllegalStateException("should not be executed");
36 | }
37 | }
38 |
39 | /**
40 | *
41 | *
42 | * @param parameterName
43 | * @param arg
44 | * @param parameterType
45 | * @param ps
46 | */
47 | void processParameter(String parameterName, Object arg, Class> parameterType, MapSqlParameterSource ps);
48 |
49 | int order();
50 |
51 | class ProcessParameterContext {
52 | private final NamedParameterJdbcTemplate jdbc;
53 | private final String parameterName;
54 | private final Class> parameterType;
55 | private final Annotation[] parameterAnnotations;
56 | private final Object arg;
57 | private final MapSqlParameterSource ps;
58 |
59 | public ProcessParameterContext(NamedParameterJdbcTemplate jdbc, String parameterName, Object arg, Class> parameterType, Annotation[] parameterAnnotations, MapSqlParameterSource ps) {
60 | this.jdbc = jdbc;
61 | this.parameterName = parameterName;
62 | this.arg = arg;
63 | this.parameterType = parameterType;
64 | this.parameterAnnotations = parameterAnnotations;
65 | this.ps = ps;
66 | }
67 |
68 | public NamedParameterJdbcTemplate getJdbc() {
69 | return jdbc;
70 | }
71 |
72 | public Connection getConnection() {
73 | return DataSourceUtils.getConnection(jdbc.getJdbcTemplate().getDataSource());
74 | }
75 |
76 | public Class> getParameterType() {
77 | return parameterType;
78 | }
79 |
80 | public Annotation[] getParameterAnnotations() {
81 | return parameterAnnotations;
82 | }
83 |
84 | public Object getArg() {
85 | return arg;
86 | }
87 |
88 | public String getParameterName() {
89 | return parameterName;
90 | }
91 |
92 | public MapSqlParameterSource getParameterSource() {
93 | return ps;
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/main/java/ch/digitalfondue/npjt/mapper/ZonedDateTimeMapper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt.mapper;
17 |
18 | import java.lang.annotation.Annotation;
19 | import java.sql.ResultSet;
20 | import java.sql.SQLException;
21 | import java.sql.Timestamp;
22 | import java.sql.Types;
23 | import java.time.ZoneId;
24 | import java.time.ZonedDateTime;
25 | import java.util.Calendar;
26 | import java.util.TimeZone;
27 |
28 | import org.springframework.jdbc.core.RowMapper;
29 | import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
30 |
31 | public class ZonedDateTimeMapper extends ColumnMapper {
32 |
33 | private static final int ORDER = Integer.MAX_VALUE -2;
34 |
35 | private static final TimeZone UTC_TZ = TimeZone.getTimeZone("UTC");
36 | private static final ZoneId UTC_Z_ID = ZoneId.of("UTC");
37 |
38 | public ZonedDateTimeMapper(String name, Class> paramType) {
39 | super(name, paramType);
40 | }
41 |
42 | public Object getObject(ResultSet rs) throws SQLException {
43 | Timestamp timestamp = rs.getTimestamp(name, Calendar.getInstance(UTC_TZ));
44 | return toZonedDateTime(timestamp);
45 | }
46 |
47 | private static Object toZonedDateTime(Timestamp timestamp) {
48 | if (timestamp == null) {
49 | return null;
50 | }
51 | return ZonedDateTime.ofInstant(timestamp.toInstant(), UTC_Z_ID);
52 | }
53 |
54 | public static class Converter implements ParameterConverter {
55 |
56 | @Override
57 | public boolean accept(Class> parameterType, Annotation[] annotations) {
58 | return ZonedDateTime.class.isAssignableFrom(parameterType);
59 | }
60 |
61 | @Override
62 | public void processParameter(String parameterName, Object arg, Class> parameterType, MapSqlParameterSource ps) {
63 | Calendar c = null;
64 | if(arg != null) {
65 | ZonedDateTime dateTime = ZonedDateTime.class.cast(arg);
66 | ZonedDateTime utc = dateTime.withZoneSameInstant(UTC_Z_ID);
67 | c = Calendar.getInstance();
68 | c.setTimeZone(UTC_TZ);
69 | c.setTimeInMillis(utc.toInstant().toEpochMilli());
70 | }
71 | ps.addValue(parameterName, c, Types.TIMESTAMP);
72 | }
73 |
74 | @Override
75 | public int order() {
76 | return ORDER;
77 | }
78 |
79 | }
80 |
81 |
82 |
83 | public static class Factory implements ColumnMapperFactory {
84 |
85 | @Override
86 | public ColumnMapper build(String name, Class> paramType) {
87 | return new ZonedDateTimeMapper(name, paramType);
88 | }
89 |
90 | @Override
91 | public int order() {
92 | return ORDER;
93 | }
94 |
95 | @Override
96 | public boolean accept(Class> paramType, Annotation[] annotations) {
97 | return ZonedDateTime.class.isAssignableFrom(paramType);
98 | }
99 |
100 | @Override
101 | public RowMapper getSingleColumnRowMapper(Class clzz) {
102 | return (rs, rowNum) -> {
103 | Timestamp timestamp = rs.getTimestamp(1, Calendar.getInstance(UTC_TZ));
104 | return toZonedDateTime(timestamp);
105 | };
106 | }
107 |
108 | }
109 | }
--------------------------------------------------------------------------------
/src/test/java/ch/digitalfondue/npjt/ConstructorAnnotationRowMapperTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | /**
17 | * This file is part of lavagna.
18 | *
19 | * lavagna is free software: you can redistribute it and/or modify
20 | * it under the terms of the GNU General Public License as published by
21 | * the Free Software Foundation, either version 3 of the License, or
22 | * (at your option) any later version.
23 | *
24 | * lavagna is distributed in the hope that it will be useful,
25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 | * GNU General Public License for more details.
28 | *
29 | * You should have received a copy of the GNU General Public License
30 | * along with lavagna. If not, see .
31 | */
32 | package ch.digitalfondue.npjt;
33 |
34 | import java.util.Collections;
35 | import java.util.List;
36 |
37 | import org.junit.Assert;
38 | import org.junit.Test;
39 |
40 | import ch.digitalfondue.npjt.ConstructorAnnotationRowMapper.Column;
41 | import ch.digitalfondue.npjt.mapper.ColumnMapperFactory;
42 | import ch.digitalfondue.npjt.mapper.DefaultMapper.Factory;
43 |
44 | public class ConstructorAnnotationRowMapperTest {
45 |
46 | private static final List DEFAULT_COLUMN_MAPPER_FACTORY = Collections.singletonList(new Factory());
47 |
48 | @Test
49 | public void testCorrectMapping() {
50 | new ConstructorAnnotationRowMapper<>(Mapping.class, DEFAULT_COLUMN_MAPPER_FACTORY);
51 | }
52 |
53 | @Test(expected = IllegalArgumentException.class)
54 | public void testsMoreThanOnePublicConstructor() {
55 | new ConstructorAnnotationRowMapper<>(MultiplePublicConstructor.class, DEFAULT_COLUMN_MAPPER_FACTORY);
56 | }
57 |
58 | @Test(expected = IllegalStateException.class)
59 | public void testMissingColumnAnnotation() {
60 | new ConstructorAnnotationRowMapper<>(MissingColumn.class, DEFAULT_COLUMN_MAPPER_FACTORY);
61 | }
62 |
63 | @Test(expected = IllegalStateException.class)
64 | public void testNoMatchingColumnMapperFactory() {
65 | new ConstructorAnnotationRowMapper<>(Mapping.class, Collections.emptyList());
66 | }
67 |
68 | @Test
69 | public void testsMoreThanOnePublicConstructorForm() {
70 | Assert.assertFalse(ConstructorAnnotationRowMapper.hasConstructorInTheCorrectForm(MultiplePublicConstructor.class));
71 | }
72 |
73 | @Test
74 | public void testMissingColumnAnnotationForm() {
75 | Assert.assertFalse(ConstructorAnnotationRowMapper.hasConstructorInTheCorrectForm(MissingColumn.class));
76 | }
77 |
78 | @Test
79 | public void testZeroArgConstructorForm() {
80 | Assert.assertFalse(ConstructorAnnotationRowMapper.hasConstructorInTheCorrectForm(ZeroArgConstructor.class));
81 | }
82 |
83 |
84 | public static class Mapping {
85 | public Mapping(@Column("COL_1") String a, @Column("COL_2") int b) {
86 | }
87 | }
88 |
89 | public static class ZeroArgConstructor {
90 |
91 | public ZeroArgConstructor() {
92 | }
93 |
94 | }
95 |
96 | public static class MultiplePublicConstructor {
97 | public MultiplePublicConstructor() {
98 | }
99 |
100 | public MultiplePublicConstructor(String s) {
101 | }
102 | }
103 |
104 | public static class MissingColumn {
105 | public MissingColumn(String a) {
106 |
107 | }
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/src/test/java/ch/digitalfondue/npjt/QueryFactoryTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | /**
17 | * This file is part of lavagna.
18 | *
19 | * lavagna is free software: you can redistribute it and/or modify
20 | * it under the terms of the GNU General Public License as published by
21 | * the Free Software Foundation, either version 3 of the License, or
22 | * (at your option) any later version.
23 | *
24 | * lavagna is distributed in the hope that it will be useful,
25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 | * GNU General Public License for more details.
28 | *
29 | * You should have received a copy of the GNU General Public License
30 | * along with lavagna. If not, see .
31 | */
32 | package ch.digitalfondue.npjt;
33 |
34 | import org.junit.Assert;
35 | import org.junit.Test;
36 | import org.junit.runner.RunWith;
37 | import org.mockito.Mock;
38 | import org.mockito.runners.MockitoJUnitRunner;
39 |
40 | import javax.sql.DataSource;
41 |
42 | @RunWith(MockitoJUnitRunner.class)
43 | public class QueryFactoryTest {
44 |
45 | @Mock
46 | DataSource dataSource;
47 |
48 | public interface QueryTest {
49 | @Query(type = QueryType.TEMPLATE, value = "SELECT * FROM LA_BOARD_COLUMN_FULL WHERE BOARD_COLUMN_ID = :columnId")
50 | String findById();
51 |
52 | @Query(type = QueryType.TEMPLATE, value = "SELECT * FROM LA_BOARD_COLUMN_FULL WHERE BOARD_COLUMN_ID = :columnId")
53 | @QueriesOverride(@QueryOverride(db = "MYSQL", value = "SELECT * FROM LA_BOARD_COLUMN_FULL_MYSQL WHERE BOARD_COLUMN_ID = :columnId"))
54 | String overrideQuery();
55 | }
56 |
57 | @Test
58 | public void testSimpleAnnotationQuery() {
59 | QueryFactory qf = new QueryFactory<>(QueryTest.class, "HSQLDB");
60 | qf.setDataSource(dataSource);
61 |
62 | QueryTest qt = qf.getObject();
63 |
64 | Assert.assertEquals("SELECT * FROM LA_BOARD_COLUMN_FULL WHERE BOARD_COLUMN_ID = :columnId", qt.findById());
65 | }
66 |
67 | @Test
68 | public void testOverrideAnnotation() {
69 | QueryFactory qf = new QueryFactory<>(QueryTest.class, "HSQLDB");
70 | qf.setDataSource(dataSource);
71 | QueryTest qt = qf.getObject();
72 | Assert.assertEquals("SELECT * FROM LA_BOARD_COLUMN_FULL WHERE BOARD_COLUMN_ID = :columnId", qt.overrideQuery());
73 |
74 |
75 | QueryFactory qfMysql = new QueryFactory<>(QueryTest.class, "MYSQL");
76 | qfMysql.setDataSource(dataSource);
77 | QueryTest qtMysql = qfMysql.getObject();
78 | Assert.assertEquals("SELECT * FROM LA_BOARD_COLUMN_FULL_MYSQL WHERE BOARD_COLUMN_ID = :columnId", qtMysql.overrideQuery());
79 | }
80 |
81 | @Test
82 | public void testObjectCallOnProxiedInterface() {
83 | QueryFactory qf = new QueryFactory<>(QueryTest.class, "HSQLDB");
84 | qf.setDataSource(dataSource);
85 | QueryTest qt = qf.getObject();
86 |
87 | Assert.assertTrue(qt.toString().startsWith("com.sun.proxy.$Proxy"));
88 | qt.hashCode(); //<- should not fail
89 | Assert.assertTrue(qt.equals(qt));
90 | Assert.assertFalse(qt.equals(qf));
91 | }
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/src/test/java/ch/digitalfondue/npjt/QueryRepositoryScannerTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt;
17 |
18 | import org.junit.Assert;
19 | import org.junit.Test;
20 | import org.junit.runner.RunWith;
21 | import org.springframework.beans.factory.annotation.Autowired;
22 | import org.springframework.test.context.ContextConfiguration;
23 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
24 | import org.springframework.transaction.annotation.Transactional;
25 |
26 | import ch.digitalfondue.npjt.query.QueryRepo;
27 | import ch.digitalfondue.npjt.query.deeper.QueryRepo2;
28 |
29 | @Transactional
30 | @RunWith(SpringJUnit4ClassRunner.class)
31 | @ContextConfiguration(classes = {TestJdbcConfiguration.class, QueryScannerConfiguration.class})
32 | public class QueryRepositoryScannerTest {
33 |
34 | @Autowired
35 | QueryRepo queryRepo;
36 |
37 | @Autowired
38 | QueryRepo2 queryRepo2;
39 |
40 | @Test
41 | public void checkInjection() {
42 | Assert.assertNotNull(queryRepo);
43 | Assert.assertNotNull(queryRepo2);
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/src/test/java/ch/digitalfondue/npjt/QueryScannerConfiguration.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt;
17 |
18 | @EnableNpjt(basePackages = {"ch.digitalfondue.npjt.query", "ch.digitalfondue.npjt.columnmapper"})
19 | public class QueryScannerConfiguration {
20 | }
21 |
--------------------------------------------------------------------------------
/src/test/java/ch/digitalfondue/npjt/TestJdbcConfiguration.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt;
17 |
18 | import java.util.Properties;
19 |
20 | import javax.sql.DataSource;
21 |
22 | import org.hsqldb.jdbc.JDBCDataSourceFactory;
23 | import org.springframework.context.annotation.Bean;
24 | import org.springframework.jdbc.datasource.DataSourceTransactionManager;
25 | import org.springframework.jdbc.datasource.DriverManagerDataSource;
26 | import org.springframework.transaction.PlatformTransactionManager;
27 |
28 | public class TestJdbcConfiguration {
29 |
30 | @Bean
31 | public DataSource getDataSource() throws Exception {
32 | Properties prop = new Properties();
33 | prop.put("url", "jdbc:hsqldb:mem:extra");
34 | prop.put("user", "sa");
35 | prop.put("password", "");
36 | return JDBCDataSourceFactory.createDataSource(prop);
37 | }
38 |
39 | /*@Bean
40 | DataSource getPostgresqlDataSource() {
41 | DriverManagerDataSource ds = new DriverManagerDataSource();
42 | ds.setDriverClassName("org.postgresql.Driver");
43 | ds.setUrl("jdbc:postgresql://localhost:5432/alfio");
44 | ds.setUsername("postgres");
45 | ds.setPassword("password");
46 | return ds;
47 | }*/
48 |
49 | @Bean
50 | public PlatformTransactionManager getPlatfomrTransactionManager(DataSource dataSource) {
51 | return new DataSourceTransactionManager(dataSource);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/test/java/ch/digitalfondue/npjt/mapper/DefaultMapperTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt.mapper;
17 |
18 | import static org.mockito.Mockito.when;
19 |
20 | import java.sql.ResultSet;
21 | import java.sql.SQLException;
22 |
23 | import org.junit.Assert;
24 | import org.junit.Test;
25 | import org.junit.runner.RunWith;
26 | import org.mockito.Mock;
27 | import org.mockito.runners.MockitoJUnitRunner;
28 |
29 | import ch.digitalfondue.npjt.mapper.DefaultMapper;
30 |
31 | @RunWith(MockitoJUnitRunner.class)
32 | public class DefaultMapperTest {
33 |
34 | @Mock
35 | ResultSet resultSet;
36 |
37 | @Test
38 | public void testNull() throws SQLException {
39 | DefaultMapper m = new DefaultMapper("PARAM", String.class);
40 | Assert.assertNull(m.getObject(resultSet));
41 | }
42 |
43 | @Test
44 | public void testString() throws SQLException {
45 | DefaultMapper m = new DefaultMapper("PARAM", String.class);
46 | when(resultSet.findColumn("PARAM")).thenReturn(1);
47 | when(resultSet.getString(1)).thenReturn("MY_VALUE");
48 | Assert.assertEquals("MY_VALUE", m.getObject(resultSet));
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/test/java/ch/digitalfondue/npjt/mapper/EnumMapperTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt.mapper;
17 |
18 | import static org.mockito.Mockito.when;
19 |
20 | import java.sql.ResultSet;
21 | import java.sql.SQLException;
22 |
23 | import org.junit.Assert;
24 | import org.junit.Test;
25 | import org.junit.runner.RunWith;
26 | import org.mockito.Mock;
27 | import org.mockito.runners.MockitoJUnitRunner;
28 |
29 | import ch.digitalfondue.npjt.mapper.EnumMapper;
30 |
31 | @RunWith(MockitoJUnitRunner.class)
32 | public class EnumMapperTest {
33 |
34 | public enum MyEnum {
35 | BLA, TEST;
36 | }
37 |
38 | @Mock
39 | ResultSet resultSet;
40 |
41 | @Test
42 | public void testNull() throws SQLException {
43 | EnumMapper m = new EnumMapper("PARAM", MyEnum.class);
44 | Assert.assertNull(m.getObject(resultSet));
45 | }
46 |
47 | @Test
48 | public void testValue() throws SQLException {
49 | EnumMapper m = new EnumMapper("PARAM", MyEnum.class);
50 | when(resultSet.getString("PARAM")).thenReturn("BLA");
51 | Assert.assertEquals(MyEnum.BLA, m.getObject(resultSet));
52 |
53 | when(resultSet.getString("PARAM")).thenReturn("TEST");
54 | Assert.assertEquals(MyEnum.TEST, m.getObject(resultSet));
55 | }
56 |
57 | @Test(expected = IllegalArgumentException.class)
58 | public void testWrongValue() throws SQLException {
59 | EnumMapper m = new EnumMapper("PARAM", MyEnum.class);
60 | when(resultSet.getString("PARAM")).thenReturn("NOT_IN_ENUM");
61 | m.getObject(resultSet);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/test/java/ch/digitalfondue/npjt/mapper/ZonedDateTimeMapperTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt.mapper;
17 |
18 | import static org.mockito.Mockito.*;
19 |
20 | import java.sql.ResultSet;
21 | import java.sql.SQLException;
22 | import java.sql.Timestamp;
23 | import java.time.ZonedDateTime;
24 | import java.util.Calendar;
25 |
26 | import org.junit.Assert;
27 | import org.junit.Test;
28 | import org.junit.runner.RunWith;
29 | import org.mockito.Mock;
30 | import org.mockito.runners.MockitoJUnitRunner;
31 |
32 | import ch.digitalfondue.npjt.mapper.ZonedDateTimeMapper;
33 |
34 | @RunWith(MockitoJUnitRunner.class)
35 | public class ZonedDateTimeMapperTest {
36 |
37 | @Mock
38 | ResultSet resultSet;
39 |
40 | @Test
41 | public void testNull() throws SQLException {
42 | ZonedDateTimeMapper m = new ZonedDateTimeMapper("PARAM", ZonedDateTime.class);
43 | Assert.assertNull(m.getObject(resultSet));
44 | }
45 |
46 | @Test
47 | public void testFromTimestampToZonedDateTime() throws SQLException {
48 | ZonedDateTimeMapper m = new ZonedDateTimeMapper("PARAM", ZonedDateTime.class);
49 |
50 | final int time = 42;
51 |
52 | when(resultSet.getTimestamp(eq("PARAM"), any(Calendar.class))).thenReturn(new Timestamp(time));
53 |
54 | ZonedDateTime res = (ZonedDateTime) m.getObject(resultSet);
55 | Assert.assertEquals(time, res.toInstant().toEpochMilli());
56 |
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/src/test/java/ch/digitalfondue/npjt/query/AutogeneratedKeyQueriesTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt.query;
17 |
18 | import ch.digitalfondue.npjt.*;
19 | import org.junit.Assert;
20 | import org.junit.Test;
21 | import org.junit.runner.RunWith;
22 | import org.springframework.beans.factory.annotation.Autowired;
23 | import org.springframework.test.context.ContextConfiguration;
24 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
25 | import org.springframework.transaction.annotation.Transactional;
26 |
27 | @Transactional
28 | @RunWith(SpringJUnit4ClassRunner.class)
29 | @ContextConfiguration(classes = {TestJdbcConfiguration.class, QueryScannerConfiguration.class})
30 | public class AutogeneratedKeyQueriesTest {
31 |
32 | @Autowired
33 | AutogeneratedKeyQueries aq;
34 |
35 | @Test
36 | public void autogeneratedKeyQueries() {
37 |
38 | aq.createTable();
39 |
40 | AffectedRowCountAndKey res = aq.insert("test");
41 | AffectedRowCountAndKey res2 = aq.insert("test2");
42 | AffectedRowCountAndKey res3 = aq.insert("test3");
43 |
44 | Assert.assertEquals(aq.findKeyByValue("test"), res.getKey());
45 | Assert.assertEquals(1, res.getAffectedRowCount());
46 |
47 | Assert.assertEquals(aq.findKeyByValue("test2"), res2.getKey());
48 | Assert.assertEquals(1, res2.getAffectedRowCount());
49 |
50 | Assert.assertEquals(aq.findKeyByValue("test3"), res3.getKey());
51 | Assert.assertEquals(1, res3.getAffectedRowCount());
52 | }
53 |
54 | @QueryRepository
55 | public interface AutogeneratedKeyQueries {
56 | @Query("CREATE TABLE LA_AUTO (ID INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY NOT NULL, VALUE CLOB NOT NULL)")
57 | void createTable();
58 |
59 | @Query("INSERT INTO LA_AUTO(VALUE) VALUES (:value)")
60 | AffectedRowCountAndKey insert(@Bind("value") String value);
61 |
62 | @Query("SELECT ID FROM LA_AUTO WHERE VALUE = :value")
63 | Integer findKeyByValue(@Bind("value") String value);
64 |
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/src/test/java/ch/digitalfondue/npjt/query/BooleanQueriesTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt.query;
17 |
18 | import ch.digitalfondue.npjt.*;
19 | import org.junit.Assert;
20 | import org.junit.Test;
21 | import org.junit.runner.RunWith;
22 | import org.springframework.beans.factory.annotation.Autowired;
23 | import org.springframework.test.context.ContextConfiguration;
24 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
25 | import org.springframework.transaction.annotation.Transactional;
26 |
27 | import ch.digitalfondue.npjt.ConstructorAnnotationRowMapper.Column;
28 |
29 | @Transactional
30 | @RunWith(SpringJUnit4ClassRunner.class)
31 | @ContextConfiguration(classes = {TestJdbcConfiguration.class, QueryScannerConfiguration.class})
32 | public class BooleanQueriesTest {
33 |
34 | @Autowired
35 | BoolQueries bq;
36 |
37 | @Test
38 | public void simpleQueriesTest() {
39 |
40 | bq.createTable();
41 |
42 | bq.insertValue("KEY", true, true, true);
43 |
44 | BoolConf bc = bq.findByKey("KEY");
45 |
46 | Assert.assertTrue(bc.confBool);
47 | Assert.assertTrue(bc.confStr);
48 | Assert.assertTrue(bc.confInt);
49 |
50 | bq.insertValue("KEY2", false, true, false);
51 | BoolConf bc2 = bq.findByKey("KEY2");
52 |
53 | Assert.assertFalse(bc2.confBool);
54 | Assert.assertTrue(bc2.confStr);
55 | Assert.assertFalse(bc2.confInt);
56 |
57 | Assert.assertTrue(bq.findConfBoolByKey("KEY"));
58 | Assert.assertFalse(bq.findConfBoolByKey("KEY2"));
59 |
60 | }
61 |
62 | public static class BoolConf {
63 |
64 | final String key;
65 | final boolean confBool;
66 | final Boolean confStr;
67 | final boolean confInt;
68 |
69 | public BoolConf(@Column("CONF_KEY") String key,
70 | @Column("CONF_BOOL") boolean confBool, @Column("CONF_STR") Boolean confStr,
71 | @Column("CONF_INT") boolean confInt) {
72 | this.key = key;
73 | this.confBool = confBool;
74 | this.confStr = confStr;
75 | this.confInt = confInt;
76 | }
77 |
78 | }
79 |
80 | @QueryRepository
81 | public interface BoolQueries {
82 | @Query("CREATE TABLE LA_CONF_BOOL (CONF_KEY VARCHAR(64) PRIMARY KEY NOT NULL, CONF_BOOL BOOLEAN NOT NULL, CONF_STR VARCHAR(255) NOT NULL, CONF_INT INTEGER NOT NULL)")
83 | void createTable();
84 |
85 | @Query("INSERT INTO LA_CONF_BOOL(CONF_KEY, CONF_BOOL, CONF_STR, CONF_INT) VALUES(:key, :confBool, :confStr, :confInt)")
86 | int insertValue(@Bind("key") String key,
87 | @Bind("confBool") Boolean confBool,
88 | @Bind("confStr") Boolean confStr,
89 | @Bind("confInt") Boolean confInt);
90 |
91 | @Query("SELECT * FROM LA_CONF_BOOL WHERE CONF_KEY = :key")
92 | BoolConf findByKey(@Bind("key") String key);
93 |
94 | @Query("SELECT CONF_BOOL FROM LA_CONF_BOOL WHERE CONF_KEY = :key")
95 | Boolean findConfBoolByKey(@Bind("key") String key);
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/src/test/java/ch/digitalfondue/npjt/query/CustomJSONQueriesTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt.query;
17 |
18 | import ch.digitalfondue.npjt.*;
19 | import ch.digitalfondue.npjt.mapper.ColumnMapper;
20 | import ch.digitalfondue.npjt.mapper.ColumnMapperFactory;
21 | import ch.digitalfondue.npjt.mapper.ParameterConverter;
22 | import com.google.gson.Gson;
23 | import com.google.gson.GsonBuilder;
24 | import org.junit.Assert;
25 | import org.junit.Test;
26 | import org.junit.runner.RunWith;
27 | import org.springframework.beans.factory.annotation.Autowired;
28 | import org.springframework.context.annotation.Bean;
29 | import org.springframework.jdbc.core.RowMapper;
30 | import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
31 | import org.springframework.test.context.ContextConfiguration;
32 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
33 | import org.springframework.transaction.annotation.Transactional;
34 |
35 | import java.lang.annotation.*;
36 | import java.sql.ResultSet;
37 | import java.sql.SQLException;
38 | import java.util.Arrays;
39 | import java.util.Collections;
40 | import java.util.List;
41 | import java.util.Map;
42 |
43 | @Transactional
44 | @RunWith(SpringJUnit4ClassRunner.class)
45 | @ContextConfiguration(classes = {TestJdbcConfiguration.class,
46 | CustomJSONQueriesTest.ColumnMapperAndParametersConfiguration.class,
47 | QueryScannerConfiguration.class})
48 | public class CustomJSONQueriesTest {
49 |
50 | @Autowired
51 | JsonQueries jq;
52 |
53 | private static Gson JSON = new GsonBuilder().create();
54 |
55 | @Retention(RetentionPolicy.RUNTIME)
56 | @Target({ElementType.PARAMETER, ElementType.METHOD})
57 | public @interface AsJson {
58 | }
59 |
60 | private static class JsonColumnMapper extends ColumnMapper {
61 |
62 | JsonColumnMapper(String name, Class> paramType) {
63 | super(name, paramType);
64 | }
65 |
66 | @Override
67 | public Object getObject(ResultSet rs) throws SQLException {
68 | return JSON.fromJson(rs.getString(name), paramType);
69 | }
70 | }
71 |
72 | private static boolean containAsJsonAnnotation(Annotation[] annotations) {
73 | if(annotations == null) {
74 | return false;
75 | }
76 | for(Annotation annotation : annotations) {
77 | if(annotation.annotationType() == AsJson.class) {
78 | return true;
79 | }
80 | }
81 | return false;
82 | }
83 |
84 | private static class JsonColumnMapperFactory implements ColumnMapperFactory {
85 |
86 | @Override
87 | public ColumnMapper build(String name, Class> paramType) {
88 | return new JsonColumnMapper(name, paramType);
89 | }
90 |
91 | @Override
92 | public int order() {
93 | return 0;
94 | }
95 |
96 | @Override
97 | public boolean accept(Class> paramType, Annotation[] annotations) {
98 | return containAsJsonAnnotation(annotations);
99 | }
100 |
101 | @Override
102 | public RowMapper getSingleColumnRowMapper(final Class clzz) {
103 | return (resultSet, rowNum) -> JSON.fromJson(resultSet.getString(1), clzz);
104 | }
105 | }
106 |
107 | private static class JsonParameterConverter implements ParameterConverter.AdvancedParameterConverter {
108 |
109 | @Override
110 | public boolean accept(Class> parameterType, Annotation[] annotations) {
111 | return containAsJsonAnnotation(annotations);
112 | }
113 |
114 | @Override
115 | public void processParameter(ProcessParameterContext ctx) {
116 | ctx.getParameterSource().addValue(ctx.getParameterName(), JSON.toJson(ctx.getArg(), ctx.getParameterType()));
117 | }
118 |
119 |
120 | @Override
121 | public int order() {
122 | return 0;
123 | }
124 | }
125 |
126 | //test class to check we don't override converters with the same order
127 | public static class DummyColumnMapperFactory implements ColumnMapperFactory {
128 |
129 | @Override
130 | public ColumnMapper build(String name, Class> paramType) {
131 | return null;
132 | }
133 |
134 | @Override
135 | public int order() {
136 | return 0;
137 | }
138 |
139 | @Override
140 | public boolean accept(Class> paramType, Annotation[] annotations) {
141 | return false;
142 | }
143 |
144 | @Override
145 | public RowMapper getSingleColumnRowMapper(Class clzz) {
146 | return null;
147 | }
148 | }
149 |
150 | //test class to check we don't override converters with the same order
151 | public static class DummyParameterConverter implements ParameterConverter {
152 |
153 | @Override
154 | public boolean accept(Class> parameterType, Annotation[] annotations) {
155 | return false;
156 | }
157 |
158 | @Override
159 | public void processParameter(String parameterName, Object arg, Class> parameterType, MapSqlParameterSource ps) {
160 | ps.addValue(parameterName, null);
161 | }
162 |
163 | @Override
164 | public int order() {
165 | return 0;
166 | }
167 | }
168 |
169 | public static class ColumnMapperAndParametersConfiguration {
170 |
171 | @Bean
172 | List getColumnMapper() {
173 | return Arrays.asList(new DummyColumnMapperFactory(), new JsonColumnMapperFactory());
174 | }
175 |
176 | @Bean
177 | List getParameterConverter() {
178 | return Arrays.asList(new DummyParameterConverter(), new JsonParameterConverter());
179 | }
180 | }
181 |
182 | @Test
183 | public void simpleQueriesTest() {
184 |
185 |
186 | jq.createTable();
187 |
188 | Map map = Collections.singletonMap("MY_KEY", "MY_VALUE");
189 | jq.insertValue("TEST", map);
190 |
191 | JsonConf conf = jq.findByKey("TEST");
192 | Assert.assertTrue(conf.conf.equals(map));
193 |
194 | Assert.assertTrue(jq.findConfBoolByKey("TEST").equals(map));
195 |
196 | }
197 |
198 | public static class JsonConf {
199 |
200 | final String key;
201 | final Map conf;
202 |
203 | public JsonConf(@ConstructorAnnotationRowMapper.Column("CONF_KEY") String key,
204 | @ConstructorAnnotationRowMapper.Column("CONF_JSON") @AsJson Map conf) {
205 | this.key = key;
206 | this.conf = conf;
207 | }
208 |
209 | }
210 |
211 | @QueryRepository
212 | public interface JsonQueries {
213 | @Query("CREATE TABLE LA_CONF_JSON (CONF_KEY VARCHAR(64) PRIMARY KEY NOT NULL, CONF_JSON CLOB NOT NULL)")
214 | void createTable();
215 |
216 | @Query("INSERT INTO LA_CONF_JSON(CONF_KEY, CONF_JSON) VALUES(:key, :confJson)")
217 | int insertValue(@Bind("key") String key, @Bind("confJson") @AsJson Map conf);
218 |
219 | @Query("SELECT * FROM LA_CONF_JSON WHERE CONF_KEY = :key")
220 | JsonConf findByKey(@Bind("key") String key);
221 |
222 | @Query("SELECT CONF_JSON FROM LA_CONF_JSON WHERE CONF_KEY = :key")
223 | @AsJson
224 | Map findConfBoolByKey(@Bind("key") String key);
225 | }
226 | }
227 |
--------------------------------------------------------------------------------
/src/test/java/ch/digitalfondue/npjt/query/DateTimeQueriesTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt.query;
17 |
18 | import java.time.Instant;
19 | import java.time.LocalDate;
20 | import java.time.LocalDateTime;
21 | import java.time.LocalTime;
22 | import java.time.ZoneId;
23 | import java.time.ZonedDateTime;
24 | import java.time.temporal.ChronoUnit;
25 |
26 | import ch.digitalfondue.npjt.*;
27 | import org.junit.Assert;
28 | import org.junit.Test;
29 | import org.junit.runner.RunWith;
30 | import org.springframework.beans.factory.annotation.Autowired;
31 | import org.springframework.test.context.ContextConfiguration;
32 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
33 | import org.springframework.transaction.annotation.Transactional;
34 |
35 | import ch.digitalfondue.npjt.ConstructorAnnotationRowMapper.Column;
36 |
37 | @Transactional
38 | @RunWith(SpringJUnit4ClassRunner.class)
39 | @ContextConfiguration(classes = {TestJdbcConfiguration.class, QueryScannerConfiguration.class})
40 | public class DateTimeQueriesTest {
41 |
42 | @Autowired
43 | DateQueries dq;
44 |
45 | @Test
46 | public void dateQueriesTest() {
47 |
48 | dq.createTable();
49 |
50 | ZonedDateTime now = ZonedDateTime.now(ZoneId.of("UTC"));
51 |
52 | dq.insertValue("KEY", now);
53 | check(dq, "KEY", now);
54 |
55 | dq.insertValue("KEY2", now.toLocalDate());
56 | check(dq, "KEY2", now.toLocalDate());
57 |
58 | dq.insertValue("KEY3", now.toLocalDateTime());
59 | check(dq, "KEY3", now);
60 |
61 | Instant iNow = Instant.now();
62 | dq.insertValue("KEY4", iNow);
63 | Assert.assertEquals(iNow, dq.findInstantByKey("KEY4"));
64 | Assert.assertEquals(iNow, dq.findConfInstantByKey("KEY4").value);
65 |
66 | }
67 |
68 | private static LocalDateTime truncateToSec(LocalDateTime localDateTime) {
69 | return localDateTime.truncatedTo(ChronoUnit.SECONDS);
70 | }
71 |
72 | private static ZonedDateTime truncateToSec(ZonedDateTime zonedDateTime) {
73 | return zonedDateTime.truncatedTo(ChronoUnit.SECONDS);
74 | }
75 |
76 | private void check(DateQueries dq, String key, LocalDate now) {
77 | Assert.assertEquals(now, dq.findByKey(key).valueLocalDate);
78 | Assert.assertEquals(truncateToSec(LocalDateTime.of(now, LocalTime.MIDNIGHT)), truncateToSec(dq.findByKey(key).valueLocalDateTime));
79 | }
80 |
81 | private void check(DateQueries dq, String key, ZonedDateTime now) {
82 | Assert.assertEquals(truncateToSec(now), truncateToSec(dq.findByKey(key).value));
83 | Assert.assertEquals(truncateToSec(now), truncateToSec(dq.findDateByKey(key)));
84 | Assert.assertEquals(now.toLocalDate(), dq.findByKey(key).valueLocalDate);
85 | Assert.assertEquals(truncateToSec(now.toLocalDateTime()), truncateToSec(dq.findByKey(key).valueLocalDateTime));
86 | }
87 |
88 | public static class Conf {
89 | final String key;
90 | final ZonedDateTime value;
91 | final LocalDate valueLocalDate;
92 | final LocalDateTime valueLocalDateTime;
93 |
94 | public Conf(@Column("CONF_KEY") String key,
95 | @Column("CONF_VALUE") ZonedDateTime value,
96 | @Column("CONF_VALUE") LocalDate valueLocalDate,
97 | @Column("CONF_VALUE") LocalDateTime valueLocalDateTime) {
98 | this.key = key;
99 | this.value = value;
100 | this.valueLocalDate = valueLocalDate;
101 | this.valueLocalDateTime = valueLocalDateTime;
102 | }
103 | }
104 |
105 | public static class ConfInstant {
106 | final String key;
107 | final Instant value;
108 |
109 | public ConfInstant(@Column("CONF_KEY") String key,
110 | @Column("CONF_VALUE") Instant value) {
111 | this.key = key;
112 | this.value = value;
113 | }
114 | }
115 |
116 | @QueryRepository
117 | public interface DateQueries {
118 |
119 | @Query("CREATE TABLE LA_CONF_DATE (CONF_KEY VARCHAR(64) PRIMARY KEY NOT NULL, CONF_VALUE TIMESTAMP NOT NULL)")
120 | void createTable();
121 |
122 | @Query("INSERT INTO LA_CONF_DATE(CONF_KEY, CONF_VALUE) VALUES(:key, :value)")
123 | int insertValue(@Bind("key") String key, @Bind("value") ZonedDateTime date);
124 |
125 | @Query("INSERT INTO LA_CONF_DATE(CONF_KEY, CONF_VALUE) VALUES(:key, :value)")
126 | int insertValue(@Bind("key") String key, @Bind("value") LocalDate date);
127 |
128 | @Query("INSERT INTO LA_CONF_DATE(CONF_KEY, CONF_VALUE) VALUES(:key, :value)")
129 | int insertValue(@Bind("key") String key, @Bind("value") LocalDateTime date);
130 |
131 | @Query("INSERT INTO LA_CONF_DATE(CONF_KEY, CONF_VALUE) VALUES(:key, :value)")
132 | int insertValue(@Bind("key") String key, @Bind("value") Instant date);
133 |
134 | @Query("SELECT * FROM LA_CONF_DATE WHERE CONF_KEY = :key")
135 | Conf findByKey(@Bind("key") String key);
136 |
137 | @Query("SELECT CONF_VALUE FROM LA_CONF_DATE WHERE CONF_KEY = :key")
138 | Instant findInstantByKey(@Bind("key") String key);
139 |
140 | @Query("SELECT * FROM LA_CONF_DATE WHERE CONF_KEY = :key")
141 | ConfInstant findConfInstantByKey(@Bind("key") String key);
142 |
143 | @Query("SELECT CONF_VALUE FROM LA_CONF_DATE WHERE CONF_KEY = :key")
144 | ZonedDateTime findDateByKey(@Bind("key") String key);
145 |
146 | }
147 |
148 | }
149 |
--------------------------------------------------------------------------------
/src/test/java/ch/digitalfondue/npjt/query/EnumQueriesTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt.query;
17 |
18 | import java.util.List;
19 |
20 | import ch.digitalfondue.npjt.*;
21 | import org.junit.Assert;
22 | import org.junit.Test;
23 | import org.junit.runner.RunWith;
24 | import org.springframework.beans.factory.annotation.Autowired;
25 | import org.springframework.test.context.ContextConfiguration;
26 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
27 | import org.springframework.transaction.annotation.Transactional;
28 |
29 | import ch.digitalfondue.npjt.ConstructorAnnotationRowMapper.Column;
30 |
31 | @Transactional
32 | @RunWith(SpringJUnit4ClassRunner.class)
33 | @ContextConfiguration(classes = {TestJdbcConfiguration.class, QueryScannerConfiguration.class})
34 | public class EnumQueriesTest {
35 |
36 | @Autowired
37 | EnumQueries eq;
38 |
39 | @Test
40 | public void enumQueriesTest() {
41 | eq.createTable();
42 |
43 | eq.insert(null);
44 |
45 | eq.insert(TestEnum.TEST);
46 | eq.insert(TestEnum.TEST2);
47 |
48 | Assert.assertEquals(null, eq.findByNull());
49 | Assert.assertEquals(TestEnum.TEST, eq.findByKey(TestEnum.TEST));
50 | Assert.assertEquals(TestEnum.TEST2, eq.findByKey(TestEnum.TEST2));
51 |
52 | Assert.assertEquals(TestEnum.TEST, eq.findContainerByKey(TestEnum.TEST).key);
53 | Assert.assertEquals(null, eq.findContainerByKeyNull().key);
54 |
55 | List all = eq.findAll();
56 | Assert.assertTrue(all.contains(TestEnum.TEST));
57 | Assert.assertTrue(all.contains(TestEnum.TEST2));
58 | Assert.assertTrue(all.contains(null));
59 | }
60 |
61 | public enum TestEnum {
62 | TEST, TEST2;
63 | }
64 |
65 | public static class EnumContainer {
66 | final TestEnum key;
67 |
68 | public EnumContainer(@Column("CONF_KEY") TestEnum key) {
69 | this.key = key;
70 | }
71 | }
72 |
73 | @QueryRepository
74 | public interface EnumQueries {
75 |
76 | @Query("CREATE TABLE LA_CONF_ENUM (CONF_KEY VARCHAR(64))")
77 | void createTable();
78 |
79 | @Query("INSERT INTO LA_CONF_ENUM (CONF_KEY) VALUES (:key)")
80 | int insert(@Bind("key") TestEnum test);
81 |
82 | //most useless method ever :D
83 | @Query("SELECT CONF_KEY FROM LA_CONF_ENUM WHERE CONF_KEY = :key")
84 | TestEnum findByKey(@Bind("key") TestEnum test);
85 |
86 | @Query("SELECT CONF_KEY FROM LA_CONF_ENUM WHERE CONF_KEY is null")
87 | TestEnum findByNull();
88 |
89 | @Query("SELECT CONF_KEY FROM LA_CONF_ENUM WHERE CONF_KEY = :key")
90 | EnumContainer findContainerByKey(@Bind("key") TestEnum test);
91 |
92 | @Query("SELECT CONF_KEY FROM LA_CONF_ENUM WHERE CONF_KEY is null")
93 | EnumContainer findContainerByKeyNull();
94 |
95 | @Query("SELECT CONF_KEY FROM LA_CONF_ENUM")
96 | List findAll();
97 |
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/test/java/ch/digitalfondue/npjt/query/QueryRepo.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt.query;
17 |
18 | import ch.digitalfondue.npjt.QueryRepository;
19 |
20 | @QueryRepository
21 | public interface QueryRepo {
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/src/test/java/ch/digitalfondue/npjt/query/SimpleQueriesTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt.query;
17 |
18 | import java.sql.ResultSet;
19 | import java.sql.SQLException;
20 | import java.util.List;
21 | import java.util.Optional;
22 |
23 | import ch.digitalfondue.npjt.*;
24 | import org.junit.Assert;
25 | import org.junit.Test;
26 | import org.junit.runner.RunWith;
27 | import org.springframework.beans.factory.annotation.Autowired;
28 | import org.springframework.jdbc.core.RowMapper;
29 | import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
30 | import org.springframework.test.context.ContextConfiguration;
31 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
32 | import org.springframework.transaction.annotation.Transactional;
33 |
34 | import ch.digitalfondue.npjt.ConstructorAnnotationRowMapper.Column;
35 |
36 | import javax.sql.DataSource;
37 |
38 | @Transactional
39 | @RunWith(SpringJUnit4ClassRunner.class)
40 | @ContextConfiguration(classes = {TestJdbcConfiguration.class, QueryScannerConfiguration.class})
41 | public class SimpleQueriesTest {
42 |
43 | @Autowired
44 | DataSource dataSource;
45 |
46 | @Autowired
47 | MySimpleQueries mq;
48 |
49 | /**
50 | * Simple DB interaction.
51 | */
52 | @Test
53 | public void simpleQueriesTest() {
54 |
55 | mq.createTable();
56 |
57 | Assert.assertTrue(mq.findAll().isEmpty());
58 | Assert.assertEquals(1, mq.insertValue("MY_KEY", "MY_VALUE"));
59 |
60 | Conf conf = mq.findByKey("MY_KEY");
61 |
62 | Assert.assertEquals("MY_KEY", conf.key);
63 | Assert.assertEquals("MY_VALUE", conf.value);
64 |
65 | Assert.assertFalse(mq.findAll().isEmpty());
66 |
67 | Assert.assertEquals(1, mq.update("MY_KEY", "MY_VALUE_UPDATED"));
68 |
69 | Conf confUpd = mq.findByKey("MY_KEY");
70 | Assert.assertEquals("MY_KEY", confUpd.key);
71 | Assert.assertEquals("MY_VALUE_UPDATED", confUpd.value);
72 |
73 | Assert.assertTrue(mq.findAllKeys().contains("MY_KEY"));
74 |
75 | Assert.assertEquals("MY_VALUE_UPDATED", mq.findValueForKey("MY_KEY"));
76 |
77 | Assert.assertEquals("MY_TEMPLATE", mq.template());
78 |
79 | Assert.assertNotNull(mq.getNamedParameterJdbcTemplate());
80 |
81 | Assert.assertEquals("MY_VALUE_UPDATED", mq.findOptionalValueForKey("MY_KEY").get());
82 |
83 | Assert.assertFalse(mq.findOptionalValueForKey("MY_KEY_NOT").isPresent());
84 |
85 | Assert.assertEquals("MY_VALUE_UPDATED", mq.findOptionalWrappedValueForKey("MY_KEY").get().value);
86 |
87 | Assert.assertFalse(mq.findOptionalWrappedValueForKey("MY_KEY_NOT").isPresent());
88 |
89 | Assert.assertEquals("defaultMethod", mq.defaultMethod());
90 |
91 | //
92 | MySimpleQueries mq2 = QueryFactory.from(MySimpleQueries.class, "HSQLDB", dataSource);
93 |
94 | Assert.assertEquals(mq.findByKey("MY_KEY"), mq2.findByKey("MY_KEY"));
95 | //
96 | }
97 |
98 | public static class Conf {
99 | final String key;
100 | final String value;
101 |
102 | public Conf(@Column("CONF_KEY") String key,
103 | @Column("CONF_VALUE") String value) {
104 | this.key = key;
105 | this.value = value;
106 | }
107 |
108 | @Override
109 | public boolean equals(Object obj) {
110 | if(obj != null && obj instanceof Conf) {
111 | Conf c2 = (Conf) obj;
112 | return key.equals(c2.key) && value.equals(c2.value);
113 | }
114 | return false;
115 | }
116 | }
117 |
118 | public static class MyCustomWrapper {
119 | final String value;
120 |
121 | public MyCustomWrapper(String value) {
122 | this.value = value;
123 | }
124 | }
125 |
126 | public static class MyCustomWrapperRowMapper implements RowMapper {
127 |
128 | @Override
129 | public MyCustomWrapper mapRow(ResultSet rs, int rowNum) throws SQLException {
130 | return new MyCustomWrapper(rs.getString(1));
131 | }
132 |
133 | }
134 |
135 | @QueryRepository
136 | public interface MySimpleQueries {
137 |
138 | @Query("CREATE TABLE LA_CONF (CONF_KEY VARCHAR(64) PRIMARY KEY NOT NULL, CONF_VALUE CLOB NOT NULL)")
139 | void createTable();
140 |
141 | @Query("INSERT INTO LA_CONF(CONF_KEY, CONF_VALUE) VALUES(:key, :value)")
142 | int insertValue(@Bind("key") String key, @Bind("value") String value);
143 |
144 | @Query("SELECT * FROM LA_CONF WHERE CONF_KEY = :key")
145 | Conf findByKey(@Bind("key") String key);
146 |
147 | @Query("SELECT * FROM LA_CONF")
148 | List findAll();
149 |
150 | @Query("SELECT CONF_KEY FROM LA_CONF")
151 | List findAllKeys();
152 |
153 | @Query("SELECT CONF_VALUE FROM LA_CONF WHERE CONF_KEY = :key")
154 | String findValueForKey(@Bind("key") String key);
155 |
156 | @Query("SELECT CONF_VALUE FROM LA_CONF WHERE CONF_KEY = :key")
157 | Optional findOptionalValueForKey(@Bind("key") String key);
158 |
159 | @Query(value = "SELECT CONF_VALUE FROM LA_CONF WHERE CONF_KEY = :key", mapper = MyCustomWrapperRowMapper.class)
160 | Optional findOptionalWrappedValueForKey(@Bind("key") String key);
161 |
162 | @Query("UPDATE LA_CONF SET CONF_VALUE = :value WHERE CONF_KEY = :key")
163 | int update(@Bind("key") String key, @Bind("value") String value);
164 |
165 | @Query(type = QueryType.TEMPLATE, value = "MY_TEMPLATE")
166 | String template();
167 |
168 | NamedParameterJdbcTemplate getNamedParameterJdbcTemplate();
169 |
170 | default String defaultMethod() {
171 | return "defaultMethod";
172 | }
173 | }
174 |
175 | }
176 |
--------------------------------------------------------------------------------
/src/test/java/ch/digitalfondue/npjt/query/customfactory/CustomJSONQueriesWithCustomQueryFactoryTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2019 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt.query.customfactory;
17 |
18 | import ch.digitalfondue.npjt.*;
19 | import ch.digitalfondue.npjt.mapper.ColumnMapper;
20 | import ch.digitalfondue.npjt.mapper.ColumnMapperFactory;
21 | import ch.digitalfondue.npjt.mapper.ParameterConverter;
22 | import com.google.gson.Gson;
23 | import com.google.gson.GsonBuilder;
24 | import org.junit.Assert;
25 | import org.junit.Test;
26 | import org.junit.runner.RunWith;
27 | import org.springframework.beans.factory.annotation.Autowired;
28 | import org.springframework.context.annotation.Bean;
29 | import org.springframework.jdbc.core.RowMapper;
30 | import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
31 | import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
32 | import org.springframework.test.context.ContextConfiguration;
33 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
34 | import org.springframework.transaction.annotation.Transactional;
35 |
36 | import javax.sql.DataSource;
37 | import java.lang.annotation.*;
38 | import java.sql.ResultSet;
39 | import java.sql.SQLException;
40 | import java.util.*;
41 |
42 | @Transactional
43 | @RunWith(SpringJUnit4ClassRunner.class)
44 | @ContextConfiguration(classes = {TestJdbcConfiguration.class, CustomJSONQueriesWithCustomQueryFactoryTest.CustomQueryFactoryConf.class})
45 | public class CustomJSONQueriesWithCustomQueryFactoryTest {
46 |
47 | @Autowired
48 | JsonQueries jq;
49 |
50 | private static Gson JSON = new GsonBuilder().create();
51 |
52 | @Retention(RetentionPolicy.RUNTIME)
53 | @Target({ElementType.PARAMETER, ElementType.METHOD})
54 | public @interface AsJson {
55 | }
56 |
57 | private static class JsonColumnMapper extends ColumnMapper {
58 |
59 | JsonColumnMapper(String name, Class> paramType) {
60 | super(name, paramType);
61 | }
62 |
63 | @Override
64 | public Object getObject(ResultSet rs) throws SQLException {
65 | return JSON.fromJson(rs.getString(name), paramType);
66 | }
67 | }
68 |
69 | private static boolean containAsJsonAnnotation(Annotation[] annotations) {
70 | if(annotations == null) {
71 | return false;
72 | }
73 | for(Annotation annotation : annotations) {
74 | if(annotation.annotationType() == AsJson.class) {
75 | return true;
76 | }
77 | }
78 | return false;
79 | }
80 |
81 | private static class JsonColumnMapperFactory implements ColumnMapperFactory {
82 |
83 | @Override
84 | public ColumnMapper build(String name, Class> paramType) {
85 | return new JsonColumnMapper(name, paramType);
86 | }
87 |
88 | @Override
89 | public int order() {
90 | return 0;
91 | }
92 |
93 | @Override
94 | public boolean accept(Class> paramType, Annotation[] annotations) {
95 | return containAsJsonAnnotation(annotations);
96 | }
97 |
98 | @Override
99 | public RowMapper getSingleColumnRowMapper(final Class clzz) {
100 | return (resultSet, rowNum) -> JSON.fromJson(resultSet.getString(1), clzz);
101 | }
102 | }
103 |
104 | private static class JsonParameterConverter implements ParameterConverter {
105 |
106 | @Override
107 | public boolean accept(Class> parameterType, Annotation[] annotations) {
108 | return containAsJsonAnnotation(annotations);
109 | }
110 |
111 | @Override
112 | public void processParameter(String parameterName, Object arg, Class> parameterType, MapSqlParameterSource ps) {
113 | ps.addValue(parameterName, JSON.toJson(arg, parameterType));
114 | }
115 |
116 | @Override
117 | public int order() {
118 | return 0;
119 | }
120 | }
121 |
122 | @EnableNpjt(queryFactory = CustomQueryFactory.class, basePackages = {"ch.digitalfondue.npjt.query.customfactory"})
123 | public static class CustomQueryFactoryConf {
124 |
125 | @Bean
126 | public NamedParameterJdbcTemplate getNamedParameterJdbcTemplate(DataSource dataSource) {
127 | return new NamedParameterJdbcTemplate(dataSource);
128 | }
129 | }
130 |
131 | public static class CustomQueryFactory extends QueryFactory {
132 |
133 | public CustomQueryFactory(Class targetInterface, String activeDB) {
134 | super(targetInterface, activeDB);
135 | }
136 |
137 | @Override
138 | public List getDefaultFactories() {
139 | List a = super.getDefaultFactories();
140 | a.add(new JsonColumnMapperFactory());
141 | return a;
142 | }
143 |
144 | @Override
145 | public List getDefaultParameterConverters() {
146 | List a = super.getDefaultParameterConverters();
147 | a.add(new JsonParameterConverter());
148 | return a;
149 | }
150 | }
151 |
152 | @Test
153 | public void simpleQueriesTest() {
154 |
155 |
156 | jq.createTable();
157 |
158 | Map map = Collections.singletonMap("MY_KEY", "MY_VALUE");
159 | jq.insertValue("TEST", map);
160 |
161 | JsonConf conf = jq.findByKey("TEST");
162 | Assert.assertTrue(conf.conf.equals(map));
163 |
164 | Assert.assertTrue(jq.findConfBoolByKey("TEST").equals(map));
165 |
166 | }
167 |
168 | public static class JsonConf {
169 |
170 | final String key;
171 | final Map conf;
172 |
173 | public JsonConf(@ConstructorAnnotationRowMapper.Column("CONF_KEY") String key,
174 | @ConstructorAnnotationRowMapper.Column("CONF_JSON") @AsJson Map conf) {
175 | this.key = key;
176 | this.conf = conf;
177 | }
178 |
179 | }
180 |
181 | @QueryRepository
182 | public interface JsonQueries {
183 | @Query("CREATE TABLE LA_CONF_JSON2 (CONF_KEY VARCHAR(64) PRIMARY KEY NOT NULL, CONF_JSON CLOB NOT NULL)")
184 | void createTable();
185 |
186 | @Query("INSERT INTO LA_CONF_JSON2(CONF_KEY, CONF_JSON) VALUES(:key, :confJson)")
187 | int insertValue(@Bind("key") String key, @Bind("confJson") @AsJson Map conf);
188 |
189 | @Query("SELECT * FROM LA_CONF_JSON2 WHERE CONF_KEY = :key")
190 | JsonConf findByKey(@Bind("key") String key);
191 |
192 | @Query("SELECT CONF_JSON FROM LA_CONF_JSON2 WHERE CONF_KEY = :key")
193 | @AsJson
194 | Map findConfBoolByKey(@Bind("key") String key);
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/src/test/java/ch/digitalfondue/npjt/query/deeper/QueryRepo2.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © 2015 digitalfondue (info@digitalfondue.ch)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ch.digitalfondue.npjt.query.deeper;
17 |
18 | import ch.digitalfondue.npjt.QueryRepository;
19 |
20 | @QueryRepository
21 | public interface QueryRepo2 {
22 |
23 | }
24 |
--------------------------------------------------------------------------------