├── LICENSE ├── README-EN.md ├── README.md ├── pom.xml └── src └── main └── java └── com └── littlenb └── mybatisjpa ├── annotation ├── CodeEnum.java ├── InsertDefinition.java ├── MappedJdbcType.java └── UpdateDefinition.java ├── keygen ├── DefaultIdGenerator.java ├── IdGenerator.java └── IdentityKeyGenerator.java ├── rs ├── ResultMapParser.java └── ResultTypePlugin.java ├── statement ├── AbstractStatementFactory.java ├── AnnotationStatementRegistry.java ├── InsertStatementFactory.java ├── StatementFactory.java └── UpdateStatementFactory.java ├── support ├── AnnotationStatementScanner.java ├── Certainty.java ├── Constant.java ├── MybatisJapConfiguration.java └── template │ ├── InsertBatchSqlTemplate.java │ ├── InsertCertainSqlTemplate.java │ ├── InsertIgnoreNullSqlTemplate.java │ ├── InsertSqlTemplate.java │ ├── SqlTemplate.java │ ├── UpdateCertainSqlTemplate.java │ ├── UpdateIgnoreNullSqlTemplate.java │ └── UpdateSqlTemplate.java ├── type ├── CodeEnumAdapter.java ├── CodeType.java ├── ICodeEnum.java ├── IntCodeEnumTypeHandler.java ├── SelectorStrategy.java └── StringCodeEnumTypeHandler.java └── util ├── ColumnMetaResolver.java ├── FieldReflectUtil.java ├── NamingPolicy.java ├── NamingStrategy.java └── PersistentUtil.java /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [2017] [svili] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README-EN.md: -------------------------------------------------------------------------------- 1 | # mybatis-jpa 2 | 3 | [![Mybatis](https://img.shields.io/badge/mybatis-3.4.x-brightgreen.svg)](https://maven-badges.herokuapp.com/maven-central/org.mybatis/mybatis) 4 | [![JDK 1.7](https://img.shields.io/badge/JDK-1.7-green.svg)]() 5 | [![maven central](https://img.shields.io/badge/version-2.2.0-brightgreen.svg)](http://search.maven.org/#artifactdetails%7Ccom.github.cnsvili%7Cmybatis-jpa%7C2.1.3%7C) 6 | [![APACHE 2 License](https://img.shields.io/badge/license-Apache2-blue.svg?style=flat)](LICENSE) 7 | 8 | :book: English Documentation | [:book: 中文文档](README.md) 9 | 10 | The plugins for mybatis, in order to provider the ability to handler jpa. 11 | 12 | ## maven 13 | 14 | ```xml 15 | 16 | com.littlenb 17 | mybatis-jpa 18 | 2.2.0 19 | 20 | ``` 21 | 22 | ## Plugin boom 23 | 24 | + ResultTypePlugin [![Interceptor](https://img.shields.io/badge/Interceptor-brightgreen.svg)](https://github.com/svili365/mybatis-jpa/wiki/ResultTypePlugin) 25 | 26 | Introduce the JPA annotation to handle result set mappings(JavaBean/POJO). 27 | 28 | It means with ResultTypePlugin,no longer need to be build ResultMap. 29 | 30 | + AnnotationStatementScanner [![statementFactory](https://img.shields.io/badge/StatementFactory-brightgreen.svg)](https://github.com/svili365/mybatis-jpa/wiki/DefinitionStatementScanner) 31 | 32 | register MappedStatement with annotation-based,only support for Insert and Update. 33 | 34 | 35 | Please view test project where has more examples[mybatis-jpa-test](https://github.com/svili365/mybatis-jpa-test) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mybatis-jpa 2 | 3 | [![Mybatis](https://img.shields.io/badge/mybatis-3.4.x-brightgreen.svg)](https://maven-badges.herokuapp.com/maven-central/org.mybatis/mybatis) 4 | [![JDK 1.7](https://img.shields.io/badge/JDK-1.7-green.svg)]() 5 | [![maven central](https://img.shields.io/badge/version-2.2.0-brightgreen.svg)](http://search.maven.org/#artifactdetails%7Ccom.github.cnsvili%7Cmybatis-jpa%7C2.1.3%7C) 6 | [![APACHE 2 License](https://img.shields.io/badge/license-Apache2-blue.svg?style=flat)](LICENSE) 7 | 8 | [:book: English Documentation](README-EN.md) | :book: 中文文档 9 | 10 | Mybatis插件,提供Mybatis处理JPA的能力. 11 | 12 | ## maven 13 | 14 | ```xml 15 | 16 | com.littlenb 17 | mybatis-jpa 18 | 2.2.0 19 | 20 | ``` 21 | 22 | ## 插件清单 23 | 24 | + ResultTypePlugin [![Interceptor](https://img.shields.io/badge/Interceptor-brightgreen.svg)](https://github.com/svili365/mybatis-jpa/wiki/ResultTypePlugin) 25 | 26 | 对于常规的结果映射, 不需要再构建ResultMap, ResultTypePlugin按照(JavaBean/POJO)中JPA注解, 解析生成相应的ResultMap. 27 | 28 | + AnnotationStatementScanner [![statementFactory](https://img.shields.io/badge/StatementFactory-brightgreen.svg)](https://github.com/svili365/mybatis-jpa/wiki/DefinitionStatementScanner) 29 | 30 | 注册MappedStatement, 基于注解, 仅支持Insert和Update. 31 | 32 | 33 | 更多示例请查看[mybatis-jpa-test](https://github.com/svili365/mybatis-jpa-test) 34 | 35 | ## 联系方式 36 | QQ交流群:246912326 37 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.littlenb 7 | mybatis-jpa 8 | jar 9 | 2.5.1 10 | 11 | mybatis-jpa 12 | The plugins for mybatis, in order to provider the ability to handler jpa. 13 | 14 | https://github.com/svili365/mybatis-jpa 15 | 16 | 17 | UTF-8 18 | UTF-8 19 | 1.7 20 | 21 | 3.4.0 22 | 23 | 24 | 25 | 26 | 27 | javax.persistence 28 | persistence-api 29 | 1.0.2 30 | 31 | 32 | 33 | org.mybatis 34 | mybatis 35 | ${mybatis.version} 36 | provided 37 | 38 | 39 | 40 | org.reflections 41 | reflections 42 | 0.9.11 43 | 44 | 45 | 46 | com.littlenb 47 | snowflake 48 | 1.0.5 49 | 50 | 51 | 52 | junit 53 | junit 54 | 4.12 55 | test 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | org.apache.maven.plugins 65 | maven-compiler-plugin 66 | 3.7.0 67 | 68 | ${java.version} 69 | ${java.version} 70 | 71 | 72 | 73 | org.apache.maven.plugins 74 | maven-surefire-plugin 75 | 76 | true 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | ${project.basedir} 85 | META-INF 86 | 87 | LICENSE 88 | NOTICE 89 | 90 | 91 | 92 | ${project.build.sourceDirectory} 93 | 94 | **/*.java 95 | 96 | 97 | 98 | 99 | 100 | ${project.build.testSourceDirectory} 101 | 102 | **/*.java 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | The Apache Software License, Version 2.0 111 | http://www.apache.org/licenses/LICENSE-2.0.txt 112 | 113 | 114 | 115 | 116 | 117 | svili365 118 | svili365@outlook.com 119 | 120 | 121 | 122 | 123 | scm:git@github.com:svili365/mybatis-jpa.git 124 | scm:git@github.com:svili365/mybatis-jpa.git 125 | git@github.com:svili365/mybatis-jpa.git 126 | 127 | 128 | 129 | 130 | release 131 | 132 | 133 | 134 | 135 | org.apache.maven.plugins 136 | maven-source-plugin 137 | 3.0.0 138 | 139 | 140 | package 141 | 142 | jar-no-fork 143 | 144 | 145 | 146 | 147 | 148 | 149 | org.apache.maven.plugins 150 | maven-javadoc-plugin 151 | 2.10.4 152 | 153 | 154 | package 155 | 156 | jar 157 | 158 | 159 | 160 | 161 | 162 | 163 | org.apache.maven.plugins 164 | maven-gpg-plugin 165 | 1.5 166 | 167 | 168 | verify 169 | 170 | sign 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | sonatype 180 | https://oss.sonatype.org/content/repositories/snapshots/ 181 | false 182 | 183 | 184 | sonatype 185 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 186 | 187 | 188 | 189 | 190 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/annotation/CodeEnum.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.annotation; 2 | 3 | import com.littlenb.mybatisjpa.type.CodeType; 4 | import java.lang.annotation.Documented; 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * The priority is higher than Enumerated 12 | * 13 | * @author sway.li 14 | **/ 15 | @Documented 16 | @Retention(RetentionPolicy.RUNTIME) 17 | @Target(ElementType.FIELD) 18 | public @interface CodeEnum { 19 | 20 | CodeType value() default CodeType.INT; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/annotation/InsertDefinition.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.annotation; 2 | 3 | import com.littlenb.mybatisjpa.type.SelectorStrategy; 4 | import java.lang.annotation.Documented; 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * @author sway.li 12 | **/ 13 | @Documented 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Target(ElementType.METHOD) 16 | public @interface InsertDefinition { 17 | 18 | SelectorStrategy strategy() default SelectorStrategy.NONE; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/annotation/MappedJdbcType.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.annotation; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | import org.apache.ibatis.type.JdbcType; 9 | 10 | /** 11 | * @author sway.li 12 | **/ 13 | @Documented 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Target(ElementType.FIELD) 16 | public @interface MappedJdbcType { 17 | 18 | JdbcType value(); 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/annotation/UpdateDefinition.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.annotation; 2 | 3 | import com.littlenb.mybatisjpa.type.SelectorStrategy; 4 | import java.lang.annotation.Documented; 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * @author sway.li 12 | **/ 13 | @Documented 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Target(ElementType.METHOD) 16 | public @interface UpdateDefinition { 17 | 18 | SelectorStrategy strategy() default SelectorStrategy.NONE; 19 | 20 | /** 21 | * default id = #{id} 22 | */ 23 | String where() default "id = #{id}"; 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/keygen/DefaultIdGenerator.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.keygen; 2 | 3 | 4 | import com.littlenb.snowflake.support.SecondsIdGeneratorFactory; 5 | 6 | /** 7 | * @author sway.li 8 | * @since 2.5.1 9 | */ 10 | public class DefaultIdGenerator implements IdGenerator { 11 | 12 | private com.littlenb.snowflake.sequence.IdGenerator idGenerator; 13 | 14 | private DefaultIdGenerator() { 15 | } 16 | 17 | public static DefaultIdGenerator newInstance() { 18 | // 2017-01-01 00:00:00 19 | return newInstance(1483200000L, 1L); 20 | } 21 | 22 | public static DefaultIdGenerator newInstance(long seconds, long workerId) { 23 | DefaultIdGenerator instance = new DefaultIdGenerator(); 24 | SecondsIdGeneratorFactory idGeneratorFactory = new SecondsIdGeneratorFactory(seconds); 25 | instance.idGenerator = idGeneratorFactory.create(workerId); 26 | return instance; 27 | } 28 | 29 | @Override 30 | public synchronized Object nextId() { 31 | return idGenerator.nextId(); 32 | } 33 | 34 | @Override 35 | public synchronized Object[] nextSegment(int size) { 36 | long[] segment = idGenerator.nextSegment(size); 37 | Object[] array = new Object[size]; 38 | for (int i = 0; i < size; i++) { 39 | array[i] = segment[i]; 40 | } 41 | return array; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/keygen/IdGenerator.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.keygen; 2 | 3 | /** 4 | * @author sway.li 5 | */ 6 | public interface IdGenerator { 7 | 8 | Object nextId(); 9 | 10 | Object[] nextSegment(int size); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/keygen/IdentityKeyGenerator.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.keygen; 2 | 3 | import java.sql.Statement; 4 | import java.util.Arrays; 5 | import java.util.Collection; 6 | import java.util.Iterator; 7 | import java.util.List; 8 | import java.util.Map; 9 | import org.apache.ibatis.executor.Executor; 10 | import org.apache.ibatis.executor.ExecutorException; 11 | import org.apache.ibatis.executor.keygen.KeyGenerator; 12 | import org.apache.ibatis.mapping.MappedStatement; 13 | import org.apache.ibatis.reflection.MetaObject; 14 | import org.apache.ibatis.session.Configuration; 15 | 16 | /** 17 | * @author sway.li 18 | */ 19 | public class IdentityKeyGenerator implements KeyGenerator { 20 | 21 | private IdGenerator idGenerator; 22 | 23 | public IdentityKeyGenerator(IdGenerator idGenerator) { 24 | this.idGenerator = idGenerator; 25 | } 26 | 27 | @Override 28 | public void processBefore(Executor executor, MappedStatement ms, Statement stmt, 29 | Object parameter) { 30 | processGeneratedKeys(ms, stmt, parameter); 31 | } 32 | 33 | @Override 34 | public void processAfter(Executor executor, MappedStatement ms, Statement stmt, 35 | Object parameter) { 36 | // do nothing 37 | } 38 | 39 | public void processGeneratedKeys(MappedStatement ms, Statement stmt, Object parameter) { 40 | final String[] keyProperties = ms.getKeyProperties(); 41 | if (keyProperties == null || keyProperties.length == 0) { 42 | return; 43 | } 44 | if (keyProperties.length > 1) { 45 | throw new ExecutorException( 46 | "There are more than one keyProperty in MappedStatement : " + ms.getId() + "."); 47 | } 48 | 49 | if (parameter instanceof Collection) { 50 | handleCollection(ms, stmt, (Collection) parameter); 51 | } else if (parameter.getClass().isArray()) { 52 | handleCollection(ms, stmt, Arrays.asList(parameter)); 53 | } else if (parameter instanceof Map) { 54 | Map parameterMap = (Map) parameter; 55 | if (parameterMap.containsKey("collection")) { 56 | handleCollection(ms, stmt, (Collection) parameterMap.get("collection")); 57 | } else if (parameterMap.containsKey("list")) { 58 | handleCollection(ms, stmt, (List) parameterMap.get("list")); 59 | } else if (parameterMap.containsKey("array")) { 60 | handleCollection(ms, stmt, Arrays.asList((Object[]) parameterMap.get("array"))); 61 | } 62 | } else { 63 | handleObject(ms, stmt, parameter, idGenerator.nextId()); 64 | } 65 | } 66 | 67 | private void handleCollection(MappedStatement ms, Statement stmt, Collection collection) { 68 | if (collection.isEmpty()) { 69 | return; 70 | } 71 | int size = collection.size(); 72 | Object[] idArray = idGenerator.nextSegment(size); 73 | 74 | Iterator iterator = collection.iterator(); 75 | for (int i = 0; i < size; i++) { 76 | Object parameter = iterator.next(); 77 | Object id = idArray[i]; 78 | handleObject(ms, stmt, parameter, id); 79 | } 80 | } 81 | 82 | private void handleObject(MappedStatement ms, Statement stmt, Object parameter, Object idValue) { 83 | final String[] keyProperties = ms.getKeyProperties(); 84 | final Configuration configuration = ms.getConfiguration(); 85 | final MetaObject metaParam = configuration.newMetaObject(parameter); 86 | setValue(metaParam, keyProperties[0], idValue); 87 | } 88 | 89 | private void setValue(MetaObject metaParam, String property, Object value) { 90 | if (metaParam.hasSetter(property)) { 91 | metaParam.setValue(property, value); 92 | } else { 93 | throw new ExecutorException( 94 | "No setter found for the keyProperty '" + property + "' in " + metaParam 95 | .getOriginalObject().getClass().getName() + "."); 96 | } 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/rs/ResultMapParser.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.rs; 2 | 3 | import com.littlenb.mybatisjpa.util.ColumnMetaResolver; 4 | import com.littlenb.mybatisjpa.util.PersistentUtil; 5 | import java.lang.reflect.Field; 6 | import java.lang.reflect.ParameterizedType; 7 | import java.lang.reflect.Type; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.concurrent.ConcurrentHashMap; 11 | import javax.persistence.Id; 12 | import javax.persistence.OneToMany; 13 | import javax.persistence.OneToOne; 14 | import org.apache.ibatis.builder.MapperBuilderAssistant; 15 | import org.apache.ibatis.mapping.ResultFlag; 16 | import org.apache.ibatis.mapping.ResultMap; 17 | import org.apache.ibatis.mapping.ResultMapping; 18 | import org.apache.ibatis.session.Configuration; 19 | import org.apache.ibatis.type.JdbcType; 20 | import org.apache.ibatis.type.TypeHandler; 21 | 22 | /** 23 | * @author sway.li 24 | */ 25 | public class ResultMapParser { 26 | 27 | private Configuration configuration; 28 | 29 | /** 30 | * Result Maps collection,key : id 31 | */ 32 | private ConcurrentHashMap resultMaps = new ConcurrentHashMap<>(); 33 | 34 | public ResultMapParser(Configuration configuration) { 35 | this.configuration = configuration; 36 | } 37 | 38 | public ResultMap reloadResultMap(String resource, String id, Class type) { 39 | if (!resultMaps.containsKey(id)) { 40 | resultMaps.put(id, resolveResultMap(resource, id, type)); 41 | } 42 | 43 | return resultMaps.get(id); 44 | } 45 | 46 | public void registerResultMap(ResultMap resultMap) { 47 | configuration.addResultMap(resultMap); 48 | } 49 | 50 | public ResultMap resolveResultMap(String resource, String id, Class type) { 51 | List resultMappings = resolveResultMappings(resource, id, type); 52 | return new ResultMap.Builder(configuration, id, type, resultMappings).build(); 53 | } 54 | 55 | public List resolveResultMappings(String resource, String id, Class type) { 56 | List resultMappings = new ArrayList<>(); 57 | 58 | MapperBuilderAssistant assistant = new MapperBuilderAssistant(configuration, resource); 59 | 60 | List fields = PersistentUtil.getPersistentFields(type); 61 | 62 | for (Field field : fields) { 63 | // java field name 64 | String property = field.getName(); 65 | // sql column name 66 | String column = PersistentUtil.getColumnName(field); 67 | Class javaType = field.getType(); 68 | 69 | //resultMap is not need jdbcType 70 | JdbcType jdbcType = null; 71 | 72 | String nestedSelect = null; 73 | String nestedResultMap = null; 74 | if (PersistentUtil.isAssociationField(field)) { 75 | // OneToOne or OneToMany 76 | 77 | // mappedBy 78 | column = PersistentUtil.getMappedName(field); 79 | if (field.isAnnotationPresent(OneToOne.class)) { 80 | nestedResultMap = id + "_association[" + javaType.getSimpleName() + "]"; 81 | registerResultMap(resolveResultMap(resource, nestedResultMap, javaType)); 82 | } 83 | if (field.isAnnotationPresent(OneToMany.class)) { 84 | Type genericType = field.getGenericType(); 85 | if (genericType instanceof ParameterizedType) { 86 | ParameterizedType pt = (ParameterizedType) genericType; 87 | Class actualType = (Class) pt.getActualTypeArguments()[0]; 88 | // create resultMap with actualType 89 | nestedResultMap = id + "collection[" + actualType.getSimpleName() + "]"; 90 | registerResultMap(resolveResultMap(resource, nestedResultMap, actualType)); 91 | } 92 | } 93 | } 94 | 95 | String notNullColumn = null; 96 | String columnPrefix = null; 97 | String resultSet = null; 98 | String foreignColumn = null; 99 | // if primaryKey,then flags.add(ResultFlag.ID); 100 | List flags = new ArrayList<>(); 101 | if (field.isAnnotationPresent(Id.class)) { 102 | flags.add(ResultFlag.ID); 103 | } 104 | // lazy or eager 105 | boolean lazy = false; 106 | // typeHandler 107 | Class> typeHandlerClass = ColumnMetaResolver 108 | .resolveTypeHandler(field); 109 | 110 | ResultMapping resultMapping = assistant.buildResultMapping(type, property, column, 111 | javaType, jdbcType, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, 112 | typeHandlerClass, flags, resultSet, foreignColumn, lazy); 113 | resultMappings.add(resultMapping); 114 | } 115 | return resultMappings; 116 | 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/rs/ResultTypePlugin.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.rs; 2 | 3 | import com.littlenb.mybatisjpa.util.FieldReflectUtil; 4 | import java.sql.Statement; 5 | import java.util.ArrayList; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.Properties; 10 | import javax.persistence.Entity; 11 | import javax.persistence.Table; 12 | import org.apache.ibatis.executor.resultset.DefaultResultSetHandler; 13 | import org.apache.ibatis.executor.resultset.ResultSetHandler; 14 | import org.apache.ibatis.mapping.MappedStatement; 15 | import org.apache.ibatis.mapping.ResultMap; 16 | import org.apache.ibatis.plugin.Interceptor; 17 | import org.apache.ibatis.plugin.Intercepts; 18 | import org.apache.ibatis.plugin.Invocation; 19 | import org.apache.ibatis.plugin.Plugin; 20 | import org.apache.ibatis.plugin.Signature; 21 | import org.apache.ibatis.session.Configuration; 22 | 23 | /** 24 | * @author sway.li 25 | **/ 26 | @Intercepts({@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = { 27 | Statement.class})}) 28 | public class ResultTypePlugin implements Interceptor { 29 | 30 | @Override 31 | public Object intercept(Invocation invocation) throws Throwable { 32 | if (invocation.getTarget() instanceof DefaultResultSetHandler) { 33 | DefaultResultSetHandler resultSetHandler = (DefaultResultSetHandler) invocation.getTarget(); 34 | 35 | Object[] args = invocation.getArgs(); 36 | Statement stmt = (Statement) args[0]; 37 | 38 | MappedStatement mappedStatement = (MappedStatement) FieldReflectUtil 39 | .getFieldValue(resultSetHandler, "mappedStatement"); 40 | 41 | List resultMaps = mappedStatement.getResultMaps(); 42 | 43 | if (resultMaps != null && !resultMaps.isEmpty()) { 44 | ResultMap resultMap = resultMaps.get(0); 45 | 46 | if (resultMap.getResultMappings() == null || resultMap.getResultMappings().isEmpty()) { 47 | if (resultMap.getType().isAnnotationPresent(Entity.class) || resultMap.getType() 48 | .isAnnotationPresent(Table.class)) { 49 | 50 | ResultMapParser parser = ResultMapParserHolder 51 | .getInstance(mappedStatement.getConfiguration()); 52 | ResultMap newResultMap = parser 53 | .reloadResultMap(mappedStatement.getResource(), resultMap.getId(), 54 | resultMap.getType()); 55 | 56 | List newResultMaps = new ArrayList<>(); 57 | newResultMaps.add(newResultMap); 58 | 59 | FieldReflectUtil.setFieldValue(mappedStatement, "resultMaps", newResultMaps); 60 | 61 | // modify the resultMaps 62 | FieldReflectUtil.setFieldValue(resultSetHandler, "mappedStatement", mappedStatement); 63 | } 64 | } 65 | } 66 | 67 | // return resultSetHandler.handleResultSets(stmt); 68 | } 69 | return invocation.proceed(); 70 | } 71 | 72 | @Override 73 | public Object plugin(Object target) { 74 | return Plugin.wrap(target, this); 75 | } 76 | 77 | @Override 78 | public void setProperties(Properties properties) { 79 | 80 | } 81 | 82 | /** 83 | * Use static inner classes ensure thread safety 84 | */ 85 | private static class ResultMapParserHolder { 86 | 87 | private static Map parsers = new HashMap<>(); 88 | 89 | static ResultMapParser getInstance(Configuration configuration) { 90 | String id = configuration.getEnvironment().getId(); 91 | if (!parsers.containsKey(id)) { 92 | parsers.put(id, new ResultMapParser(configuration)); 93 | } 94 | return parsers.get(id); 95 | } 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/statement/AbstractStatementFactory.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.statement; 2 | 3 | import java.lang.reflect.Method; 4 | import java.lang.reflect.ParameterizedType; 5 | import java.lang.reflect.Type; 6 | import java.util.Collection; 7 | import sun.reflect.generics.reflectiveObjects.TypeVariableImpl; 8 | 9 | /** 10 | * @author sway.li 11 | */ 12 | public abstract class AbstractStatementFactory implements StatementFactory { 13 | 14 | protected String recognizeResource(String targetClassName) { 15 | return targetClassName.replace(".", "/") + ".java (best guess)"; 16 | } 17 | 18 | protected Class recognizeEntityType(Method method, Class targetClass) { 19 | Type[] genericTypes = method.getGenericParameterTypes(); 20 | Type genericType = genericTypes[0]; 21 | 22 | if (genericType instanceof TypeVariableImpl) { 23 | // interface XXXMapper extends IBaseMapper 24 | Type[] interfaces = targetClass.getGenericInterfaces(); 25 | for (Type type : interfaces) { 26 | if (type instanceof ParameterizedType) { 27 | ParameterizedType pt = (ParameterizedType) type; 28 | return (Class) pt.getActualTypeArguments()[0]; 29 | } 30 | } 31 | } 32 | 33 | if (genericType instanceof ParameterizedType) { 34 | ParameterizedType pt = (ParameterizedType) genericType; 35 | if (pt.getActualTypeArguments()[0] instanceof TypeVariableImpl) { 36 | // interface XXXMapper extends IBaseMapper 37 | // insert(List list) 38 | Type[] interfaces = targetClass.getGenericInterfaces(); 39 | for (Type type : interfaces) { 40 | if (type instanceof ParameterizedType) { 41 | ParameterizedType pt2 = (ParameterizedType) type; 42 | return (Class) pt2.getActualTypeArguments()[0]; 43 | } 44 | } 45 | } 46 | } 47 | 48 | Class actualType; 49 | if (genericType instanceof ParameterizedType) { 50 | ParameterizedType pt = (ParameterizedType) genericType; 51 | actualType = (Class) pt.getActualTypeArguments()[0]; 52 | } else { 53 | actualType = (Class) genericType; 54 | } 55 | return actualType; 56 | } 57 | 58 | protected boolean isCollectionParameter(Method method) { 59 | Class[] parameterTypes = method.getParameterTypes(); 60 | if (parameterTypes == null || parameterTypes.length == 0) { 61 | return false; 62 | } 63 | Class clazz = parameterTypes[0]; 64 | return Collection.class.isAssignableFrom(clazz); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/statement/AnnotationStatementRegistry.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.statement; 2 | 3 | import com.littlenb.mybatisjpa.annotation.InsertDefinition; 4 | import com.littlenb.mybatisjpa.annotation.UpdateDefinition; 5 | import java.lang.annotation.Annotation; 6 | import java.util.Collections; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | /** 11 | * @author sway.li 12 | */ 13 | public class AnnotationStatementRegistry { 14 | 15 | private Map, StatementFactory> REGISTERS = new HashMap<>(); 16 | 17 | public static AnnotationStatementRegistry getDefaultRegistry() { 18 | AnnotationStatementRegistry registry = new AnnotationStatementRegistry(); 19 | registry.register(InsertDefinition.class, InsertStatementFactory.INSTANCE); 20 | registry.register(UpdateDefinition.class, UpdateStatementFactory.INSTANCE); 21 | return registry; 22 | } 23 | 24 | public void register(Class annotationType, StatementFactory factory) { 25 | if (annotationType == null) { 26 | throw new IllegalArgumentException("The parameter 'annotationType' cannot be null."); 27 | } 28 | if (factory == null) { 29 | throw new IllegalArgumentException("The parameter 'factory' cannot be null."); 30 | } 31 | REGISTERS.put(annotationType, factory); 32 | } 33 | 34 | public Map, StatementFactory> getRegisters() { 35 | return Collections.unmodifiableMap(REGISTERS); 36 | } 37 | 38 | public StatementFactory findFactory(Class annotationType) { 39 | if (annotationType == null) { 40 | return null; 41 | } 42 | 43 | return REGISTERS.get(annotationType); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/statement/InsertStatementFactory.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.statement; 2 | 3 | import com.littlenb.mybatisjpa.annotation.InsertDefinition; 4 | import com.littlenb.mybatisjpa.support.Constant; 5 | import com.littlenb.mybatisjpa.support.template.InsertBatchSqlTemplate; 6 | import com.littlenb.mybatisjpa.support.template.InsertCertainSqlTemplate; 7 | import com.littlenb.mybatisjpa.support.template.InsertIgnoreNullSqlTemplate; 8 | import com.littlenb.mybatisjpa.support.template.InsertSqlTemplate; 9 | import com.littlenb.mybatisjpa.type.SelectorStrategy; 10 | import com.littlenb.mybatisjpa.util.FieldReflectUtil; 11 | import com.littlenb.mybatisjpa.util.PersistentUtil; 12 | import java.lang.reflect.Field; 13 | import java.lang.reflect.Method; 14 | import java.util.List; 15 | import javax.persistence.GeneratedValue; 16 | import javax.persistence.GenerationType; 17 | import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator; 18 | import org.apache.ibatis.executor.keygen.KeyGenerator; 19 | import org.apache.ibatis.mapping.MappedStatement; 20 | import org.apache.ibatis.mapping.SqlCommandType; 21 | import org.apache.ibatis.mapping.SqlSource; 22 | import org.apache.ibatis.mapping.StatementType; 23 | import org.apache.ibatis.scripting.LanguageDriver; 24 | import org.apache.ibatis.session.Configuration; 25 | 26 | /** 27 | * @author sway.li 28 | */ 29 | public class InsertStatementFactory extends AbstractStatementFactory { 30 | 31 | public static final StatementFactory INSTANCE = new InsertStatementFactory(); 32 | 33 | @Override 34 | public MappedStatement parseStatement(Configuration configuration, Method method, 35 | Class targetClass) { 36 | String resourceName = targetClass.toString(); 37 | 38 | if (!configuration.isResourceLoaded(resourceName)) { 39 | configuration.addLoadedResource(resourceName); 40 | } 41 | String targetClassName = targetClass.getName(); 42 | Class type = super.recognizeEntityType(method, targetClass); 43 | LanguageDriver languageDriver = Constant.XML_LANGUAGE_DRIVER; 44 | SqlSource sqlSource = languageDriver 45 | .createSqlSource(configuration, "", 46 | Object.class); 47 | String statementId = targetClassName + "." + method.getName(); 48 | MappedStatement.Builder builder = new MappedStatement.Builder(configuration, statementId, 49 | sqlSource, SqlCommandType.INSERT); 50 | builder.resource(super.recognizeResource(targetClassName)).lang(languageDriver) 51 | .statementType(StatementType.PREPARED); 52 | 53 | // keyGenerator 54 | List fields = FieldReflectUtil.findFields(type, GeneratedValue.class); 55 | for (Field generatedField : fields) { 56 | String keyProperty = generatedField.getName(); 57 | if (SelectorStrategy.CERTAIN.equals(recognizeStrategy(method))) { 58 | keyProperty = Constant.CERTAINTY_ENTITY_KEY + "." + keyProperty; 59 | } 60 | if (generatedField.isAnnotationPresent(GeneratedValue.class)) { 61 | GeneratedValue generatedValue = generatedField.getAnnotation(GeneratedValue.class); 62 | if (GenerationType.AUTO.equals(generatedValue.strategy())) { 63 | builder.keyGenerator(new Jdbc3KeyGenerator()).keyProperty(keyProperty) 64 | .keyColumn(PersistentUtil.getColumnName(generatedField)); 65 | } else if (GenerationType.IDENTITY.equals(generatedValue.strategy())) { 66 | String generator = "".equals(generatedValue.generator()) ? Constant.DEFAULT_KEY_GENERATOR 67 | : generatedValue.generator(); 68 | KeyGenerator keyGenerator = configuration.getKeyGenerator(generator); 69 | if (keyGenerator == null) { 70 | throw new IllegalArgumentException( 71 | "mybatisjpa init failure , can not find '" + generator + "' in configuration."); 72 | } 73 | builder.keyGenerator(keyGenerator).keyProperty(keyProperty) 74 | .keyColumn(PersistentUtil.getColumnName(generatedField)); 75 | } 76 | } 77 | } 78 | 79 | return builder.build(); 80 | } 81 | 82 | private String parseSQL(Method method, Class type) { 83 | SelectorStrategy strategy = recognizeStrategy(method); 84 | if (SelectorStrategy.IGNORE_NULL.equals(strategy)) { 85 | return InsertIgnoreNullSqlTemplate.INSTANCE.parseSQL(type); 86 | } 87 | 88 | if (SelectorStrategy.CERTAIN.equals(strategy)) { 89 | return InsertCertainSqlTemplate.INSTANCE.parseSQL(type); 90 | } 91 | 92 | if (isCollectionParameter(method)) { 93 | return InsertBatchSqlTemplate.INSTANCE.parseSQL(type); 94 | } 95 | 96 | return InsertSqlTemplate.INSTANCE.parseSQL(type); 97 | } 98 | 99 | private SelectorStrategy recognizeStrategy(Method method) { 100 | InsertDefinition insertDefinition = method.getAnnotation(InsertDefinition.class); 101 | if (insertDefinition == null) { 102 | throw new RuntimeException( 103 | "'" + method.getName() + "' method is not annotation with 'InsertDefinition'."); 104 | } 105 | return insertDefinition.strategy(); 106 | } 107 | 108 | } -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/statement/StatementFactory.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.statement; 2 | 3 | import java.lang.reflect.Method; 4 | import org.apache.ibatis.mapping.MappedStatement; 5 | import org.apache.ibatis.session.Configuration; 6 | 7 | /** 8 | * @author sway.li 9 | **/ 10 | public interface StatementFactory { 11 | 12 | MappedStatement parseStatement(Configuration configuration, Method method, Class targetClass); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/statement/UpdateStatementFactory.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.statement; 2 | 3 | import com.littlenb.mybatisjpa.annotation.UpdateDefinition; 4 | import com.littlenb.mybatisjpa.support.Constant; 5 | import com.littlenb.mybatisjpa.support.template.UpdateCertainSqlTemplate; 6 | import com.littlenb.mybatisjpa.support.template.UpdateIgnoreNullSqlTemplate; 7 | import com.littlenb.mybatisjpa.support.template.UpdateSqlTemplate; 8 | import com.littlenb.mybatisjpa.type.SelectorStrategy; 9 | import java.lang.reflect.Method; 10 | import org.apache.ibatis.mapping.MappedStatement; 11 | import org.apache.ibatis.mapping.SqlCommandType; 12 | import org.apache.ibatis.mapping.SqlSource; 13 | import org.apache.ibatis.mapping.StatementType; 14 | import org.apache.ibatis.scripting.LanguageDriver; 15 | import org.apache.ibatis.session.Configuration; 16 | 17 | /** 18 | * @author sway.li 19 | */ 20 | public class UpdateStatementFactory extends AbstractStatementFactory { 21 | 22 | public static final StatementFactory INSTANCE = new UpdateStatementFactory(); 23 | 24 | @Override 25 | public MappedStatement parseStatement(Configuration configuration, Method method, 26 | Class targetClass) { 27 | String resourceName = targetClass.toString(); 28 | 29 | if (!configuration.isResourceLoaded(resourceName)) { 30 | configuration.addLoadedResource(resourceName); 31 | } 32 | String targetClassName = targetClass.getName(); 33 | Class type = super.recognizeEntityType(method, targetClass); 34 | LanguageDriver languageDriver = Constant.XML_LANGUAGE_DRIVER; 35 | SqlSource sqlSource = languageDriver 36 | .createSqlSource(configuration, "", 37 | Object.class); 38 | String statementId = targetClassName + "." + method.getName(); 39 | MappedStatement.Builder builder = new MappedStatement.Builder(configuration, statementId, 40 | sqlSource, SqlCommandType.UPDATE); 41 | builder.resource(super.recognizeResource(targetClassName)).lang(languageDriver) 42 | .statementType(StatementType.PREPARED); 43 | 44 | return builder.build(); 45 | } 46 | 47 | private String parseSQL(Method method, Class type) { 48 | UpdateDefinition updateDefinition = method.getAnnotation(UpdateDefinition.class); 49 | if (updateDefinition == null) { 50 | throw new RuntimeException( 51 | "'" + method.getName() + "' method is not annotation with 'UpdateDefinition'."); 52 | } 53 | SelectorStrategy strategy = updateDefinition.strategy(); 54 | 55 | String sql = ""; 56 | 57 | if (SelectorStrategy.NONE.equals(strategy)) { 58 | sql = UpdateSqlTemplate.INSTANCE.parseSQL(type); 59 | } 60 | 61 | if (SelectorStrategy.IGNORE_NULL.equals(strategy)) { 62 | sql = UpdateIgnoreNullSqlTemplate.INSTANCE.parseSQL(type); 63 | } 64 | 65 | if (SelectorStrategy.CERTAIN.equals(strategy)) { 66 | sql = UpdateCertainSqlTemplate.INSTANCE.parseSQL(type); 67 | } 68 | 69 | if (!"".equals(updateDefinition.where())) { 70 | sql = sql + " WHERE " + updateDefinition.where(); 71 | } 72 | 73 | return sql; 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/support/AnnotationStatementScanner.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.support; 2 | 3 | import com.littlenb.mybatisjpa.statement.AnnotationStatementRegistry; 4 | import com.littlenb.mybatisjpa.statement.StatementFactory; 5 | import java.lang.annotation.Annotation; 6 | import java.lang.reflect.Method; 7 | import java.util.Collection; 8 | import java.util.Iterator; 9 | import java.util.Set; 10 | import org.apache.ibatis.annotations.Mapper; 11 | import org.apache.ibatis.builder.IncompleteElementException; 12 | import org.apache.ibatis.builder.annotation.MethodResolver; 13 | import org.apache.ibatis.mapping.MappedStatement; 14 | import org.apache.ibatis.session.Configuration; 15 | import org.reflections.Reflections; 16 | import org.reflections.scanners.MethodAnnotationsScanner; 17 | import org.reflections.scanners.SubTypesScanner; 18 | import org.reflections.scanners.TypeAnnotationsScanner; 19 | 20 | /** 21 | * @author sway.li 22 | **/ 23 | public class AnnotationStatementScanner { 24 | 25 | private Configuration configuration; 26 | 27 | private String[] basePackages; 28 | 29 | private AnnotationStatementRegistry annotationStatementRegistry; 30 | 31 | public void scan() { 32 | for (String basePackage : basePackages) { 33 | Reflections reflections = new Reflections(basePackage, new TypeAnnotationsScanner(), 34 | new SubTypesScanner(), new MethodAnnotationsScanner()); 35 | Set> mappers = reflections.getTypesAnnotatedWith(Mapper.class); 36 | for (Class mapperClass : mappers) { 37 | Method[] methods = mapperClass.getMethods(); 38 | for (Method method : methods) { 39 | Annotation[] annotations = method.getDeclaredAnnotations(); 40 | for (Annotation annotation : annotations) { 41 | StatementFactory statementFactory = annotationStatementRegistry 42 | .findFactory(annotation.annotationType()); 43 | if (statementFactory != null) { 44 | MappedStatement statement = statementFactory.parseStatement(configuration, method, mapperClass); 45 | configuration.addMappedStatement(statement); 46 | } 47 | } 48 | 49 | } 50 | } 51 | } 52 | parsePendingMethods(); 53 | } 54 | 55 | private void parsePendingMethods() { 56 | Collection incompleteMethods = configuration.getIncompleteMethods(); 57 | synchronized (incompleteMethods) { 58 | Iterator iter = incompleteMethods.iterator(); 59 | while (iter.hasNext()) { 60 | try { 61 | iter.next().resolve(); 62 | iter.remove(); 63 | } catch (IncompleteElementException e) { 64 | // This method is still missing a resource 65 | } 66 | } 67 | } 68 | } 69 | 70 | public static class Builder { 71 | 72 | private AnnotationStatementScanner instance = new AnnotationStatementScanner(); 73 | 74 | public Builder() { 75 | } 76 | 77 | public Builder configuration(Configuration configuration) { 78 | this.instance.configuration = configuration; 79 | return this; 80 | } 81 | 82 | public Builder basePackages(String[] basePackages) { 83 | this.instance.basePackages = basePackages; 84 | return this; 85 | } 86 | 87 | public Builder annotationStatementRegistry(AnnotationStatementRegistry annotationStatementRegistry) { 88 | this.instance.annotationStatementRegistry = annotationStatementRegistry; 89 | return this; 90 | } 91 | 92 | public AnnotationStatementScanner build() { 93 | return this.instance; 94 | } 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/support/Certainty.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.support; 2 | 3 | import java.util.Collections; 4 | import java.util.Set; 5 | 6 | /** 7 | * @author sway.li 8 | */ 9 | public class Certainty { 10 | 11 | private E entity; 12 | 13 | private Set includes; 14 | 15 | private Set excludes; 16 | 17 | public Certainty() { 18 | } 19 | 20 | public static Certainty includes(T entity, Set fields) { 21 | Certainty certainty = new Certainty<>(); 22 | certainty.entity = entity; 23 | certainty.includes = fields; 24 | certainty.excludes = Collections.emptySet(); 25 | return certainty; 26 | } 27 | 28 | public static Certainty excludes(T entity, Set fields) { 29 | Certainty certainty = new Certainty<>(); 30 | certainty.entity = entity; 31 | certainty.includes = Collections.emptySet(); 32 | certainty.excludes = fields; 33 | return certainty; 34 | } 35 | 36 | // getter 37 | 38 | public E getEntity() { 39 | return entity; 40 | } 41 | 42 | public Set getIncludes() { 43 | return includes; 44 | } 45 | 46 | public Set getExcludes() { 47 | return excludes; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/support/Constant.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.support; 2 | 3 | import org.apache.ibatis.scripting.LanguageDriver; 4 | import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver; 5 | 6 | /** 7 | * @author sway.li 8 | */ 9 | public interface Constant { 10 | 11 | LanguageDriver XML_LANGUAGE_DRIVER = new XMLLanguageDriver(); 12 | 13 | String DEFAULT_KEY_GENERATOR = "defaultKeyGenerator"; 14 | 15 | String CERTAINTY_ENTITY_KEY = "entity"; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/support/MybatisJapConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.support; 2 | 3 | import com.littlenb.mybatisjpa.util.NamingPolicy; 4 | import com.littlenb.mybatisjpa.util.NamingStrategy; 5 | 6 | /** 7 | * @author sway.li 8 | */ 9 | public class MybatisJapConfiguration { 10 | 11 | private NamingStrategy tableNamingStrategy = NamingPolicy.IDENTITY; 12 | 13 | private NamingStrategy columnNamingStrategy = NamingPolicy.IDENTITY; 14 | 15 | private MybatisJapConfiguration(){ 16 | } 17 | 18 | public static MybatisJapConfiguration getInstance(){ 19 | return Holder.instance; 20 | } 21 | 22 | private static class Holder{ 23 | private static MybatisJapConfiguration instance = new MybatisJapConfiguration(); 24 | } 25 | 26 | public NamingStrategy getTableNamingStrategy() { 27 | return tableNamingStrategy; 28 | } 29 | 30 | public void setTableNamingStrategy(NamingStrategy tableNamingStrategy) { 31 | this.tableNamingStrategy = tableNamingStrategy; 32 | } 33 | 34 | public NamingStrategy getColumnNamingStrategy() { 35 | return columnNamingStrategy; 36 | } 37 | 38 | public void setColumnNamingStrategy(NamingStrategy columnNamingStrategy) { 39 | this.columnNamingStrategy = columnNamingStrategy; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/support/template/InsertBatchSqlTemplate.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.support.template; 2 | 3 | import com.littlenb.mybatisjpa.util.ColumnMetaResolver; 4 | import com.littlenb.mybatisjpa.util.PersistentUtil; 5 | import java.lang.reflect.Field; 6 | 7 | /** 8 | * @author sway.li 9 | **/ 10 | public class InsertBatchSqlTemplate implements SqlTemplate { 11 | 12 | public static final SqlTemplate INSTANCE = new InsertBatchSqlTemplate(); 13 | 14 | @Override 15 | public String parseSQL(final Class type) { 16 | // columns 17 | StringBuilder columns = new StringBuilder(); 18 | columns.append(" "); 19 | // values 20 | StringBuilder values = new StringBuilder(); 21 | values.append(" "); 22 | 23 | for (Field field : PersistentUtil.getPersistentFields(type)) { 24 | if (!PersistentUtil.insertable(field)) { 25 | continue; 26 | } 27 | // columns 28 | columns.append(PersistentUtil.getColumnName(field)).append(", "); 29 | // values 30 | values.append(ColumnMetaResolver.resolveSqlPlaceholder(field, "data")).append(", "); 31 | } 32 | 33 | columns.append(" "); 34 | values.append(" "); 35 | 36 | return "INSERT INTO " + PersistentUtil.getTableName(type) + columns.toString() + " VALUES " 37 | + "" + values.toString() 38 | + ""; 39 | } 40 | } -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/support/template/InsertCertainSqlTemplate.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.support.template; 2 | 3 | import com.littlenb.mybatisjpa.support.Constant; 4 | import com.littlenb.mybatisjpa.util.ColumnMetaResolver; 5 | import com.littlenb.mybatisjpa.util.PersistentUtil; 6 | import java.lang.reflect.Field; 7 | 8 | /** 9 | * @author sway.li 10 | **/ 11 | public class InsertCertainSqlTemplate implements SqlTemplate { 12 | 13 | public static final SqlTemplate INSTANCE = new InsertCertainSqlTemplate(); 14 | 15 | @Override 16 | public String parseSQL(Class type) { 17 | // columns 18 | StringBuilder columns = new StringBuilder(); 19 | columns.append(" "); 20 | // values 21 | StringBuilder values = new StringBuilder(); 22 | values.append(" "); 23 | 24 | for (Field field : PersistentUtil.getPersistentFields(type)) { 25 | if (!PersistentUtil.insertable(field)) { 26 | continue; 27 | } 28 | // columns 29 | columns.append(String.format( 30 | " ", 31 | field.getName() 32 | )); 33 | columns.append(PersistentUtil.getColumnName(field)).append(", "); 34 | columns.append(" "); 35 | // values 36 | values.append(String.format( 37 | " ", 38 | field.getName() 39 | )); 40 | values.append( 41 | ColumnMetaResolver.resolveSqlPlaceholder(field, Constant.CERTAINTY_ENTITY_KEY)) 42 | .append(", "); 43 | values.append(" "); 44 | } 45 | 46 | columns.append(" "); 47 | values.append(" "); 48 | 49 | return "INSERT INTO " + PersistentUtil.getTableName(type) + columns.toString() + " VALUES " 50 | + values.toString(); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/support/template/InsertIgnoreNullSqlTemplate.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.support.template; 2 | 3 | import com.littlenb.mybatisjpa.util.ColumnMetaResolver; 4 | import com.littlenb.mybatisjpa.util.PersistentUtil; 5 | import java.lang.reflect.Field; 6 | 7 | /** 8 | * @author sway.li 9 | **/ 10 | public class InsertIgnoreNullSqlTemplate implements SqlTemplate { 11 | 12 | public static final SqlTemplate INSTANCE = new InsertIgnoreNullSqlTemplate(); 13 | 14 | @Override 15 | public String parseSQL(Class type) { 16 | // columns 17 | StringBuilder columns = new StringBuilder(); 18 | columns.append(" "); 19 | // values 20 | StringBuilder values = new StringBuilder(); 21 | values.append(" "); 22 | 23 | for (Field field : PersistentUtil.getPersistentFields(type)) { 24 | // columns 25 | columns.append(" "); 26 | columns.append(PersistentUtil.getColumnName(field) + ", "); 27 | columns.append(" "); 28 | // values 29 | values.append(" "); 30 | values.append(ColumnMetaResolver.resolveSqlPlaceholder(field) + ", "); 31 | values.append(" "); 32 | } 33 | 34 | columns.append(" "); 35 | values.append(" "); 36 | 37 | return "INSERT INTO " + PersistentUtil.getTableName(type) + columns.toString() + " VALUES " 38 | + values.toString(); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/support/template/InsertSqlTemplate.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.support.template; 2 | 3 | import com.littlenb.mybatisjpa.util.ColumnMetaResolver; 4 | import com.littlenb.mybatisjpa.util.PersistentUtil; 5 | import java.lang.reflect.Field; 6 | import org.apache.ibatis.jdbc.SQL; 7 | 8 | /** 9 | * @author sway.li 10 | **/ 11 | public class InsertSqlTemplate implements SqlTemplate { 12 | 13 | public static final SqlTemplate INSTANCE = new InsertSqlTemplate(); 14 | 15 | @Override 16 | public String parseSQL(final Class type) { 17 | return new SQL() { 18 | { 19 | INSERT_INTO(PersistentUtil.getTableName(type)); 20 | for (Field field : PersistentUtil.getPersistentFields(type)) { 21 | if (PersistentUtil.insertable(field)) { 22 | VALUES(PersistentUtil.getColumnName(field), 23 | ColumnMetaResolver.resolveSqlPlaceholder(field)); 24 | } 25 | } 26 | } 27 | }.toString(); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/support/template/SqlTemplate.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.support.template; 2 | 3 | /** 4 | * @author sway.li 5 | */ 6 | public interface SqlTemplate { 7 | 8 | String parseSQL(final Class type); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/support/template/UpdateCertainSqlTemplate.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.support.template; 2 | 3 | import com.littlenb.mybatisjpa.support.Constant; 4 | import com.littlenb.mybatisjpa.util.ColumnMetaResolver; 5 | import com.littlenb.mybatisjpa.util.PersistentUtil; 6 | import java.lang.reflect.Field; 7 | 8 | /** 9 | * @author sway.li 10 | **/ 11 | public class UpdateCertainSqlTemplate implements SqlTemplate { 12 | 13 | public static final SqlTemplate INSTANCE = new UpdateCertainSqlTemplate(); 14 | 15 | @Override 16 | public String parseSQL(Class type) { 17 | // columns 18 | StringBuilder sets = new StringBuilder(); 19 | sets.append(" "); 20 | for (Field field : PersistentUtil.getPersistentFields(type)) { 21 | sets.append(String.format( 22 | " ", 23 | field.getName() 24 | )); 25 | // columnName = #{ } 26 | 27 | sets.append(PersistentUtil.getColumnName(field)).append(" = ") 28 | .append(ColumnMetaResolver.resolveSqlPlaceholder(field, Constant.CERTAINTY_ENTITY_KEY)) 29 | .append(", "); 30 | sets.append(" "); 31 | } 32 | 33 | sets.append(" "); 34 | 35 | return "UPDATE " + PersistentUtil.getTableName(type) + " SET " + sets.toString(); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/support/template/UpdateIgnoreNullSqlTemplate.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.support.template; 2 | 3 | import com.littlenb.mybatisjpa.util.ColumnMetaResolver; 4 | import com.littlenb.mybatisjpa.util.PersistentUtil; 5 | import java.lang.reflect.Field; 6 | 7 | /** 8 | * @author sway.li 9 | **/ 10 | public class UpdateIgnoreNullSqlTemplate implements SqlTemplate { 11 | 12 | public static final SqlTemplate INSTANCE = new UpdateIgnoreNullSqlTemplate(); 13 | 14 | @Override 15 | public String parseSQL(Class type) { 16 | // columns 17 | StringBuilder sets = new StringBuilder(); 18 | sets.append(" "); 19 | for (Field field : PersistentUtil.getPersistentFields(type)) { 20 | 21 | sets.append(" "); 22 | // columnName = #{ } 23 | 24 | sets.append(PersistentUtil.getColumnName(field)).append(" = ") 25 | .append(ColumnMetaResolver.resolveSqlPlaceholder(field)).append(" , "); 26 | sets.append(" "); 27 | } 28 | 29 | sets.append(" "); 30 | 31 | return "UPDATE " + PersistentUtil.getTableName(type) + " set " + sets.toString(); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/support/template/UpdateSqlTemplate.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.support.template; 2 | 3 | import com.littlenb.mybatisjpa.util.ColumnMetaResolver; 4 | import com.littlenb.mybatisjpa.util.PersistentUtil; 5 | import java.lang.reflect.Field; 6 | import org.apache.ibatis.jdbc.SQL; 7 | 8 | /** 9 | * @author sway.li 10 | **/ 11 | public class UpdateSqlTemplate implements SqlTemplate { 12 | 13 | public static final SqlTemplate INSTANCE = new UpdateSqlTemplate(); 14 | 15 | @Override 16 | public String parseSQL(final Class type) { 17 | return new SQL() { 18 | { 19 | UPDATE(PersistentUtil.getTableName(type)); 20 | for (Field field : PersistentUtil.getPersistentFields(type)) { 21 | if (PersistentUtil.updatable(field)) { 22 | SET(PersistentUtil.getColumnName(field) + " = " + ColumnMetaResolver 23 | .resolveSqlPlaceholder(field)); 24 | } 25 | } 26 | } 27 | }.toString(); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/type/CodeEnumAdapter.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.type; 2 | 3 | import java.util.Objects; 4 | 5 | /** 6 | * ICodeEnum适配器 7 | * 8 | * @author sway.li 9 | * @see ICodeEnum 10 | **/ 11 | public class CodeEnumAdapter { 12 | 13 | /** 14 | * adapt to {@link ICodeEnum} with code. 15 | * 16 | * @param the type which implements {@link ICodeEnum} and extends {@link Enum} 17 | */ 18 | public static T toCodeEnum(Object code, Class type) { 19 | 20 | if (!type.isEnum()) { 21 | throw new IllegalArgumentException( 22 | String.format("type must be Enum,type : %s", type.getName())); 23 | } 24 | 25 | if (!ICodeEnum.class.isAssignableFrom(type)) { 26 | throw new IllegalArgumentException( 27 | String.format("type must be sub class by ICodeEnum,type : %s", type.getName())); 28 | } 29 | 30 | for (T t : type.getEnumConstants()) { 31 | 32 | ICodeEnum codeEnum = (ICodeEnum) t; 33 | 34 | if (Objects.equals(codeEnum.getCode(), code)) { 35 | return t; 36 | } 37 | 38 | } 39 | 40 | throw new IllegalArgumentException( 41 | "Cannot convert " + code + " to " + type.getSimpleName() + " by code."); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/type/CodeType.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.type; 2 | 3 | /** 4 | * @author sway.li 5 | **/ 6 | public enum CodeType { 7 | 8 | INT, STRING 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/type/ICodeEnum.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.type; 2 | 3 | 4 | /** 5 | * 枚举类型扩展 6 | * 7 | * @param Integer or String 8 | * @author sway.li 9 | */ 10 | public interface ICodeEnum { 11 | 12 | T getCode(); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/type/IntCodeEnumTypeHandler.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.type; 2 | 3 | import java.sql.CallableStatement; 4 | import java.sql.PreparedStatement; 5 | import java.sql.ResultSet; 6 | import java.sql.SQLException; 7 | import org.apache.ibatis.type.BaseTypeHandler; 8 | import org.apache.ibatis.type.JdbcType; 9 | import org.apache.ibatis.type.MappedJdbcTypes; 10 | import org.apache.ibatis.type.MappedTypes; 11 | 12 | /** 13 | * @author sway.li 14 | **/ 15 | @MappedJdbcTypes({JdbcType.INTEGER, JdbcType.TINYINT}) 16 | @MappedTypes({ICodeEnum.class}) 17 | public class IntCodeEnumTypeHandler extends BaseTypeHandler { 18 | 19 | private Class type; 20 | 21 | public IntCodeEnumTypeHandler(Class type) { 22 | if (type == null) { 23 | throw new IllegalArgumentException("Type argument cannot be null"); 24 | } 25 | this.type = type; 26 | } 27 | 28 | @Override 29 | public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) 30 | throws SQLException { 31 | if (jdbcType == null) { 32 | ps.setInt(i, (Integer) parameter.getCode()); 33 | } else { 34 | ps.setObject(i, parameter.getCode(), jdbcType.TYPE_CODE); 35 | } 36 | } 37 | 38 | @Override 39 | public E getNullableResult(ResultSet rs, String columnName) throws SQLException { 40 | int i = rs.getInt(columnName); 41 | if (rs.wasNull()) { 42 | return null; 43 | } else { 44 | return CodeEnumAdapter.toCodeEnum(i, type); 45 | } 46 | } 47 | 48 | @Override 49 | public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException { 50 | int i = rs.getInt(columnIndex); 51 | if (rs.wasNull()) { 52 | return null; 53 | } else { 54 | return CodeEnumAdapter.toCodeEnum(i, type); 55 | } 56 | } 57 | 58 | @Override 59 | public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { 60 | int i = cs.getInt(columnIndex); 61 | if (cs.wasNull()) { 62 | return null; 63 | } else { 64 | return CodeEnumAdapter.toCodeEnum(i, type); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/type/SelectorStrategy.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.type; 2 | 3 | /** 4 | * @author sway.li 5 | */ 6 | public enum SelectorStrategy { 7 | 8 | /** 9 | * none 10 | */ 11 | NONE, 12 | 13 | /** 14 | * ignore null 15 | */ 16 | IGNORE_NULL, 17 | 18 | /** 19 | * @see com.littlenb.mybatisjpa.support.Certainty 20 | */ 21 | CERTAIN 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/type/StringCodeEnumTypeHandler.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.type; 2 | 3 | import java.sql.CallableStatement; 4 | import java.sql.PreparedStatement; 5 | import java.sql.ResultSet; 6 | import java.sql.SQLException; 7 | import org.apache.ibatis.type.BaseTypeHandler; 8 | import org.apache.ibatis.type.JdbcType; 9 | import org.apache.ibatis.type.MappedJdbcTypes; 10 | import org.apache.ibatis.type.MappedTypes; 11 | 12 | /** 13 | * @author sway.li 14 | **/ 15 | @MappedJdbcTypes({JdbcType.VARCHAR}) 16 | @MappedTypes({ICodeEnum.class}) 17 | public class StringCodeEnumTypeHandler extends BaseTypeHandler { 18 | 19 | private Class type; 20 | 21 | public StringCodeEnumTypeHandler(Class type) { 22 | if (type == null) { 23 | throw new IllegalArgumentException("Type argument cannot be null"); 24 | } 25 | this.type = type; 26 | } 27 | 28 | @Override 29 | public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) 30 | throws SQLException { 31 | if (jdbcType == null) { 32 | ps.setString(i, (String) parameter.getCode()); 33 | } else { 34 | ps.setObject(i, parameter.getCode(), jdbcType.TYPE_CODE); 35 | } 36 | } 37 | 38 | @Override 39 | public E getNullableResult(ResultSet rs, String columnName) throws SQLException { 40 | String s = rs.getString(columnName); 41 | return s == null ? null : CodeEnumAdapter.toCodeEnum(s, type); 42 | } 43 | 44 | @Override 45 | public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException { 46 | String s = rs.getString(columnIndex); 47 | return s == null ? null : CodeEnumAdapter.toCodeEnum(s, type); 48 | } 49 | 50 | @Override 51 | public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { 52 | String s = cs.getString(columnIndex); 53 | return s == null ? null : CodeEnumAdapter.toCodeEnum(s, type); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/util/ColumnMetaResolver.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.util; 2 | 3 | import com.littlenb.mybatisjpa.annotation.CodeEnum; 4 | import com.littlenb.mybatisjpa.annotation.MappedJdbcType; 5 | import com.littlenb.mybatisjpa.type.CodeType; 6 | import com.littlenb.mybatisjpa.type.IntCodeEnumTypeHandler; 7 | import com.littlenb.mybatisjpa.type.StringCodeEnumTypeHandler; 8 | import java.lang.reflect.Field; 9 | import java.util.Objects; 10 | import javax.persistence.EnumType; 11 | import javax.persistence.Enumerated; 12 | import org.apache.ibatis.builder.BuilderException; 13 | import org.apache.ibatis.type.BooleanTypeHandler; 14 | import org.apache.ibatis.type.EnumOrdinalTypeHandler; 15 | import org.apache.ibatis.type.EnumTypeHandler; 16 | import org.apache.ibatis.type.JdbcType; 17 | import org.apache.ibatis.type.TypeHandler; 18 | 19 | /** 20 | * @author sway.li 21 | **/ 22 | public class ColumnMetaResolver { 23 | 24 | public static JdbcType resolveJdbcType(String alias) { 25 | if (alias == null) { 26 | return null; 27 | } 28 | try { 29 | return JdbcType.valueOf(alias); 30 | } catch (IllegalArgumentException e) { 31 | throw new BuilderException("Error resolving JdbcType. Cause: " + e, e); 32 | } 33 | } 34 | 35 | public static String resolveJdbcAlias(Field field) { 36 | 37 | if (field.isAnnotationPresent(MappedJdbcType.class)) { 38 | MappedJdbcType jdbcType = field.getAnnotation(MappedJdbcType.class); 39 | return jdbcType.value().name(); 40 | } 41 | 42 | if (field.isAnnotationPresent(CodeEnum.class)) { 43 | CodeEnum codeEnum = field.getAnnotation(CodeEnum.class); 44 | if (Objects.equals(codeEnum.value(), CodeType.INT)) { 45 | return JdbcType.INTEGER.name(); 46 | } 47 | if (Objects.equals(codeEnum.value(), CodeType.STRING)) { 48 | return JdbcType.VARCHAR.name(); 49 | } 50 | } 51 | if (field.getType().isEnum()) { 52 | if (field.isAnnotationPresent(Enumerated.class)) { 53 | Enumerated enumerated = field.getAnnotation(Enumerated.class); 54 | if (Objects.equals(enumerated.value(), EnumType.ORDINAL)) { 55 | return "INTEGER"; 56 | } 57 | } 58 | return "VARCHAR"; 59 | } 60 | 61 | Class fieldType = field.getType(); 62 | 63 | if (Integer.class.equals(fieldType)) { 64 | return "INTEGER"; 65 | } 66 | if (Double.class.equals(fieldType)) { 67 | return "DOUBLE"; 68 | } 69 | if (Float.class.equals(fieldType)) { 70 | return "FLOAT"; 71 | } 72 | if (String.class.equals(fieldType)) { 73 | return "VARCHAR"; 74 | } 75 | if (java.util.Date.class.isAssignableFrom(fieldType)) { 76 | return "TIMESTAMP"; 77 | } 78 | return null; 79 | } 80 | 81 | @SuppressWarnings({"unchecked", "rawtypes"}) 82 | public static Class> resolveTypeHandler(Field field) { 83 | 84 | if (field.isAnnotationPresent(CodeEnum.class)) { 85 | CodeEnum codeEnum = field.getAnnotation(CodeEnum.class); 86 | if (Objects.equals(codeEnum.value(), CodeType.INT)) { 87 | IntCodeEnumTypeHandler typeHandler = new IntCodeEnumTypeHandler(field.getType()); 88 | return (Class>) typeHandler.getClass(); 89 | } 90 | if (Objects.equals(codeEnum.value(), CodeType.STRING)) { 91 | StringCodeEnumTypeHandler typeHandler = new StringCodeEnumTypeHandler(field.getType()); 92 | return (Class>) typeHandler.getClass(); 93 | } 94 | } 95 | 96 | if (field.getType().isEnum()) { 97 | if (field.isAnnotationPresent(Enumerated.class)) { 98 | Enumerated enumerated = field.getAnnotation(Enumerated.class); 99 | if (enumerated.value() == EnumType.ORDINAL) { 100 | EnumOrdinalTypeHandler> typeHandler = new EnumOrdinalTypeHandler( 101 | field.getType()); 102 | return (Class>) typeHandler.getClass(); 103 | 104 | } 105 | } 106 | EnumTypeHandler> typeHandler = new EnumTypeHandler(field.getType()); 107 | return (Class>) typeHandler.getClass(); 108 | } 109 | 110 | if (field.getType().equals(Boolean.class)) { 111 | return BooleanTypeHandler.class; 112 | } 113 | return null; 114 | } 115 | 116 | /** 117 | * 装配sql中动态参数的占位符 #{parameterName,jdbcType=,typeHandler=} 118 | */ 119 | public static String resolveSqlPlaceholder(Field field) { 120 | 121 | return resolveSqlPlaceholder(field, ""); 122 | } 123 | 124 | /** 125 | * 装配sql中动态参数的占位符 #{alias.parameterName,jdbcType=,typeHandler=} 126 | */ 127 | public static String resolveSqlPlaceholder(Field field, String alias) { 128 | StringBuilder sqlParameter = new StringBuilder(); 129 | sqlParameter.append("#{"); 130 | if (alias != null && !"".equals(alias)) { 131 | sqlParameter.append(alias).append("."); 132 | } 133 | sqlParameter.append(field.getName()); 134 | 135 | // jdbcType 136 | String jdbcType = resolveJdbcAlias(field); 137 | 138 | if (jdbcType != null) { 139 | sqlParameter.append(", jdbcType=").append(jdbcType); 140 | } 141 | // typeHandler 142 | Class> typeHandler = resolveTypeHandler(field); 143 | 144 | if (typeHandler != null) { 145 | sqlParameter.append(", typeHandler=").append(typeHandler.getName()); 146 | } 147 | sqlParameter.append("} "); 148 | 149 | return sqlParameter.toString(); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/util/FieldReflectUtil.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.util; 2 | 3 | import java.lang.annotation.Annotation; 4 | import java.lang.reflect.Field; 5 | import java.lang.reflect.Modifier; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | /** 10 | * @author sway.li 11 | */ 12 | public class FieldReflectUtil { 13 | 14 | /** 15 | * Set the field value to target object. 16 | * 17 | * @param target obj 18 | * @param name field name 19 | * @throws Exception IllegalArgumentException, IllegalAccessException 20 | */ 21 | public static void setFieldValue(T target, String name, Object value) throws Exception { 22 | if (target == null) { 23 | throw new IllegalArgumentException("Target must not be null"); 24 | } 25 | 26 | Field field = findField(target.getClass(), name); 27 | 28 | makeAccessible(field); 29 | 30 | field.set(target, value); 31 | } 32 | 33 | /** 34 | * Get the field value from target object. 35 | * 36 | * @param target obj 37 | * @param name field name 38 | * @return value 39 | * @throws Exception IllegalArgumentException, IllegalAccessException 40 | */ 41 | public static Object getFieldValue(T target, String name) throws Exception { 42 | if (target == null) { 43 | throw new IllegalArgumentException("Target must not be null"); 44 | } 45 | 46 | Field field = findField(target.getClass(), name); 47 | 48 | if (field == null) { 49 | throw new IllegalArgumentException( 50 | "Can not find field " + name + " from " + target.getClass()); 51 | } 52 | 53 | makeAccessible(field); 54 | 55 | return field.get(target); 56 | } 57 | 58 | /** 59 | * Attempt to find a field on the supplied Class with the supplied {@code annotationType}. 60 | * Searches all superclasses up to Object. 61 | * 62 | * @param clazz the class to introspect 63 | * @param annotationType the annotation type of the field 64 | * @return Field or null 65 | */ 66 | public static List findFields(Class clazz, Class annotationType) { 67 | List list = new ArrayList<>(); 68 | Class searchType = clazz; 69 | while (!Object.class.equals(searchType) && searchType != null) { 70 | Field[] fields = searchType.getDeclaredFields(); 71 | for (Field field : fields) { 72 | if (field.isAnnotationPresent(annotationType)) { 73 | list.add(field); 74 | } 75 | } 76 | searchType = searchType.getSuperclass(); 77 | } 78 | return list; 79 | } 80 | 81 | /** 82 | * copy from org.springframework.util.ReflectionUtils 83 | */ 84 | public static Field findField(Class clazz, String name) { 85 | return findField(clazz, name, null); 86 | } 87 | 88 | /** 89 | * copy from org.springframework.util.ReflectionUtils 90 | */ 91 | public static Field findField(Class clazz, String name, Class type) { 92 | if (clazz == null) { 93 | throw new IllegalArgumentException("Class must not be null"); 94 | } 95 | if (name == null && type == null) { 96 | throw new IllegalArgumentException("Either name or type of the field must be specified"); 97 | } 98 | Class searchType = clazz; 99 | while (Object.class != searchType && searchType != null) { 100 | Field[] fields = searchType.getDeclaredFields(); 101 | for (Field field : fields) { 102 | if ((name == null || name.equals(field.getName())) && 103 | (type == null || type.equals(field.getType()))) { 104 | return field; 105 | } 106 | } 107 | searchType = searchType.getSuperclass(); 108 | } 109 | return null; 110 | } 111 | 112 | /** 113 | * copy from org.springframework.util.ReflectionUtils 114 | */ 115 | public static void makeAccessible(Field field) { 116 | if ((!Modifier.isPublic(field.getModifiers()) || 117 | !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || 118 | Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) { 119 | field.setAccessible(true); 120 | } 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/util/NamingPolicy.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.util; 2 | 3 | /** 4 | * @author sway.li 5 | * @since 2.5.0 6 | */ 7 | public enum NamingPolicy implements NamingStrategy { 8 | 9 | IDENTITY(){ 10 | @Override 11 | public String translate(String name) { 12 | return name; 13 | } 14 | }, 15 | LOWER_CASE_WITH_UNDERSCORES(){ 16 | @Override 17 | public String translate(String name) { 18 | StringBuilder buf = new StringBuilder(); 19 | for (int i = 0; i < name.length(); ++i) { 20 | char ch = name.charAt(i); 21 | if (ch >= 'A' && ch <= 'Z') { 22 | char ch_ucase = (char) (ch + 32); 23 | if (i > 0) { 24 | buf.append('_'); 25 | } 26 | buf.append(ch_ucase); 27 | } else { 28 | buf.append(ch); 29 | } 30 | } 31 | return buf.toString(); 32 | } 33 | }, 34 | UPPER_CASE_WITH_UNDERSCORES(){ 35 | @Override 36 | public String translate(String name) { 37 | StringBuilder buf = new StringBuilder(); 38 | for (int i = 0; i < name.length(); ++i) { 39 | char ch = name.charAt(i); 40 | if (ch >= 'a' && ch <= 'z') { 41 | char ch_ucase = (char) (ch - 32); 42 | 43 | buf.append(ch_ucase); 44 | } else if(ch >= 'A' && ch <= 'Z'){ 45 | if (i > 0) { 46 | buf.append('_'); 47 | } 48 | buf.append(ch); 49 | } else{ 50 | buf.append(ch); 51 | } 52 | } 53 | return buf.toString(); 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/util/NamingStrategy.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.util; 2 | 3 | /** 4 | * @author sway.li 5 | * @since 2.5.0 6 | */ 7 | public interface NamingStrategy { 8 | 9 | String translate(String name); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/littlenb/mybatisjpa/util/PersistentUtil.java: -------------------------------------------------------------------------------- 1 | package com.littlenb.mybatisjpa.util; 2 | 3 | import com.littlenb.mybatisjpa.support.MybatisJapConfiguration; 4 | import java.lang.reflect.Field; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import javax.persistence.Column; 8 | import javax.persistence.Id; 9 | import javax.persistence.ManyToMany; 10 | import javax.persistence.ManyToOne; 11 | import javax.persistence.OneToMany; 12 | import javax.persistence.OneToOne; 13 | import javax.persistence.Table; 14 | import javax.persistence.Transient; 15 | 16 | /** 17 | * @author sway.li 18 | */ 19 | public class PersistentUtil { 20 | 21 | private static MybatisJapConfiguration conf = MybatisJapConfiguration.getInstance(); 22 | 23 | public static String getTableName(Class clazz) { 24 | return getTableName(clazz, conf.getTableNamingStrategy()); 25 | } 26 | 27 | public static String getTableName(Class clazz, NamingStrategy namingStrategy) { 28 | if (clazz.isAnnotationPresent(Table.class)) { 29 | Table table = clazz.getAnnotation(Table.class); 30 | if (!"".equals(table.name().trim())) { 31 | return table.name(); 32 | } 33 | } 34 | String tableName = clazz.getSimpleName(); 35 | 36 | return namingStrategy.translate(tableName); 37 | } 38 | 39 | public static String getColumnName(Field field) { 40 | return getColumnName(field, conf.getColumnNamingStrategy()); 41 | } 42 | 43 | public static String getColumnName(Field field, NamingStrategy namingStrategy) { 44 | if (field.isAnnotationPresent(Column.class)) { 45 | Column column = field.getAnnotation(Column.class); 46 | if (!"".equals(column.name().trim())) { 47 | return column.name(); 48 | } 49 | } 50 | String columnName = field.getName(); 51 | 52 | return namingStrategy.translate(columnName); 53 | } 54 | 55 | public static String getMappedName(Field field) { 56 | if (field.isAnnotationPresent(OneToOne.class)) { 57 | OneToOne one = field.getAnnotation(OneToOne.class); 58 | if (!one.mappedBy().trim().equals("")) { 59 | return one.mappedBy(); 60 | } 61 | } 62 | if (field.isAnnotationPresent(OneToMany.class)) { 63 | OneToMany one = field.getAnnotation(OneToMany.class); 64 | if (!one.mappedBy().trim().equals("")) { 65 | return one.mappedBy(); 66 | } 67 | } 68 | return null; 69 | } 70 | 71 | public static List getPersistentFields(Class clazz) { 72 | List list = new ArrayList<>(); 73 | Class searchType = clazz; 74 | while (!Object.class.equals(searchType) && searchType != null) { 75 | Field[] fields = searchType.getDeclaredFields(); 76 | for (Field field : fields) { 77 | if (isPersistentField(field)) { 78 | list.add(field); 79 | } 80 | } 81 | searchType = searchType.getSuperclass(); 82 | } 83 | return list; 84 | } 85 | 86 | public static Field getIdField(Class clazz) { 87 | Class searchType = clazz; 88 | while (!Object.class.equals(searchType) && searchType != null) { 89 | Field[] fields = searchType.getDeclaredFields(); 90 | for (Field field : fields) { 91 | if (field.isAnnotationPresent(Id.class) && isPersistentField(field)) { 92 | return field; 93 | } 94 | } 95 | searchType = searchType.getSuperclass(); 96 | } 97 | return null; 98 | } 99 | 100 | public static boolean insertable(Field field) { 101 | if (!isPersistentField(field) || isAssociationField(field)) { 102 | return false; 103 | } 104 | 105 | if (field.isAnnotationPresent(Column.class)) { 106 | Column column = field.getAnnotation(Column.class); 107 | return column.insertable(); 108 | } 109 | 110 | return true; 111 | } 112 | 113 | public static boolean updatable(Field field) { 114 | if (!isPersistentField(field) || isAssociationField(field)) { 115 | return false; 116 | } 117 | 118 | if (field.isAnnotationPresent(Column.class)) { 119 | Column column = field.getAnnotation(Column.class); 120 | return column.updatable(); 121 | } 122 | 123 | return true; 124 | } 125 | 126 | public static boolean isPersistentField(Field field) { 127 | return !field.isAnnotationPresent(Transient.class) && !isAssociationField(field); 128 | } 129 | 130 | public static boolean isAssociationField(Field field) { 131 | return field.isAnnotationPresent(OneToOne.class) 132 | || field.isAnnotationPresent(OneToMany.class) 133 | || field.isAnnotationPresent(ManyToOne.class) 134 | || field.isAnnotationPresent(ManyToMany.class); 135 | } 136 | } 137 | --------------------------------------------------------------------------------