├── .gitattributes ├── .mvn ├── maven.config ├── extensions.xml ├── wrapper │ ├── maven-wrapper.properties │ └── MavenWrapperDownloader.java └── settings.xml ├── CONTRIBUTING.md ├── renovate.json ├── NOTICE ├── src ├── site │ ├── markdown │ │ └── index.md │ └── site.xml ├── test │ ├── resources │ │ ├── mybatis-thymeleaf-empty.properties │ │ ├── org │ │ │ └── mybatis │ │ │ │ └── scripting │ │ │ │ └── thymeleaf │ │ │ │ ├── support │ │ │ │ ├── BaseMapper_selectOne.sql │ │ │ │ ├── TestMapper │ │ │ │ │ ├── selectAllAsc.sql │ │ │ │ │ ├── TestMapper-delete.sql │ │ │ │ │ ├── TestMapper-update.sql │ │ │ │ │ └── TestMapper-update-h2.sql │ │ │ │ ├── BaseMapper │ │ │ │ │ ├── BaseMapper-count.sql │ │ │ │ │ ├── BaseMapper-insert.sql │ │ │ │ │ └── BaseMapper-insert-h2.sql │ │ │ │ ├── sql │ │ │ │ │ └── TestMapper-selectAllDesc.sql │ │ │ │ └── DefaultPackageNameMapper │ │ │ │ │ └── DefaultPackageNameMapper-selectAllDesc.sql │ │ │ │ └── integrationtest │ │ │ │ └── mapper │ │ │ │ ├── XmlNameMapper.xml │ │ │ │ └── XmlNameSqlSessionMapper.xml │ │ ├── sql │ │ │ ├── OneWayNameMapper │ │ │ │ ├── delete.sql │ │ │ │ ├── findByIdWhere.sql │ │ │ │ ├── findById.sql │ │ │ │ ├── insert.sql │ │ │ │ ├── findByIdWithNestedParam.sql │ │ │ │ ├── findByIdWithoutParamAnnotation.sql │ │ │ │ ├── insertByBulk.sql │ │ │ │ ├── update.sql │ │ │ │ ├── findByIds.sql │ │ │ │ ├── findByIdsWithoutParamAnnotation.sql │ │ │ │ └── findByName.sql │ │ │ ├── NameMapper │ │ │ │ ├── findById.sql │ │ │ │ ├── delete.sql │ │ │ │ ├── insert.sql │ │ │ │ ├── findByIdWithoutParamAnnotation.sql │ │ │ │ ├── findByIdWhere.sql │ │ │ │ ├── findByIds.sql │ │ │ │ ├── findByFirstNames.sql │ │ │ │ ├── findByIdWithNestedParam.sql │ │ │ │ ├── insertByBulk.sql │ │ │ │ ├── findByIdsWithinParam.sql │ │ │ │ ├── findByIdsWithoutParamAnnotation.sql │ │ │ │ ├── update.sql │ │ │ │ └── findByName.sql │ │ │ ├── TemplateFilePathProviderMapper │ │ │ │ ├── TemplateFilePathProviderMapper-findById.sql │ │ │ │ ├── TemplateFilePathProviderMapper-delete.sql │ │ │ │ ├── TemplateFilePathProviderMapper-insert.sql │ │ │ │ └── TemplateFilePathProviderMapper-update.sql │ │ │ ├── PersonMapper │ │ │ │ ├── insertByBulk.sql │ │ │ │ ├── insertByBulkWithIndexed.sql │ │ │ │ ├── selectMailsByConditionsArray.sql │ │ │ │ ├── insertMailsByBulk.sql │ │ │ │ └── insertMailsByBulkWithIndexed.sql │ │ │ └── OneWayPersonMapper │ │ │ │ └── selectMailsByConditionsArray.sql │ │ ├── mybatis-thymeleaf.properties │ │ ├── mybatis-thymeleaf-customizer-not-found.properties │ │ ├── mybatis-thymeleaf-customizer-no-default-constructor.properties │ │ ├── mybatis-thymeleaf-custom-without-path-provider.properties │ │ ├── mybatis-thymeleaf-custom.properties │ │ ├── logback-test.xml │ │ ├── mapper-config.xml │ │ └── create-db.sql │ └── java │ │ ├── DefaultPackageNameMapper.java │ │ └── org │ │ └── mybatis │ │ └── scripting │ │ └── thymeleaf │ │ ├── support │ │ ├── BaseMapper.java │ │ └── TestMapper.java │ │ ├── CustomTemplateEngineCustomizer.java │ │ ├── integrationtest │ │ ├── mapper │ │ │ ├── InvalidNameParam.java │ │ │ ├── OneWayPersonMapper.java │ │ │ ├── XmlNameMapper.java │ │ │ ├── TemplateFilePathProviderMapper.java │ │ │ ├── NameParam.java │ │ │ ├── OneWayNameMapper.java │ │ │ ├── PersonMapper.java │ │ │ └── NameMapper.java │ │ ├── domain │ │ │ ├── Mail.java │ │ │ ├── Person.java │ │ │ └── Name.java │ │ ├── TemplateFilePathProviderMapperNoCacheTest.java │ │ └── TemplateFilePathProviderMapperTest.java │ │ ├── NoDefaultConstructorTemplateEngineCustomizer.java │ │ ├── DefaultTemplateEngineCustomizer.java │ │ ├── expression │ │ └── LikesTest.java │ │ ├── TemplateEngineCustomizerTest.java │ │ ├── PropertyAccessorTest.java │ │ ├── SqlGeneratorConfigTest.java │ │ ├── MyBatisDialectTest.java │ │ └── ThymeleafSqlSourceTest.java └── main │ ├── java │ └── org │ │ └── mybatis │ │ └── scripting │ │ └── thymeleaf │ │ ├── support │ │ ├── package-info.java │ │ └── spring │ │ │ ├── package-info.java │ │ │ └── SpringNamedParameterBindVariableRender.java │ │ ├── expression │ │ ├── package-info.java │ │ └── Likes.java │ │ ├── package-info.java │ │ ├── processor │ │ ├── package-info.java │ │ ├── BindVariableRender.java │ │ ├── MyBatisBindTagProcessor.java │ │ └── MyBatisParamTagProcessor.java │ │ ├── MyBatisIntegratingEngineContextFactory.java │ │ ├── ThymeleafLanguageDriver.java │ │ ├── TemplateEngineCustomizer.java │ │ ├── MyBatisBindingContext.java │ │ ├── MyBatisDialect.java │ │ └── PropertyAccessor.java │ └── asciidoc │ └── docinfos │ ├── docinfo.html │ └── docinfo-footer.html ├── .gitignore ├── LICENSE_HEADER ├── format.xml ├── .github └── workflows │ ├── sonatype.yaml │ ├── ci.yaml │ ├── site.yaml │ ├── sonar.yaml │ ├── coveralls.yaml │ └── codeql.yml ├── README.md ├── pom.xml └── mvnw.cmd /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set default behaviour, in case users don't have core.autocrlf set. 2 | * text=auto -------------------------------------------------------------------------------- /.mvn/maven.config: -------------------------------------------------------------------------------- 1 | -Daether.checksums.algorithms=SHA-512,SHA-256,SHA-1,MD5 2 | -Daether.connector.smartChecksums=false 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Please see [the MyBatis core module Guideline](https://github.com/mybatis/mybatis-3/blob/master/CONTRIBUTING.md). 2 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | MyBatis Thymeleaf Scripting 2 | Copyright 2018-2024 3 | 4 | This product includes software developed by 5 | The MyBatis Team (https://www.mybatis.org/). 6 | -------------------------------------------------------------------------------- /src/site/markdown/index.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | ## What is MyBatis Thymeleaf ? 4 | 5 | The mybatis-thymeleaf is a plugin that helps applying an SQL using template provided by Thymeleaf 3. 6 | 7 | For details, please see the [User's Guide](./user-guide.html). -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /*.iml 2 | /*.ipr 3 | /*.iws 4 | /.classpath 5 | /.factorypath 6 | /.idea 7 | /.project 8 | /.settings 9 | /nb* 10 | /release.properties 11 | /target 12 | .github/keys/ 13 | 14 | # These are needed if running in IDE without properties set 15 | /ibderby 16 | derby.log 17 | /bin/ 18 | .mvn/wrapper/maven-wrapper.jar 19 | pom.xml.releaseBackup 20 | -------------------------------------------------------------------------------- /LICENSE_HEADER: -------------------------------------------------------------------------------- 1 | Copyright ${license.git.copyrightYears} the original author or authors. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | https://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /src/test/resources/mybatis-thymeleaf-empty.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2018-2022 the original author or authors. 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 | # https://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 | # 18 | -------------------------------------------------------------------------------- /src/test/resources/org/mybatis/scripting/thymeleaf/support/BaseMapper_selectOne.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | -------------------------------------------------------------------------------- /src/test/resources/org/mybatis/scripting/thymeleaf/support/TestMapper/selectAllAsc.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | -------------------------------------------------------------------------------- /src/test/resources/org/mybatis/scripting/thymeleaf/support/BaseMapper/BaseMapper-count.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | -------------------------------------------------------------------------------- /src/test/resources/org/mybatis/scripting/thymeleaf/support/BaseMapper/BaseMapper-insert.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | -------------------------------------------------------------------------------- /src/test/resources/org/mybatis/scripting/thymeleaf/support/TestMapper/TestMapper-delete.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | -------------------------------------------------------------------------------- /src/test/resources/org/mybatis/scripting/thymeleaf/support/TestMapper/TestMapper-update.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | -------------------------------------------------------------------------------- /src/test/resources/org/mybatis/scripting/thymeleaf/support/sql/TestMapper-selectAllDesc.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | -------------------------------------------------------------------------------- /src/test/resources/org/mybatis/scripting/thymeleaf/support/BaseMapper/BaseMapper-insert-h2.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | -------------------------------------------------------------------------------- /src/test/resources/org/mybatis/scripting/thymeleaf/support/TestMapper/TestMapper-update-h2.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | -------------------------------------------------------------------------------- /src/test/resources/sql/OneWayNameMapper/delete.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | DELETE FROM names 18 | WHERE id = #{id} 19 | -------------------------------------------------------------------------------- /src/test/resources/mybatis-thymeleaf.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2018-2022 the original author or authors. 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 | # https://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 | customizer=org.mybatis.scripting.thymeleaf.DefaultTemplateEngineCustomizer 18 | -------------------------------------------------------------------------------- /src/test/resources/mybatis-thymeleaf-customizer-not-found.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2018-2022 the original author or authors. 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 | # https://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 | customizer=org.mybatis.scripting.thymeleaf.FooTemplateEngineCustomizer -------------------------------------------------------------------------------- /src/test/resources/org/mybatis/scripting/thymeleaf/support/DefaultPackageNameMapper/DefaultPackageNameMapper-selectAllDesc.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | -------------------------------------------------------------------------------- /src/test/resources/sql/NameMapper/findById.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | SELECT * FROM names 18 | /*[# th:insert="~{sql/NameMapper/findByIdWhere.sql}" /]*/ 19 | -------------------------------------------------------------------------------- /src/test/resources/sql/OneWayNameMapper/findByIdWhere.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | WHERE 1 = 1 18 | [# th:if="${id} != null"] 19 | AND id = #{id} 20 | [/] 21 | -------------------------------------------------------------------------------- /format.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/test/resources/sql/OneWayNameMapper/findById.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | SELECT * FROM names 18 | [# th:insert="~{sql/OneWayNameMapper/findByIdWhere.sql}" /] 19 | -------------------------------------------------------------------------------- /src/test/resources/mybatis-thymeleaf-customizer-no-default-constructor.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2018-2022 the original author or authors. 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 | # https://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 | customizer=org.mybatis.scripting.thymeleaf.NoDefaultConstructorTemplateEngineCustomizer -------------------------------------------------------------------------------- /src/test/resources/sql/OneWayNameMapper/insert.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | INSERT INTO names (firstName, lastName) 18 | VALUES ( 19 | #{firstName}, #{lastName} 20 | ) 21 | -------------------------------------------------------------------------------- /src/test/resources/sql/NameMapper/delete.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | DELETE FROM names 18 | WHERE id = /*[# mb:p="id,typeHandler=org.apache.ibatis.type.IntegerTypeHandler"]*/ 1 /*[/]*/ 19 | -------------------------------------------------------------------------------- /src/test/resources/sql/NameMapper/insert.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | INSERT INTO names (firstName, lastName) 18 | VALUES (/*[# mb:p="firstName"]*/ 'Taro' /*[/]*/, /*[# mb:p="lastName"]*/ 'Yamada' /*[/]*/) 19 | -------------------------------------------------------------------------------- /src/test/resources/sql/OneWayNameMapper/findByIdWithNestedParam.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | SELECT * FROM names 18 | WHERE 1 = 1 19 | [# th:if="${p.id} != null"] 20 | AND id = #{p.id} 21 | [/] 22 | -------------------------------------------------------------------------------- /src/test/resources/sql/OneWayNameMapper/findByIdWithoutParamAnnotation.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | SELECT * FROM names 18 | WHERE 1 = 1 19 | [# th:if="${id} != null"] 20 | AND id = #{id} 21 | [/] 22 | -------------------------------------------------------------------------------- /src/test/resources/sql/TemplateFilePathProviderMapper/TemplateFilePathProviderMapper-findById.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | SELECT * FROM names 18 | /*[# th:insert="~{sql/NameMapper/findByIdWhere.sql}" /]*/ 19 | -------------------------------------------------------------------------------- /.github/workflows/sonatype.yaml: -------------------------------------------------------------------------------- 1 | name: Sonatype 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | permissions: read-all 9 | 10 | jobs: 11 | build: 12 | if: github.repository_owner == 'mybatis' && ! contains(toJSON(github.event.head_commit.message), '[maven-release-plugin]') 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v6 16 | - name: Set up JDK 17 | uses: actions/setup-java@v5 18 | with: 19 | cache: maven 20 | distribution: temurin 21 | java-version: 21 22 | - name: Deploy to Sonatype 23 | run: ./mvnw deploy -DskipTests -B -V -Dlog.level.thymeleaf.config=info --no-transfer-progress --settings ./.mvn/settings.xml -Dlicense.skip=true 24 | env: 25 | CI_DEPLOY_USERNAME: ${{ secrets.CI_DEPLOY_USERNAME }} 26 | CI_DEPLOY_PASSWORD: ${{ secrets.CI_DEPLOY_PASSWORD }} 27 | -------------------------------------------------------------------------------- /src/test/resources/sql/NameMapper/findByIdWithoutParamAnnotation.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | SELECT * FROM names 18 | WHERE 1 = 1 19 | /*[# th:if="${id} != null"]*/ 20 | AND id = /*[# mb:p="id"]*/ 1 /*[/]*/ 21 | /*[/]*/ 22 | -------------------------------------------------------------------------------- /src/test/resources/sql/NameMapper/findByIdWhere.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | /*[- */ 18 | SELECT * FROM names 19 | /* -]*/ 20 | WHERE 1 = 1 21 | /*[# th:if="${id} != null"]*/ 22 | AND id = /*[# mb:p="id"]*/ 1 /*[/]*/ 23 | /*[/]*/ 24 | -------------------------------------------------------------------------------- /src/test/resources/sql/TemplateFilePathProviderMapper/TemplateFilePathProviderMapper-delete.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | DELETE FROM names 18 | WHERE id = /*[# mb:p="id,typeHandler=org.apache.ibatis.type.IntegerTypeHandler"]*/ 1 /*[/]*/ 19 | -------------------------------------------------------------------------------- /src/main/java/org/mybatis/scripting/thymeleaf/support/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 | * The package that holds classes for supports development. 18 | * 19 | * @since 1.0.1 20 | */ 21 | package org.mybatis.scripting.thymeleaf.support; -------------------------------------------------------------------------------- /src/test/resources/sql/NameMapper/findByIds.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | SELECT * FROM names 18 | WHERE 1 = 1 19 | /*[# th:if="${not #arrays.isEmpty(ids)}"]*/ 20 | AND id IN (/*[# mb:p="ids"]*/ 1 /*[/]*/) 21 | /*[/]*/ 22 | ORDER BY id 23 | -------------------------------------------------------------------------------- /src/test/java/DefaultPackageNameMapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 | import java.util.List; 17 | 18 | import org.mybatis.scripting.thymeleaf.integrationtest.domain.Name; 19 | 20 | public interface DefaultPackageNameMapper { 21 | List selectAllDesc(); 22 | } 23 | -------------------------------------------------------------------------------- /src/test/resources/sql/NameMapper/findByFirstNames.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | SELECT * FROM names 18 | WHERE 1 = 1 19 | AND firstName IN (/*[# mb:p="firstNames,typeHandler=org.apache.ibatis.type.StringTypeHandler"]*/ 'FirstName' /*[/]*/) 20 | ORDER BY id 21 | -------------------------------------------------------------------------------- /src/test/resources/sql/TemplateFilePathProviderMapper/TemplateFilePathProviderMapper-insert.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | INSERT INTO names (firstName, lastName) 18 | VALUES (/*[# mb:p="firstName"]*/ 'Taro' /*[/]*/, /*[# mb:p="lastName"]*/ 'Yamada' /*[/]*/) 19 | -------------------------------------------------------------------------------- /src/main/asciidoc/docinfos/docinfo.html: -------------------------------------------------------------------------------- 1 | 18 |
19 | 20 | MyBatis logo 21 | 22 |
23 |
-------------------------------------------------------------------------------- /src/test/resources/sql/PersonMapper/insertByBulk.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | INSERT INTO persons (name) VALUES 18 | /*[# th:each="person : ${list}"]*/ 19 | ( 20 | /*[# mb:p="person.name"]*/ 'Taro Yamada' /*[/]*/ 21 | )/*[(${personStat.last} ? '' : ',')]*/ 22 | /*[/]*/ 23 | -------------------------------------------------------------------------------- /src/main/asciidoc/docinfos/docinfo-footer.html: -------------------------------------------------------------------------------- 1 | 18 |
19 |
20 |

Copyright ©2018-{localyear} MyBatis.org. All rights reserved.

21 |
22 |
23 | -------------------------------------------------------------------------------- /src/main/java/org/mybatis/scripting/thymeleaf/support/spring/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 | * The package that holds classes for supporting integration with Spring family. 18 | * 19 | * @since 1.0.2 20 | */ 21 | package org.mybatis.scripting.thymeleaf.support.spring; -------------------------------------------------------------------------------- /src/test/resources/sql/NameMapper/findByIdWithNestedParam.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | SELECT * FROM names 18 | WHERE 1 = 1 19 | /*[# th:if="${p.id} != null"]*/ 20 | AND id = /*[# mb:p="p.id,typeHandler=org.apache.ibatis.type.IntegerTypeHandler"]*/ 1 /*[/]*/ 21 | /*[/]*/ 22 | -------------------------------------------------------------------------------- /src/test/resources/sql/OneWayNameMapper/insertByBulk.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | INSERT INTO names (firstName, lastName) VALUES 18 | [# th:each="name : ${list}"] 19 | ( 20 | [# mb:p="name.firstName" /], [# mb:p="name.lastName" /] 21 | )[(${nameStat.last ? '' : ','})] 22 | [/] 23 | -------------------------------------------------------------------------------- /src/site/site.xml: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/test/resources/sql/OneWayNameMapper/update.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | UPDATE names 18 | SET id = id 19 | [# th:if="${firstName} != null"] 20 | ,firstName = #{firstName} 21 | [/] 22 | [# th:if="${lastName} != null"] 23 | ,lastName = #{lastName} 24 | [/] 25 | WHERE id = #{id} 26 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: Java CI 2 | 3 | on: [workflow_dispatch, push, pull_request] 4 | 5 | permissions: read-all 6 | 7 | jobs: 8 | test: 9 | runs-on: ${{ matrix.os }} 10 | strategy: 11 | matrix: 12 | cache: [maven] 13 | distribution: [temurin] 14 | java: [17, 21, 24, 25-ea] 15 | os: [ubuntu-latest, macos-latest, windows-latest] 16 | fail-fast: false 17 | max-parallel: 4 18 | name: Test JDK ${{ matrix.java }}, ${{ matrix.os }} 19 | 20 | steps: 21 | - uses: actions/checkout@v6 22 | - name: Set up JDK ${{ matrix.java }} ${{ matrix.distribution }} 23 | uses: actions/setup-java@v5 24 | with: 25 | java-version: ${{ matrix.java }} 26 | distribution: ${{ matrix.distribution }} 27 | cache: ${{ matrix.cache }} 28 | - name: Test with Maven 29 | run: ./mvnw test -B -V --no-transfer-progress -D"license.skip=true" -D"log.level.thymeleaf.config=info" 30 | -------------------------------------------------------------------------------- /.mvn/extensions.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 21 | fr.jcgay.maven 22 | maven-profiler 23 | 3.3 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/test/resources/sql/PersonMapper/insertByBulkWithIndexed.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | INSERT INTO persons (name) VALUES 18 | /*[# th:each="person : ${list}"]*/ 19 | ( 20 | /*[# mb:p="list[${personStat.index}].name"]*/ 'Taro Yamada' /*[/]*/ 21 | )/*[(${personStat.last} ? '' : ',')]*/ 22 | /*[/]*/ 23 | -------------------------------------------------------------------------------- /src/test/java/org/mybatis/scripting/thymeleaf/support/BaseMapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf.support; 17 | 18 | public interface BaseMapper { 19 | void insert(T model); 20 | 21 | void update(T model); 22 | 23 | long count(); 24 | 25 | T selectOne(int id); 26 | } 27 | -------------------------------------------------------------------------------- /src/test/resources/sql/NameMapper/insertByBulk.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | INSERT INTO names (firstName, lastName) VALUES 18 | /*[# th:each="name : ${list}"]*/ 19 | ( 20 | /*[# mb:p="name.firstName"]*/ 'Taro' /*[/]*/, 21 | /*[# mb:p="name.lastName"]*/ 'Yamada' /*[/]*/ 22 | )/*[(${nameStat.last ? '' : ','})]*/ 23 | /*[/]*/ 24 | -------------------------------------------------------------------------------- /src/test/resources/sql/OneWayNameMapper/findByIds.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | SELECT * FROM names 18 | WHERE 1 = 1 19 | [# th:if="${not #lists.isEmpty(ids)}"] 20 | AND id IN ( 21 | [# th:each="id : ${ids}"] 22 | [(${idStat.first} ? '' : ',')] 23 | [# mb:p="id" /] 24 | [/] 25 | ) 26 | [/] 27 | ORDER BY id 28 | -------------------------------------------------------------------------------- /src/test/resources/sql/PersonMapper/selectMailsByConditionsArray.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | SELECT id, person_id, address 18 | FROM person_mails 19 | WHERE 1 != 1 20 | /*[# th:each="conditions : ${list}"]*/ 21 | OR address IN ( 22 | /*[# mb:p="conditions.mails"]*/ 'a@test.com', 'a2@test.com' /*[/]*/ 23 | ) 24 | /*[/]*/ 25 | ORDER BY id 26 | -------------------------------------------------------------------------------- /src/test/resources/sql/OneWayPersonMapper/selectMailsByConditionsArray.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | SELECT id, person_id, address 18 | FROM person_mails 19 | WHERE 1 != 1 20 | [# th:each="conditions : ${list}"] 21 | OR address IN ( 22 | [# mb:p="conditions.mails,typeHandler=org.apache.ibatis.type.StringTypeHandler" /] 23 | ) 24 | [/] 25 | ORDER BY id 26 | -------------------------------------------------------------------------------- /.github/workflows/site.yaml: -------------------------------------------------------------------------------- 1 | name: Site 2 | 3 | on: 4 | push: 5 | branches: 6 | - site 7 | 8 | permissions: 9 | contents: write 10 | 11 | jobs: 12 | build: 13 | if: github.repository_owner == 'mybatis' && ! contains(toJSON(github.event.head_commit.message), '[maven-release-plugin]') 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v6 17 | - name: Set up JDK 18 | uses: actions/setup-java@v5 19 | with: 20 | cache: maven 21 | distribution: temurin 22 | java-version: 21 23 | - name: Build site 24 | run: ./mvnw site site:stage -DskipTests -Dlicense.skip=true -B -V --no-transfer-progress --settings ./.mvn/settings.xml 25 | env: 26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 27 | NVD_API_KEY: ${{ secrets.NVD_API_KEY }} 28 | - name: Deploy Site to gh-pages 29 | uses: JamesIves/github-pages-deploy-action@v4 30 | with: 31 | branch: gh-pages 32 | folder: target/staging 33 | -------------------------------------------------------------------------------- /src/test/resources/sql/OneWayNameMapper/findByIdsWithoutParamAnnotation.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | SELECT * FROM names 18 | WHERE 1 = 1 19 | [# th:if="${not #lists.isEmpty(list)}"] 20 | AND id IN ( 21 | [# th:each="id : ${list}"] 22 | [(${idStat.first ? '' : ','})] 23 | [# mb:p="id" /] 24 | [/] 25 | ) 26 | [/] 27 | ORDER BY id 28 | -------------------------------------------------------------------------------- /src/main/java/org/mybatis/scripting/thymeleaf/expression/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 | * The package that holds classes for mybatis-thymeleaf's utility expression. 18 | *

19 | * The mybatis-thymeleaf provides utility expression object. 20 | *

    21 | *
  • #likes
  • 22 | *
23 | */ 24 | package org.mybatis.scripting.thymeleaf.expression; -------------------------------------------------------------------------------- /src/test/resources/sql/NameMapper/findByIdsWithinParam.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | SELECT * FROM names 18 | WHERE 1 = 1 19 | /*[# th:if="${not #lists.isEmpty(ids)}"]*/ 20 | AND id IN ( 21 | /*[# th:each="id : ${ids}"]*/ 22 | /*[(${idStat.first ? '' : ','})]*/ 23 | /*[# mb:p="id"]*/ 1 /*[/]*/ 24 | /*[/]*/ 25 | ) 26 | /*[/]*/ 27 | ORDER BY id 28 | -------------------------------------------------------------------------------- /src/main/java/org/mybatis/scripting/thymeleaf/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 | * The mybatis-thymeleaf root package. 18 | *

19 | * The mybatis-thymeleaf is a plugin that helps applying the 2-way SQL/dynamic SQL feature to the MyBatis 3 using the 20 | * template(or natural template) mechanism provided by Thymeleaf 3. 21 | */ 22 | package org.mybatis.scripting.thymeleaf; -------------------------------------------------------------------------------- /src/test/resources/sql/NameMapper/findByIdsWithoutParamAnnotation.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | SELECT * FROM names 18 | WHERE 1 = 1 19 | /*[# th:if="${not #lists.isEmpty(list)}"]*/ 20 | AND id IN ( 21 | /*[# th:each="id : ${list}"]*/ 22 | /*[(${idStat.first ? '' : ','})]*/ 23 | /*[# mb:p="id"]*/ 1 /*[/]*/ 24 | /*[/]*/ 25 | ) 26 | /*[/]*/ 27 | ORDER BY id 28 | -------------------------------------------------------------------------------- /src/test/resources/sql/NameMapper/update.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | UPDATE names 18 | SET id = id 19 | /*[# th:if="${firstName} != null"]*/ 20 | ,firstName = /*[# mb:p="firstName"]*/ 'Taro' /*[/]*/ 21 | /*[/]*/ 22 | /*[# th:if="${lastName} != null"]*/ 23 | ,lastName = /*[# mb:p="lastName"]*/ 'Yamada' /*[/]*/ 24 | /*[/]*/ 25 | WHERE id = /*[# mb:p="id"]*/ 1 /*[/]*/ 26 | -------------------------------------------------------------------------------- /.github/workflows/sonar.yaml: -------------------------------------------------------------------------------- 1 | name: SonarCloud 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | permissions: read-all 9 | 10 | jobs: 11 | build: 12 | if: github.repository_owner == 'mybatis' 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v6 16 | with: 17 | # Disabling shallow clone is recommended for improving relevancy of reporting 18 | fetch-depth: 0 19 | - name: Set up JDK 20 | uses: actions/setup-java@v5 21 | with: 22 | cache: maven 23 | distribution: temurin 24 | java-version: 21 25 | - name: Analyze with SonarCloud 26 | run: ./mvnw verify jacoco:report sonar:sonar -B -V -Dlog.level.thymeleaf.config=info -Dsonar.projectKey=mybatis_thymeleaf-scripting -Dsonar.organization=mybatis -Dsonar.host.url=https://sonarcloud.io -Dsonar.token=$SONAR_TOKEN -Dlicense.skip=true --no-transfer-progress 27 | env: 28 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 29 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 30 | -------------------------------------------------------------------------------- /src/main/java/org/mybatis/scripting/thymeleaf/processor/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 | * The package that holds classes for mybatis-thymeleaf's processor. 18 | *

19 | * The mybatis-thymeleaf provides processors for following custom attribute tag. 20 | *

    21 | *
  • mb:p
  • 22 | *
  • mb:bind
  • 23 | *
24 | */ 25 | package org.mybatis.scripting.thymeleaf.processor; -------------------------------------------------------------------------------- /src/test/resources/sql/TemplateFilePathProviderMapper/TemplateFilePathProviderMapper-update.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | UPDATE names 18 | SET id = id 19 | /*[# th:if="${firstName} != null"]*/ 20 | ,firstName = /*[# mb:p="firstName"]*/ 'Taro' /*[/]*/ 21 | /*[/]*/ 22 | /*[# th:if="${lastName} != null"]*/ 23 | ,lastName = /*[# mb:p="lastName"]*/ 'Yamada' /*[/]*/ 24 | /*[/]*/ 25 | WHERE id = /*[# mb:p="id"]*/ 1 /*[/]*/ 26 | -------------------------------------------------------------------------------- /src/test/resources/sql/PersonMapper/insertMailsByBulk.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | INSERT INTO person_mails (person_id, address) VALUES 18 | /*[# th:each="person : ${list}"]*/ 19 | /*[# th:each="mail : ${person.mails}"]*/ 20 | ( 21 | /*[# mb:p="person.id"]*/ 1 /*[/]*/, 22 | /*[# mb:p="mail.address"]*/ 'taro.yamada@test.com' /*[/]*/ 23 | )/*[(${personStat.last and mailStat.last} ? '' : ',')]*/ 24 | /*[/]*/ 25 | /*[/]*/ 26 | 27 | -------------------------------------------------------------------------------- /src/test/java/org/mybatis/scripting/thymeleaf/CustomTemplateEngineCustomizer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf; 17 | 18 | import org.thymeleaf.TemplateEngine; 19 | 20 | public class CustomTemplateEngineCustomizer implements TemplateEngineCustomizer { 21 | static TemplateEngine templateEngine; 22 | 23 | @Override 24 | public void customize(TemplateEngine defaultTemplateEngine) { 25 | templateEngine = defaultTemplateEngine; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/org/mybatis/scripting/thymeleaf/support/TestMapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf.support; 17 | 18 | import java.util.List; 19 | 20 | import org.mybatis.scripting.thymeleaf.integrationtest.domain.Name; 21 | 22 | interface TestMapper extends BaseMapper { 23 | void delete(int id); 24 | 25 | List selectAllDesc(); 26 | 27 | List selectAllAsc(); 28 | 29 | List selectAllByFirstName(String firstName); 30 | } 31 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | wrapperVersion=3.3.2 18 | distributionType=source 19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip 20 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar 21 | -------------------------------------------------------------------------------- /src/test/java/org/mybatis/scripting/thymeleaf/integrationtest/mapper/InvalidNameParam.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf.integrationtest.mapper; 17 | 18 | public class InvalidNameParam { 19 | private int id; 20 | 21 | public InvalidNameParam(int id) { 22 | this.id = id; 23 | } 24 | 25 | public int getId() { 26 | throw new IllegalStateException("invalid param"); 27 | } 28 | 29 | public void setId(int id) { 30 | this.id = id; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/org/mybatis/scripting/thymeleaf/NoDefaultConstructorTemplateEngineCustomizer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf; 17 | 18 | import org.thymeleaf.TemplateEngine; 19 | 20 | public class NoDefaultConstructorTemplateEngineCustomizer implements TemplateEngineCustomizer { 21 | private NoDefaultConstructorTemplateEngineCustomizer(String id) { 22 | } 23 | 24 | @Override 25 | public void customize(TemplateEngine defaultTemplateEngine) { 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/test/resources/sql/PersonMapper/insertMailsByBulkWithIndexed.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | INSERT INTO person_mails (person_id, address) VALUES 18 | /*[# th:each="person : ${list}"]*/ 19 | /*[# th:each="mail : ${person.mails}"]*/ 20 | ( 21 | /*[# mb:p="list[${personStat.index}].id"]*/ 1 /*[/]*/, 22 | /*[# mb:p="list[${personStat.index}].mails[${mailStat.index}].address"]*/ 'taro.yamada@test.com' /*[/]*/ 23 | )/*[(${personStat.last and mailStat.last} ? '' : ',')]*/ 24 | /*[/]*/ 25 | /*[/]*/ 26 | 27 | -------------------------------------------------------------------------------- /src/test/resources/sql/OneWayNameMapper/findByName.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | SELECT * FROM names 18 | WHERE 1 = 1 19 | [# th:if="${firstName} != null"] 20 | [# mb:bind="patternFirstName=|${#likes.escapeWildcard(firstName)}%|" /] 21 | AND firstName LIKE #{patternFirstName} [(${#likes.escapeClause()})] 22 | [/] 23 | [# th:if="${lastName} != null"] 24 | [# mb:bind="patternLastName=|${#likes.escapeWildcard(lastName)}%|" /] 25 | AND lastName LIKE #{patternLastName} [(${#likes.escapeClause()})] 26 | [/] 27 | -------------------------------------------------------------------------------- /src/test/java/org/mybatis/scripting/thymeleaf/DefaultTemplateEngineCustomizer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf; 17 | 18 | import org.thymeleaf.TemplateEngine; 19 | 20 | public class DefaultTemplateEngineCustomizer implements TemplateEngineCustomizer { 21 | static TemplateEngine templateEngine; 22 | 23 | @Override 24 | public void customize(TemplateEngine defaultTemplateEngine) { 25 | BuiltIn.DO_NOTHING.customize(defaultTemplateEngine); 26 | templateEngine = defaultTemplateEngine; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /.github/workflows/coveralls.yaml: -------------------------------------------------------------------------------- 1 | name: Coveralls 2 | 3 | on: [push, pull_request] 4 | 5 | permissions: read-all 6 | 7 | jobs: 8 | build: 9 | if: github.repository_owner == 'mybatis' 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v6 13 | - name: Set up JDK 14 | uses: actions/setup-java@v5 15 | with: 16 | cache: maven 17 | distribution: temurin 18 | java-version: 21 19 | - name: Report Coverage to Coveralls for Pull Requests 20 | if: github.event_name == 'pull_request' 21 | run: ./mvnw -B -V test jacoco:report coveralls:report -q -Dlicense.skip=true -Dlog.level.thymeleaf.config=info -DrepoToken=$GITHUB_TOKEN -DserviceName=github -DpullRequest=$PR_NUMBER --no-transfer-progress 22 | env: 23 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 24 | PR_NUMBER: ${{ github.event.number }} 25 | - name: Report Coverage to Coveralls for General Push 26 | if: github.event_name == 'push' 27 | run: ./mvnw -B -V test jacoco:report coveralls:report -q -Dlicense.skip=true -Dlog.level.thymeleaf.config=info -DrepoToken=$GITHUB_TOKEN -DserviceName=github --no-transfer-progress 28 | env: 29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | -------------------------------------------------------------------------------- /src/test/resources/sql/NameMapper/findByName.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | SELECT * FROM names 18 | WHERE 1 = 1 19 | /*[# th:if="${firstName} != null"]*/ 20 | /*[# mb:bind="patternFirstName=|${#likes.escapeWildcard(firstName)}%|" /]*/ 21 | AND firstName LIKE /*[# mb:p="patternFirstName"]*/ 'Taro%' /*[/]*/ /*[(${#likes.escapeClause()})]*/ 22 | /*[/]*/ 23 | /*[# th:if="${lastName} != null"]*/ 24 | /*[# mb:bind="patternLastName=|${#likes.escapeWildcard(lastName)}%|" /]*/ 25 | AND lastName LIKE /*[# mb:p="patternLastName"]*/ 'Yamada%' /*[/]*/ /*[(${#likes.escapeClause()})]*/ 26 | /*[/]*/ 27 | -------------------------------------------------------------------------------- /src/main/java/org/mybatis/scripting/thymeleaf/support/spring/SpringNamedParameterBindVariableRender.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf.support.spring; 17 | 18 | import org.mybatis.scripting.thymeleaf.processor.BindVariableRender; 19 | 20 | /** 21 | * The implement class that render by the Spring named parameter format(e.g. {@literal :id}). 22 | * 23 | * @author Kazuki Shimizu 24 | * 25 | * @since 1.0.2 26 | */ 27 | public class SpringNamedParameterBindVariableRender implements BindVariableRender { 28 | 29 | @Override 30 | public String render(String name) { 31 | return ":" + name; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/test/resources/mybatis-thymeleaf-custom-without-path-provider.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2018-2022 the original author or authors. 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 | # https://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 | use2way=false 18 | customizer=org.mybatis.scripting.thymeleaf.CustomTemplateEngineCustomizer 19 | template-file.cache-enabled=false 20 | template-file.cache-ttl=30000 21 | template-file.encoding=ISO-8859-1 22 | template-file.base-dir=templates/ 23 | template-file.patterns= *.sql , *.sql.template 24 | dialect.prefix=mybatis 25 | dialect.bind-variable-render=org.mybatis.scripting.thymeleaf.support.spring.SpringNamedParameterBindVariableRender 26 | dialect.like-escape-char=~ 27 | dialect.like-escape-clause-format=escape '%s' 28 | dialect.like-additional-escape-target-chars=% , _ 29 | -------------------------------------------------------------------------------- /src/test/java/org/mybatis/scripting/thymeleaf/expression/LikesTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf.expression; 17 | 18 | import org.junit.jupiter.api.Assertions; 19 | import org.junit.jupiter.api.Test; 20 | 21 | class LikesTest { 22 | 23 | @Test 24 | void testEscapeWildcardValueIsNull() { 25 | Assertions.assertEquals(Likes.newBuilder().build().escapeWildcard(null), ""); 26 | } 27 | 28 | @Test 29 | void testEscapeWildcardValueWithAdditionalEscapeTargetChars() { 30 | Assertions.assertEquals("a\\%\\_\\\\b", 31 | Likes.newBuilder().additionalEscapeTargetChars('%', '_').build().escapeWildcard("a%_\\b")); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | schedule: 9 | - cron: '41 6 * * 3' 10 | 11 | jobs: 12 | analyze: 13 | name: Analyze 14 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} 15 | timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} 16 | permissions: 17 | actions: read 18 | contents: read 19 | security-events: write 20 | 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | language: [ 'java-kotlin' ] 25 | 26 | steps: 27 | - name: Checkout repository 28 | uses: actions/checkout@v6 29 | 30 | - name: Setup Java 31 | uses: actions/setup-java@v5 32 | with: 33 | cache: maven 34 | distribution: 'temurin' 35 | java-version: 21 36 | 37 | - name: Initialize CodeQL 38 | uses: github/codeql-action/init@v4 39 | with: 40 | languages: ${{ matrix.language }} 41 | queries: +security-and-quality 42 | 43 | - name: Autobuild 44 | uses: github/codeql-action/autobuild@v4 45 | 46 | - name: Perform CodeQL Analysis 47 | uses: github/codeql-action/analyze@v4 48 | with: 49 | category: "/language:${{ matrix.language }}" 50 | -------------------------------------------------------------------------------- /src/test/java/org/mybatis/scripting/thymeleaf/integrationtest/domain/Mail.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf.integrationtest.domain; 17 | 18 | public class Mail { 19 | private int id; 20 | private int personId; 21 | private String address; 22 | 23 | public int getId() { 24 | return id; 25 | } 26 | 27 | public void setId(int id) { 28 | this.id = id; 29 | } 30 | 31 | public int getPersonId() { 32 | return personId; 33 | } 34 | 35 | public void setPersonId(int personId) { 36 | this.personId = personId; 37 | } 38 | 39 | public String getAddress() { 40 | return address; 41 | } 42 | 43 | public void setAddress(String address) { 44 | this.address = address; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/org/mybatis/scripting/thymeleaf/integrationtest/domain/Person.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf.integrationtest.domain; 17 | 18 | import java.util.List; 19 | 20 | public class Person { 21 | private int id; 22 | private String name; 23 | private List mails; 24 | 25 | public int getId() { 26 | return id; 27 | } 28 | 29 | public void setId(int id) { 30 | this.id = id; 31 | } 32 | 33 | public String getName() { 34 | return name; 35 | } 36 | 37 | public void setName(String name) { 38 | this.name = name; 39 | } 40 | 41 | public List getMails() { 42 | return mails; 43 | } 44 | 45 | public void setMails(List mails) { 46 | this.mails = mails; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/org/mybatis/scripting/thymeleaf/integrationtest/domain/Name.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf.integrationtest.domain; 17 | 18 | public class Name { 19 | private int id; 20 | private String firstName; 21 | private String lastName; 22 | 23 | public String getFirstName() { 24 | return firstName; 25 | } 26 | 27 | public void setFirstName(String firstName) { 28 | this.firstName = firstName; 29 | } 30 | 31 | public String getLastName() { 32 | return lastName; 33 | } 34 | 35 | public void setLastName(String lastName) { 36 | this.lastName = lastName; 37 | } 38 | 39 | public int getId() { 40 | return id; 41 | } 42 | 43 | public void setId(int id) { 44 | this.id = id; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/org/mybatis/scripting/thymeleaf/integrationtest/mapper/OneWayPersonMapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2024 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf.integrationtest.mapper; 17 | 18 | import java.util.List; 19 | 20 | import org.apache.ibatis.annotations.Select; 21 | import org.mybatis.scripting.thymeleaf.integrationtest.domain.Mail; 22 | 23 | public interface OneWayPersonMapper { 24 | 25 | @Select("sql/OneWayPersonMapper/selectMailsByConditionsArray.sql") 26 | List selectMailsByConditionsArray(List list); 27 | 28 | class Conditions { 29 | private List mails; 30 | 31 | public List getMails() { 32 | return mails; 33 | } 34 | 35 | public void setMails(List mails) { 36 | this.mails = mails; 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/test/resources/mybatis-thymeleaf-custom.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2018-2022 the original author or authors. 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 | # https://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 | use2way=false 18 | customizer=org.mybatis.scripting.thymeleaf.CustomTemplateEngineCustomizer 19 | template-file.cache-enabled=false 20 | template-file.cache-ttl=30000 21 | template-file.encoding=ISO-8859-1 22 | template-file.base-dir=templates/ 23 | template-file.patterns= *.sql , *.sql.template 24 | template-file.path-provider.prefix=sqls/ 25 | template-file.path-provider.includes-package-path=false 26 | template-file.path-provider.separate-directory-per-mapper=false 27 | template-file.path-provider.includes-mapper-name-when-separate-directory=false 28 | template-file.path-provider.cache-enabled=false 29 | dialect.prefix=mybatis 30 | dialect.like-escape-char=~ 31 | dialect.like-escape-clause-format=escape '%s' 32 | dialect.like-additional-escape-target-chars=% , _ 33 | -------------------------------------------------------------------------------- /src/test/java/org/mybatis/scripting/thymeleaf/TemplateEngineCustomizerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf; 17 | 18 | import org.apache.ibatis.session.Configuration; 19 | import org.apache.ibatis.session.SqlSessionFactoryBuilder; 20 | import org.junit.jupiter.api.Assertions; 21 | import org.junit.jupiter.api.Test; 22 | import org.thymeleaf.TemplateEngine; 23 | import org.thymeleaf.templateresolver.FileTemplateResolver; 24 | 25 | class TemplateEngineCustomizerTest { 26 | 27 | @Test 28 | void testNoMatchType() { 29 | Configuration configuration = new Configuration(); 30 | configuration.setDefaultScriptingLanguage(ThymeleafLanguageDriver.class); 31 | new SqlSessionFactoryBuilder().build(configuration); 32 | 33 | TemplateEngine templateEngine = DefaultTemplateEngineCustomizer.templateEngine; 34 | Assertions.assertFalse( 35 | TemplateEngineCustomizer.extractTemplateResolver(templateEngine, FileTemplateResolver.class).isPresent()); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/org/mybatis/scripting/thymeleaf/integrationtest/mapper/XmlNameMapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2024 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf.integrationtest.mapper; 17 | 18 | import java.util.List; 19 | 20 | import org.apache.ibatis.annotations.Param; 21 | import org.mybatis.scripting.thymeleaf.integrationtest.domain.Name; 22 | 23 | public interface XmlNameMapper { 24 | void insert(Name name); 25 | 26 | void update(Name name); 27 | 28 | void delete(Name name); 29 | 30 | List getAllNames(); 31 | 32 | List findByIds(@Param("ids") List ids); 33 | 34 | List findByIdsWithoutParamAnnotation(List ids); 35 | 36 | List findById(@Param("id") Integer id); 37 | 38 | List findByIdWithoutParamAnnotation(Integer id); 39 | 40 | List findByIdWithNestedParam(@Param("p") NameParam param); 41 | 42 | List findUsingScript(NameParam nameParam); 43 | 44 | List findUsingTemplateFile(NameParam nameParam); 45 | 46 | List findByName(NameParam param); 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/org/mybatis/scripting/thymeleaf/integrationtest/mapper/TemplateFilePathProviderMapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf.integrationtest.mapper; 17 | 18 | import org.apache.ibatis.annotations.DeleteProvider; 19 | import org.apache.ibatis.annotations.InsertProvider; 20 | import org.apache.ibatis.annotations.Options; 21 | import org.apache.ibatis.annotations.SelectProvider; 22 | import org.apache.ibatis.annotations.UpdateProvider; 23 | import org.mybatis.scripting.thymeleaf.integrationtest.domain.Name; 24 | import org.mybatis.scripting.thymeleaf.support.TemplateFilePathProvider; 25 | 26 | public interface TemplateFilePathProviderMapper { 27 | 28 | @Options(useGeneratedKeys = true, keyProperty = "id") 29 | @InsertProvider(type = TemplateFilePathProvider.class) 30 | void insert(Name name); 31 | 32 | @UpdateProvider(type = TemplateFilePathProvider.class) 33 | void update(Name name); 34 | 35 | @DeleteProvider(type = TemplateFilePathProvider.class) 36 | void delete(Name name); 37 | 38 | @SelectProvider(type = TemplateFilePathProvider.class) 39 | Name findById(Integer id); 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/org/mybatis/scripting/thymeleaf/integrationtest/mapper/NameParam.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf.integrationtest.mapper; 17 | 18 | import java.util.List; 19 | 20 | public class NameParam { 21 | private int id; 22 | 23 | private String firstName; 24 | 25 | private String lastName; 26 | 27 | private List ids; 28 | 29 | public NameParam() { 30 | } 31 | 32 | public NameParam(int id) { 33 | this.id = id; 34 | } 35 | 36 | public int getId() { 37 | return id; 38 | } 39 | 40 | public void setId(int id) { 41 | this.id = id; 42 | } 43 | 44 | public String getFirstName() { 45 | return firstName; 46 | } 47 | 48 | public void setFirstName(String firstName) { 49 | this.firstName = firstName; 50 | } 51 | 52 | public String getLastName() { 53 | return lastName; 54 | } 55 | 56 | public void setLastName(String lastName) { 57 | this.lastName = lastName; 58 | } 59 | 60 | public List getIds() { 61 | return ids; 62 | } 63 | 64 | public void setIds(List ids) { 65 | this.ids = ids; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | 21 | 22 | 23 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%level{5}] %-48logger{48} : %msg%n 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /.mvn/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 21 | 22 | 23 | 24 | 25 | central 26 | ${env.CI_DEPLOY_USERNAME} 27 | ${env.CI_DEPLOY_PASSWORD} 28 | 29 | 30 | 31 | 32 | gh-pages-scm 33 | 34 | branch 35 | gh-pages 36 | 37 | 38 | 39 | 40 | 41 | github 42 | ${env.GITHUB_TOKEN} 43 | 44 | 45 | 46 | 47 | nvd 48 | ${env.NVD_API_KEY} 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/test/resources/mapper-config.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /src/test/resources/create-db.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright 2018-2022 the original author or authors. 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 | -- https://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 | drop table if exists names; 18 | drop table if exists names2; 19 | drop table if exists persons; 20 | drop table if exists person_mails; 21 | 22 | create table names ( 23 | id int generated by default as identity, 24 | firstName varchar(20), 25 | lastName varchar(20) 26 | ); 27 | 28 | create table names2 ( 29 | id int generated by default as identity, 30 | firstName varchar(20), 31 | lastName varchar(20) 32 | ); 33 | 34 | create table persons ( 35 | id int generated by default as identity, 36 | name varchar(40) 37 | ); 38 | 39 | create table person_mails ( 40 | id int generated by default as identity, 41 | person_id int, 42 | address varchar(256) 43 | ); 44 | 45 | insert into names (id, firstName, lastName) values(1, 'Fred', 'Flintstone'); 46 | insert into names (id, firstName, lastName) values(2, 'Wilma', 'Flintstone'); 47 | insert into names (id, firstName, lastName) values(3, 'Pebbles', 'Flintstone'); 48 | insert into names (id, firstName, lastName) values(4, 'Barney', 'Rubble'); 49 | insert into names (id, firstName, lastName) values(5, 'Betty', 'Rubble'); 50 | insert into names (id, firstName, lastName) values(6, 'Be%ty', 'Ab_le'); 51 | insert into names (id, firstName, lastName) values(7, 'Be\ty', 'Abble'); 52 | 53 | insert into names2 (id, firstName, lastName) values(1, 'MyBatis', 'Thymeleaf'); 54 | 55 | insert into persons (id, name) values(100, 'Name 1'); 56 | insert into persons (id, name) values(101, 'Name 2'); 57 | 58 | insert into person_mails (id, person_id, address) values(1000, 100, 'mybatis1.main@test.net'); 59 | insert into person_mails (id, person_id, address) values(1001, 100, 'mybatis1.sub@test.net'); 60 | insert into person_mails (id, person_id, address) values(1002, 101, 'mybatis2.main@test.net'); 61 | insert into person_mails (id, person_id, address) values(1003, 101, 'mybatis2.sub@test.net'); 62 | -------------------------------------------------------------------------------- /src/test/java/org/mybatis/scripting/thymeleaf/integrationtest/mapper/OneWayNameMapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf.integrationtest.mapper; 17 | 18 | import java.util.List; 19 | 20 | import org.apache.ibatis.annotations.*; 21 | import org.mybatis.scripting.thymeleaf.integrationtest.domain.Name; 22 | 23 | public interface OneWayNameMapper { 24 | 25 | @Options(useGeneratedKeys = true, keyProperty = "id") 26 | @Insert("sql/OneWayNameMapper/insert.sql") 27 | void insert(Name name); 28 | 29 | @Options(useGeneratedKeys = true, keyProperty = "id") 30 | @Insert("sql/OneWayNameMapper/insertByBulk.sql") 31 | void insertByBulk(List names); 32 | 33 | @Update("sql/OneWayNameMapper/update.sql") 34 | void update(Name name); 35 | 36 | @Delete("sql/OneWayNameMapper/delete.sql") 37 | void delete(Name name); 38 | 39 | @Select("SELECT * FROM names") 40 | List getAllNames(); 41 | 42 | @Select("sql/OneWayNameMapper/findByIds.sql") 43 | List findByIds(@Param("ids") List ids); 44 | 45 | @Select("sql/OneWayNameMapper/findByIdsWithoutParamAnnotation.sql") 46 | List findByIdsWithoutParamAnnotation(List ids); 47 | 48 | @Select("sql/OneWayNameMapper/findById.sql") 49 | List findById(@Param("id") Integer id); 50 | 51 | @Select("sql/OneWayNameMapper/findByIdWithoutParamAnnotation.sql") 52 | List findByIdWithoutParamAnnotation(Integer id); 53 | 54 | @Select("sql/OneWayNameMapper/findByIdWithNestedParam.sql") 55 | List findByIdWithNestedParam(@Param("p") NameParam param); 56 | 57 | @Select({ "SELECT * FROM names", "[# th:insert=\"~{sql/OneWayNameMapper/findByIdWhere.sql}\" /]" }) 58 | List findUsingScript(NameParam nameParam); 59 | 60 | @Select("sql/OneWayNameMapper/findById.sql") 61 | List findUsingTemplateFile(NameParam nameParam); 62 | 63 | @Select("sql/OneWayNameMapper/findByName.sql") 64 | List findByName(NameParam param); 65 | } 66 | -------------------------------------------------------------------------------- /src/test/java/org/mybatis/scripting/thymeleaf/integrationtest/mapper/PersonMapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf.integrationtest.mapper; 17 | 18 | import java.util.List; 19 | 20 | import org.apache.ibatis.annotations.*; 21 | import org.apache.ibatis.mapping.FetchType; 22 | import org.mybatis.scripting.thymeleaf.integrationtest.domain.Mail; 23 | import org.mybatis.scripting.thymeleaf.integrationtest.domain.Person; 24 | 25 | public interface PersonMapper { 26 | 27 | @Options(useGeneratedKeys = true, keyProperty = "id") 28 | @Insert("sql/PersonMapper/insertByBulk.sql") 29 | void insertByBulk(List persons); 30 | 31 | @Options(useGeneratedKeys = true, keyProperty = "id") 32 | @Insert("sql/PersonMapper/insertByBulkWithIndexed.sql") 33 | void insertByBulkWithIndexed(List persons); 34 | 35 | @Insert("sql/PersonMapper/insertMailsByBulk.sql") 36 | void insertMailsByBulk(List persons); 37 | 38 | @Insert("sql/PersonMapper/insertMailsByBulkWithIndexed.sql") 39 | void insertMailsByBulkWithIndexed(List persons); 40 | 41 | @Select("SELECT MAX(id) FROM person_mails") 42 | Integer getMaxMailId(); 43 | 44 | @Select("SELECT id, name FROM persons WHERE id IN ([# mb:p='ids'/]) ORDER BY id") 45 | @Results({ @Result(property = "id", column = "id", id = true), 46 | @Result(property = "mails", column = "id", many = @Many(select = "selectPersonMails", fetchType = FetchType.EAGER)) }) 47 | List selectPersons(@Param("ids") int... ids); 48 | 49 | @Select("SELECT id, person_id, address FROM person_mails WHERE person_id = #{id} ORDER BY id") 50 | List selectPersonMails(int personId); 51 | 52 | @Select("SELECT id, person_id, address FROM person_mails WHERE address IN ([# mb:p='conditions.mails'/]) ORDER BY id") 53 | List selectMailsByConditions(@Param("conditions") Conditions conditions); 54 | 55 | @Select("sql/PersonMapper/selectMailsByConditionsArray.sql") 56 | List selectMailsByConditionsArray(List list); 57 | 58 | class Conditions { 59 | private List mails; 60 | 61 | public List getMails() { 62 | return mails; 63 | } 64 | 65 | public void setMails(List mails) { 66 | this.mails = mails; 67 | } 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/test/java/org/mybatis/scripting/thymeleaf/integrationtest/mapper/NameMapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf.integrationtest.mapper; 17 | 18 | import java.util.List; 19 | 20 | import org.apache.ibatis.annotations.*; 21 | import org.mybatis.scripting.thymeleaf.integrationtest.domain.Name; 22 | 23 | public interface NameMapper { 24 | 25 | @Options(useGeneratedKeys = true, keyProperty = "id") 26 | @Insert("sql/NameMapper/insert.sql") 27 | void insert(Name name); 28 | 29 | @Options(useGeneratedKeys = true, keyProperty = "id") 30 | @Insert("sql/NameMapper/insertByBulk.sql") 31 | void insertByBulk(List names); 32 | 33 | @Update("sql/NameMapper/update.sql") 34 | void update(Name name); 35 | 36 | @Delete("sql/NameMapper/delete.sql") 37 | void delete(Name name); 38 | 39 | @Select("SELECT * FROM names") 40 | List getAllNames(); 41 | 42 | @Select("sql/NameMapper/findByIds.sql") 43 | List findByIds(@Param("ids") int... ids); 44 | 45 | @Select("sql/NameMapper/findByFirstNames.sql") 46 | List findByFirstNames(@Param("firstNames") List firstNames); 47 | 48 | @Select("sql/NameMapper/findByFirstNames.sql") 49 | Name findByFirstNamesWithNotCollectionType(@Param("firstNames") String firstNames); 50 | 51 | @Select("sql/NameMapper/findByIdsWithoutParamAnnotation.sql") 52 | List findByIdsWithoutParamAnnotation(List ids); 53 | 54 | @Select("sql/NameMapper/findById.sql") 55 | List findById(@Param("id") Integer id); 56 | 57 | @Select("sql/NameMapper/findByIdWithoutParamAnnotation.sql") 58 | List findByIdWithoutParamAnnotation(Integer id); 59 | 60 | @Select("sql/NameMapper/findByIdWithNestedParam.sql") 61 | List findByIdWithNestedParam(@Param("p") NameParam param); 62 | 63 | @Select({ "SELECT * FROM names", "/*[# th:insert=\"~{sql/NameMapper/findByIdWhere.sql}\" /]*/" }) 64 | List findUsingScript(NameParam nameParam); 65 | 66 | @Select("sql/NameMapper/findById.sql") 67 | List findUsingTemplateFile(NameParam nameParam); 68 | 69 | @Select("sql/NameMapper/findByName.sql") 70 | List findByName(NameParam param); 71 | 72 | @Select("sql/NameMapper/findByIdsWithinParam.sql") 73 | List findByIdsWithinParam(NameParam param); 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/org/mybatis/scripting/thymeleaf/MyBatisIntegratingEngineContextFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf; 17 | 18 | import java.lang.reflect.Proxy; 19 | import java.util.Map; 20 | 21 | import org.thymeleaf.IEngineConfiguration; 22 | import org.thymeleaf.context.IContext; 23 | import org.thymeleaf.context.IEngineContext; 24 | import org.thymeleaf.context.IEngineContextFactory; 25 | import org.thymeleaf.engine.TemplateData; 26 | 27 | /** 28 | * The implementation of {@link IEngineContextFactory} for integrating with MyBatis. 29 | * 30 | * @author Kazuki Shimizu 31 | * 32 | * @version 1.0.0 33 | */ 34 | public class MyBatisIntegratingEngineContextFactory implements IEngineContextFactory { 35 | private final IEngineContextFactory delegate; 36 | private final ClassLoader classLoader = getClass().getClassLoader(); 37 | 38 | /** 39 | * Constructor. 40 | * 41 | * @param delegate 42 | * A target context factory for delegating 43 | */ 44 | public MyBatisIntegratingEngineContextFactory(IEngineContextFactory delegate) { 45 | this.delegate = delegate; 46 | } 47 | 48 | /** 49 | * {@inheritDoc} 50 | */ 51 | @Override 52 | public IEngineContext createEngineContext(IEngineConfiguration configuration, TemplateData templateData, 53 | Map templateResolutionAttributes, IContext context) { 54 | IEngineContext engineContext = delegate.createEngineContext(configuration, templateData, 55 | templateResolutionAttributes, context); 56 | return (IEngineContext) Proxy.newProxyInstance(classLoader, new Class[] { IEngineContext.class }, 57 | (proxy, method, args) -> { 58 | if (method.getName().equals("getVariable")) { 59 | String name = (String) args[0]; 60 | Object value; 61 | MyBatisBindingContext bindingContext = MyBatisBindingContext.load(engineContext); 62 | if (bindingContext.isFallbackParameterObject()) { 63 | value = engineContext.containsVariable(name) ? engineContext.getVariable(name) 64 | : engineContext.getVariable(SqlGenerator.ContextKeys.PARAMETER_OBJECT); 65 | } else { 66 | value = engineContext.getVariable(name); 67 | } 68 | return value; 69 | } 70 | return method.invoke(engineContext, args); 71 | }); 72 | } 73 | 74 | } -------------------------------------------------------------------------------- /src/test/resources/org/mybatis/scripting/thymeleaf/integrationtest/mapper/XmlNameMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 22 | 23 | 24 | 25 | sql/NameMapper/insert.sql 26 | 27 | 28 | 29 | sql/NameMapper/update.sql 30 | 31 | 32 | 33 | sql/NameMapper/delete.sql 34 | 35 | 36 | 39 | 40 | 43 | 44 | 47 | 48 | 51 | 52 | 55 | 56 | 59 | 60 | 64 | 65 | 68 | 69 | 72 | 73 | -------------------------------------------------------------------------------- /src/test/java/org/mybatis/scripting/thymeleaf/PropertyAccessorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf; 17 | 18 | import org.junit.jupiter.api.Assertions; 19 | import org.junit.jupiter.api.Test; 20 | 21 | class PropertyAccessorTest { 22 | 23 | @Test 24 | void propertyNotFoundWhenGetPropertyType() { 25 | IllegalArgumentException e = Assertions.assertThrows(IllegalArgumentException.class, 26 | () -> PropertyAccessor.BuiltIn.STANDARD.getPropertyType(SqlGeneratorConfig.class, "id")); 27 | Assertions.assertEquals( 28 | "Does not get a property type because property 'id' not found on 'org.mybatis.scripting.thymeleaf.SqlGeneratorConfig' class.", 29 | e.getMessage()); 30 | } 31 | 32 | @Test 33 | void propertyNotFoundWhenGetPropertyValue() { 34 | IllegalArgumentException e = Assertions.assertThrows(IllegalArgumentException.class, 35 | () -> PropertyAccessor.BuiltIn.STANDARD.getPropertyValue(new SqlGeneratorConfig(), "foo")); 36 | Assertions.assertEquals( 37 | "Does not get a property value because property 'foo' not found on 'org.mybatis.scripting.thymeleaf.SqlGeneratorConfig' class.", 38 | e.getMessage()); 39 | } 40 | 41 | @Test 42 | void propertyNotFoundWhenSetPropertyValue() { 43 | IllegalArgumentException e = Assertions.assertThrows(IllegalArgumentException.class, 44 | () -> PropertyAccessor.BuiltIn.STANDARD.setPropertyValue(new SqlGeneratorConfig(), "bar", "aaa")); 45 | Assertions.assertEquals( 46 | "Does not set a property value because property 'bar' not found on 'org.mybatis.scripting.thymeleaf.SqlGeneratorConfig' class.", 47 | e.getMessage()); 48 | } 49 | 50 | @Test 51 | void errorWhenGetPropertyValue() { 52 | IllegalStateException e = Assertions.assertThrows(IllegalStateException.class, 53 | () -> PropertyAccessor.BuiltIn.STANDARD.getPropertyValue(new Bean(), "id")); 54 | Assertions.assertEquals("java.lang.reflect.InvocationTargetException", e.getMessage()); 55 | } 56 | 57 | @Test 58 | void errorWhenSetPropertyValue() { 59 | IllegalStateException e = Assertions.assertThrows(IllegalStateException.class, 60 | () -> PropertyAccessor.BuiltIn.STANDARD.setPropertyValue(new Bean(), "id", 10)); 61 | Assertions.assertEquals("java.lang.reflect.InvocationTargetException", e.getMessage()); 62 | } 63 | 64 | static class Bean { 65 | private Integer id; 66 | 67 | public Integer getId() { 68 | throw new IllegalStateException("test"); 69 | } 70 | 71 | public void setId(Integer id) { 72 | throw new IllegalStateException("test"); 73 | } 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/test/java/org/mybatis/scripting/thymeleaf/SqlGeneratorConfigTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf; 17 | 18 | import java.nio.charset.Charset; 19 | import java.nio.charset.StandardCharsets; 20 | import java.nio.charset.UnsupportedCharsetException; 21 | import java.util.Properties; 22 | 23 | import org.junit.jupiter.api.Assertions; 24 | import org.junit.jupiter.api.Test; 25 | import org.mybatis.scripting.thymeleaf.support.spring.SpringNamedParameterBindVariableRender; 26 | 27 | class SqlGeneratorConfigTest { 28 | 29 | @Test 30 | void newInstanceWithResourcePath() { 31 | SqlGeneratorConfig config = SqlGeneratorConfig 32 | .newInstanceWithResourcePath("mybatis-thymeleaf-custom-without-path-provider.properties"); 33 | Assertions.assertEquals(StandardCharsets.ISO_8859_1, config.getTemplateFile().getEncoding()); 34 | Assertions.assertEquals(SpringNamedParameterBindVariableRender.class, config.getDialect().getBindVariableRender()); 35 | } 36 | 37 | @Test 38 | void newInstanceWithProperties() { 39 | Properties properties = new Properties(); 40 | properties.setProperty("template-file.encoding", "Windows-31J"); 41 | SqlGeneratorConfig config = SqlGeneratorConfig.newInstanceWithProperties(properties); 42 | Assertions.assertEquals(Charset.forName("Windows-31J"), config.getTemplateFile().getEncoding()); 43 | } 44 | 45 | @Test 46 | void invalidKey() { 47 | Properties properties = new Properties(); 48 | properties.setProperty("template-file.encodings", "Windows-31J"); 49 | 50 | IllegalArgumentException e = Assertions.assertThrows(IllegalArgumentException.class, 51 | () -> SqlGeneratorConfig.newInstanceWithProperties(properties)); 52 | Assertions.assertEquals("Detected an invalid property. key='template-file.encodings' value='Windows-31J'", 53 | e.getMessage()); 54 | Assertions.assertEquals(IllegalArgumentException.class, e.getCause().getClass()); 55 | Assertions.assertEquals( 56 | "Does not get a property type because property 'encodings' not found on 'org.mybatis.scripting.thymeleaf.SqlGeneratorConfig$TemplateFileConfig' class.", 57 | e.getCause().getMessage()); 58 | } 59 | 60 | @Test 61 | void invalidValue() { 62 | Properties properties = new Properties(); 63 | properties.setProperty("template-file.encoding", "UTF-77"); 64 | 65 | IllegalArgumentException e = Assertions.assertThrows(IllegalArgumentException.class, 66 | () -> SqlGeneratorConfig.newInstanceWithProperties(properties)); 67 | Assertions.assertEquals("Detected an invalid property. key='template-file.encoding' value='UTF-77'", 68 | e.getMessage()); 69 | Assertions.assertEquals(UnsupportedCharsetException.class, e.getCause().getClass()); 70 | Assertions.assertEquals("UTF-77", e.getCause().getMessage()); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/org/mybatis/scripting/thymeleaf/processor/BindVariableRender.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf.processor; 17 | 18 | import java.util.function.UnaryOperator; 19 | 20 | import org.mybatis.scripting.thymeleaf.support.spring.SpringNamedParameterBindVariableRender; 21 | 22 | /** 23 | * The interface for rendering a bind variable.
24 | * If you want to customize a default {@code BindVariableRender}, you implements class of this interface and you need to 25 | * specify the 'dialect.bind-variable-render' property of mybatis-thymeleaf.properties.
26 | *
27 | * e.g.) Implementation class: 28 | * 29 | *
 30 |  * package com.example;
 31 |  *
 32 |  * // ...
 33 |  * public class R2dbcMySQLBindVariableRender extends EnclosingBasedBindVariableRender {
 34 |  *   public R2dbcMySQLBindVariableRender() {
 35 |  *     super("?", ""); // Render '?...' (e.g. ?id)
 36 |  *   }
 37 |  * }
 38 |  * 
39 | * 40 | *
41 | * e.g.) Configuration file (mybatis-thymeleaf.properties): 42 | * 43 | *
 44 |  * dialect.bind-variable-render = com.example.MyBindVariableRender
 45 |  * 
46 | * 47 | * @author Kazuki Shimizu 48 | * 49 | * @version 1.0.2 50 | */ 51 | @FunctionalInterface 52 | public interface BindVariableRender extends UnaryOperator { 53 | 54 | /** 55 | * {@inheritDoc} 56 | * 57 | * @see #render(String) 58 | */ 59 | @Override 60 | default String apply(String name) { 61 | return render(name); 62 | } 63 | 64 | /** 65 | * Render a bind variable. 66 | * 67 | * @param name 68 | * a bind variable name 69 | * 70 | * @return a bind variable 71 | */ 72 | String render(String name); 73 | 74 | /** 75 | * The built-in bind variable renders. 76 | */ 77 | enum BuiltIn implements BindVariableRender { 78 | 79 | /** 80 | * The render for MyBatis core named parameter format(.e.g {@literal #{id}}). 81 | *

82 | * This is default. 83 | *

84 | */ 85 | MYBATIS(name -> "#{" + name + "}"), 86 | /** 87 | * The render for Spring JDBC named parameter format(.e.g {@literal :id}). 88 | */ 89 | SPRING_NAMED_PARAMETER(new SpringNamedParameterBindVariableRender()); 90 | 91 | private final BindVariableRender delegate; 92 | 93 | BuiltIn(BindVariableRender delegate) { 94 | this.delegate = delegate; 95 | } 96 | 97 | /** 98 | * {@inheritDoc} 99 | */ 100 | @Override 101 | public String render(String name) { 102 | return delegate.render(name); 103 | } 104 | 105 | /** 106 | * Get a type of the actual {@link BindVariableRender}. 107 | * 108 | * @return a type of delegating {@link BindVariableRender} 109 | */ 110 | public Class getType() { 111 | return delegate.getClass(); 112 | } 113 | 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/org/mybatis/scripting/thymeleaf/ThymeleafLanguageDriver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf; 17 | 18 | import org.apache.ibatis.executor.parameter.ParameterHandler; 19 | import org.apache.ibatis.mapping.BoundSql; 20 | import org.apache.ibatis.mapping.MappedStatement; 21 | import org.apache.ibatis.mapping.SqlSource; 22 | import org.apache.ibatis.parsing.XNode; 23 | import org.apache.ibatis.scripting.LanguageDriver; 24 | import org.apache.ibatis.scripting.defaults.DefaultParameterHandler; 25 | import org.apache.ibatis.session.Configuration; 26 | import org.mybatis.scripting.thymeleaf.support.TemplateFilePathProvider; 27 | import org.thymeleaf.ITemplateEngine; 28 | 29 | /** 30 | * The {@code LanguageDriver} for integrating with Thymeleaf. 31 | * 32 | * @author Kazuki Shimizu 33 | * 34 | * @version 1.0.0 35 | */ 36 | public class ThymeleafLanguageDriver implements LanguageDriver { 37 | 38 | private final SqlGenerator sqlGenerator; 39 | 40 | /** 41 | * Constructor for creating instance with default {@code TemplateEngine}. 42 | */ 43 | public ThymeleafLanguageDriver() { 44 | this.sqlGenerator = configure(new SqlGenerator(ThymeleafLanguageDriverConfig.newInstance())); 45 | } 46 | 47 | /** 48 | * Constructor for creating instance with user specified {@code Properties}. 49 | * 50 | * @param config 51 | * A user defined {@code ITemplateEngine} instance 52 | */ 53 | public ThymeleafLanguageDriver(ThymeleafLanguageDriverConfig config) { 54 | this.sqlGenerator = configure(new SqlGenerator(config)); 55 | TemplateFilePathProvider.setLanguageDriverConfig(config); 56 | } 57 | 58 | /** 59 | * Constructor for creating instance with user defined {@code ITemplateEngine}. 60 | * 61 | * @param templateEngine 62 | * A user defined {@code ITemplateEngine} instance 63 | */ 64 | public ThymeleafLanguageDriver(ITemplateEngine templateEngine) { 65 | this.sqlGenerator = configure(new SqlGenerator(templateEngine)); 66 | } 67 | 68 | private SqlGenerator configure(SqlGenerator sqlGenerator) { 69 | sqlGenerator.setContextFactory(new ThymeleafSqlSource.ContextFactory()); 70 | return sqlGenerator; 71 | } 72 | 73 | /** 74 | * {@inheritDoc} 75 | */ 76 | @Override 77 | public ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, 78 | BoundSql boundSql) { 79 | return new DefaultParameterHandler(mappedStatement, parameterObject, boundSql); 80 | } 81 | 82 | /** 83 | * {@inheritDoc} 84 | */ 85 | @Override 86 | public SqlSource createSqlSource(Configuration configuration, XNode script, Class parameterType) { 87 | return createSqlSource(configuration, script.getNode().getTextContent(), parameterType); 88 | } 89 | 90 | /** 91 | * {@inheritDoc} 92 | */ 93 | @Override 94 | public SqlSource createSqlSource(Configuration configuration, String script, Class parameterType) { 95 | return new ThymeleafSqlSource(configuration, sqlGenerator, script.trim(), parameterType); 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/test/java/org/mybatis/scripting/thymeleaf/MyBatisDialectTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf; 17 | 18 | import java.io.Reader; 19 | import java.sql.Connection; 20 | 21 | import org.apache.ibatis.annotations.Select; 22 | import org.apache.ibatis.io.Resources; 23 | import org.apache.ibatis.jdbc.ScriptRunner; 24 | import org.apache.ibatis.mapping.Environment; 25 | import org.apache.ibatis.session.Configuration; 26 | import org.apache.ibatis.session.SqlSession; 27 | import org.apache.ibatis.session.SqlSessionFactory; 28 | import org.apache.ibatis.session.SqlSessionFactoryBuilder; 29 | import org.apache.ibatis.transaction.TransactionFactory; 30 | import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; 31 | import org.hsqldb.jdbc.JDBCDataSource; 32 | import org.junit.jupiter.api.Assertions; 33 | import org.junit.jupiter.api.BeforeAll; 34 | import org.junit.jupiter.api.Test; 35 | import org.mybatis.scripting.thymeleaf.integrationtest.domain.Name; 36 | 37 | class MyBatisDialectTest { 38 | 39 | private static SqlSessionFactory sqlSessionFactory; 40 | 41 | @BeforeAll 42 | static void setUp() throws Exception { 43 | Class.forName("org.hsqldb.jdbcDriver"); 44 | JDBCDataSource dataSource = new JDBCDataSource(); 45 | dataSource.setUrl("jdbc:hsqldb:mem:dialect-db"); 46 | dataSource.setUser("sa"); 47 | dataSource.setPassword(""); 48 | 49 | TransactionFactory transactionFactory = new JdbcTransactionFactory(); 50 | Environment environment = new Environment("development", transactionFactory, dataSource); 51 | 52 | Configuration configuration = new Configuration(environment); 53 | configuration.getLanguageRegistry().register(new ThymeleafLanguageDriver( 54 | ThymeleafLanguageDriverConfig.newInstance(c -> c.getDialect().setPrefix("mybatis")))); 55 | configuration.setDefaultScriptingLanguage(ThymeleafLanguageDriver.class); 56 | configuration.getMapperRegistry().addMapper(Mapper.class); 57 | sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration); 58 | 59 | try (Connection conn = sqlSessionFactory.getConfiguration().getEnvironment().getDataSource().getConnection()) { 60 | try (Reader reader = Resources.getResourceAsReader("create-db.sql")) { 61 | ScriptRunner runner = new ScriptRunner(conn); 62 | runner.setLogWriter(null); 63 | runner.setErrorLogWriter(null); 64 | runner.runScript(reader); 65 | conn.commit(); 66 | } 67 | } 68 | } 69 | 70 | @Test 71 | void testCustomDialectPrefix() { 72 | try (SqlSession sqlSession = sqlSessionFactory.openSession()) { 73 | Mapper mapper = sqlSession.getMapper(Mapper.class); 74 | Name name = mapper.select(1); 75 | Assertions.assertEquals("Fred", name.getFirstName()); 76 | } 77 | } 78 | 79 | @Test 80 | void testDefaultConstructor() { 81 | MyBatisDialect dialect = new MyBatisDialect(); 82 | Assertions.assertEquals("mb", dialect.getPrefix()); 83 | } 84 | 85 | interface Mapper { 86 | @Select("SELECT * FROM names WHERE id = /*[# mybatis:p='id']*/ 1000 /*[/]*/") 87 | Name select(int id); 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/org/mybatis/scripting/thymeleaf/TemplateEngineCustomizer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf; 17 | 18 | import java.util.Optional; 19 | import java.util.function.Consumer; 20 | 21 | import org.thymeleaf.TemplateEngine; 22 | import org.thymeleaf.templateresolver.ITemplateResolver; 23 | 24 | /** 25 | * The interface for customizing a default {@code TemplateEngine} instanced by the MyBatis Thymeleaf.
26 | * If you want to customize a default {@code TemplateEngine}, you implements class of this interface and you need to 27 | * specify the 'customizer' property of mybatis-thymeleaf.properties.
28 | *
29 | * e.g.) Implementation class: 30 | * 31 | *
 32 |  * package com.example;
 33 |  *
 34 |  * // ...
 35 |  * public class MyTemplateEngineCustomizer implements TemplateEngineCustomizer {
 36 |  *   public void customize(TemplateEngine defaultTemplateEngine) {
 37 |  *     // ...
 38 |  *   }
 39 |  * }
 40 |  * 
41 | * 42 | *
43 | * e.g.) Configuration file (mybatis-thymeleaf.properties): 44 | * 45 | *
 46 |  * customizer = com.example.MyTemplateEngineCustomizer
 47 |  * 
48 | * 49 | * @author Kazuki Shimizu 50 | * 51 | * @version 1.0.0 52 | */ 53 | @FunctionalInterface 54 | public interface TemplateEngineCustomizer extends Consumer { 55 | 56 | /** 57 | * {@inheritDoc} 58 | * 59 | * @see #customize(TemplateEngine) 60 | */ 61 | @Override 62 | default void accept(TemplateEngine templateEngine) { 63 | customize(templateEngine); 64 | } 65 | 66 | /** 67 | * Customize a default {@code TemplateEngine} instanced by the MyBatis Thymeleaf. 68 | * 69 | * @param defaultTemplateEngine 70 | * a default {@code TemplateEngine} instanced by the MyBatis Thymeleaf 71 | */ 72 | void customize(TemplateEngine defaultTemplateEngine); 73 | 74 | /** 75 | * Utility method to extract a {@code ITemplateResolver} that implements specified type. 76 | * 77 | * @param templateEngine 78 | * A target {@code TemplateEngine} 79 | * @param type 80 | * A target type for extracting instance 81 | * @param 82 | * A type that implements the {@code ITemplateResolver} 83 | * 84 | * @return A {@code ITemplateResolver} instance that implements specified type 85 | */ 86 | static Optional extractTemplateResolver(TemplateEngine templateEngine, 87 | Class type) { 88 | return templateEngine.getTemplateResolvers().stream().filter(type::isInstance).map(type::cast).findFirst(); 89 | } 90 | 91 | /** 92 | * Enum for providing builtin customizer instance. 93 | * 94 | * @since 1.0.0 95 | */ 96 | enum BuiltIn implements TemplateEngineCustomizer { 97 | 98 | /** 99 | * Default instance. 100 | *

101 | * This customizer instance do nothing. 102 | *

103 | */ 104 | DO_NOTHING { 105 | @Override 106 | public void customize(TemplateEngine defaultTemplateEngine) { 107 | // NOP 108 | } 109 | } 110 | 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/org/mybatis/scripting/thymeleaf/processor/MyBatisBindTagProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf.processor; 17 | 18 | import java.util.List; 19 | import java.util.Objects; 20 | 21 | import org.mybatis.scripting.thymeleaf.MyBatisBindingContext; 22 | import org.thymeleaf.context.ITemplateContext; 23 | import org.thymeleaf.engine.AttributeName; 24 | import org.thymeleaf.exceptions.TemplateProcessingException; 25 | import org.thymeleaf.model.IProcessableElementTag; 26 | import org.thymeleaf.processor.element.AbstractAttributeTagProcessor; 27 | import org.thymeleaf.processor.element.IElementTagStructureHandler; 28 | import org.thymeleaf.standard.expression.Assignation; 29 | import org.thymeleaf.standard.expression.AssignationSequence; 30 | import org.thymeleaf.standard.expression.AssignationUtils; 31 | import org.thymeleaf.standard.expression.IStandardExpression; 32 | import org.thymeleaf.templatemode.TemplateMode; 33 | import org.thymeleaf.util.StringUtils; 34 | 35 | /** 36 | * The processor class for handling the {@code mybatis:bind} tag.
37 | * This processor register an any value to the MyBatis’s bind variables (similar to that of the {@code } provided 38 | * by MyBatis core module) This class's implementation was inspired with the {@code StandardWithTagProcessor} provide by 39 | * Thymeleaf. 40 | * 41 | * @author Kazuki Shimizu 42 | * 43 | * @version 1.0.0 44 | * 45 | * @see org.thymeleaf.standard.processor.StandardWithTagProcessor 46 | */ 47 | public class MyBatisBindTagProcessor extends AbstractAttributeTagProcessor { 48 | 49 | private static final int PRECEDENCE = 600; 50 | private static final String ATTR_NAME = "bind"; 51 | 52 | /** 53 | * Constructor that can be specified the template mode and dialect prefix. 54 | * 55 | * @param templateMode 56 | * A target template mode 57 | * @param prefix 58 | * A target dialect prefix 59 | */ 60 | public MyBatisBindTagProcessor(final TemplateMode templateMode, final String prefix) { 61 | super(templateMode, prefix, null, false, ATTR_NAME, true, PRECEDENCE, true); 62 | } 63 | 64 | /** 65 | * {@inheritDoc} 66 | */ 67 | @Override 68 | protected void doProcess(ITemplateContext context, IProcessableElementTag tag, AttributeName attributeName, 69 | String attributeValue, IElementTagStructureHandler structureHandler) { 70 | AssignationSequence assignations = AssignationUtils.parseAssignationSequence(context, attributeValue, false); 71 | 72 | List assignationValues = assignations.getAssignations(); 73 | assignationValues.forEach(assignation -> { 74 | IStandardExpression nameExp = assignation.getLeft(); 75 | Object name = nameExp.execute(context); 76 | IStandardExpression valueExp = assignation.getRight(); 77 | Object value = valueExp.execute(context); 78 | 79 | if (Objects.isNull(name) || StringUtils.isEmpty(name.toString())) { 80 | throw new TemplateProcessingException( 81 | "Variable name expression evaluated as null or empty: \"" + nameExp + "\""); 82 | } 83 | 84 | MyBatisBindingContext bindingContext = MyBatisBindingContext.load(context); 85 | bindingContext.setCustomBindVariable(name.toString(), value); 86 | }); 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/test/java/org/mybatis/scripting/thymeleaf/ThymeleafSqlSourceTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf; 17 | 18 | import java.io.Reader; 19 | import java.sql.Connection; 20 | import java.util.List; 21 | import java.util.Properties; 22 | 23 | import org.apache.ibatis.exceptions.PersistenceException; 24 | import org.apache.ibatis.io.Resources; 25 | import org.apache.ibatis.jdbc.ScriptRunner; 26 | import org.apache.ibatis.session.SqlSession; 27 | import org.apache.ibatis.session.SqlSessionFactory; 28 | import org.apache.ibatis.session.SqlSessionFactoryBuilder; 29 | import org.junit.jupiter.api.Assertions; 30 | import org.junit.jupiter.api.BeforeAll; 31 | import org.junit.jupiter.api.Test; 32 | import org.mybatis.scripting.thymeleaf.integrationtest.domain.Name; 33 | import org.mybatis.scripting.thymeleaf.integrationtest.mapper.InvalidNameParam; 34 | 35 | class ThymeleafSqlSourceTest { 36 | 37 | private static SqlSessionFactory sqlSessionFactory; 38 | 39 | @BeforeAll 40 | static void setUp() throws Exception { 41 | 42 | try (Reader reader = Resources.getResourceAsReader("mapper-config.xml")) { 43 | sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); 44 | } 45 | sqlSessionFactory.getConfiguration().setVariables(new Properties()); 46 | 47 | try (Connection conn = sqlSessionFactory.getConfiguration().getEnvironment().getDataSource().getConnection()) { 48 | try (Reader reader = Resources.getResourceAsReader("create-db.sql")) { 49 | ScriptRunner runner = new ScriptRunner(conn); 50 | runner.setLogWriter(null); 51 | runner.setErrorLogWriter(null); 52 | runner.runScript(reader); 53 | conn.commit(); 54 | } 55 | } 56 | } 57 | 58 | @Test 59 | void testErrorOnGetterMethod() { 60 | try (SqlSession sqlSession = sqlSessionFactory.openSession()) { 61 | try { 62 | sqlSession.selectList( 63 | "org.mybatis.scripting.thymeleaf.integrationtest.mapper.XmlNameSqlSessionMapper.findUsing_parameter", 64 | new InvalidNameParam(4)); 65 | Assertions.fail(); 66 | } catch (PersistenceException e) { 67 | Assertions.assertEquals( 68 | "Cannot get a value for property named 'id' in 'class org.mybatis.scripting.thymeleaf.integrationtest.mapper.InvalidNameParam'", 69 | e.getCause().getCause().getMessage()); 70 | } 71 | } 72 | } 73 | 74 | @Test 75 | void testConfigurationProperties() { 76 | try (SqlSession sqlSession = sqlSessionFactory.openSession()) { 77 | sqlSessionFactory.getConfiguration().getVariables().setProperty("tableNameOfNames", "names2"); 78 | List list = sqlSession.selectList( 79 | "org.mybatis.scripting.thymeleaf.integrationtest.mapper.XmlNameSqlSessionMapper.findAllFormSpecifiedTable"); 80 | Assertions.assertEquals(1, list.size()); 81 | } finally { 82 | sqlSessionFactory.getConfiguration().getVariables().remove("tableNameOfNames"); 83 | } 84 | try (SqlSession sqlSession = sqlSessionFactory.openSession()) { 85 | List list = sqlSession.selectList( 86 | "org.mybatis.scripting.thymeleaf.integrationtest.mapper.XmlNameSqlSessionMapper.findAllFormSpecifiedTable"); 87 | Assertions.assertEquals(7, list.size()); 88 | } 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * https://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.net.Authenticator; 23 | import java.net.PasswordAuthentication; 24 | import java.net.URI; 25 | import java.net.URL; 26 | import java.nio.file.Files; 27 | import java.nio.file.Path; 28 | import java.nio.file.Paths; 29 | import java.nio.file.StandardCopyOption; 30 | import java.util.concurrent.ThreadLocalRandom; 31 | 32 | public final class MavenWrapperDownloader { 33 | private static final String WRAPPER_VERSION = "3.3.2"; 34 | 35 | private static final boolean VERBOSE = Boolean.parseBoolean(System.getenv("MVNW_VERBOSE")); 36 | 37 | public static void main(String[] args) { 38 | log("Apache Maven Wrapper Downloader " + WRAPPER_VERSION); 39 | 40 | if (args.length != 2) { 41 | System.err.println(" - ERROR wrapperUrl or wrapperJarPath parameter missing"); 42 | System.exit(1); 43 | } 44 | 45 | try { 46 | log(" - Downloader started"); 47 | final URL wrapperUrl = URI.create(args[0]).toURL(); 48 | final String jarPath = args[1].replace("..", ""); // Sanitize path 49 | final Path wrapperJarPath = Paths.get(jarPath).toAbsolutePath().normalize(); 50 | downloadFileFromURL(wrapperUrl, wrapperJarPath); 51 | log("Done"); 52 | } catch (IOException e) { 53 | System.err.println("- Error downloading: " + e.getMessage()); 54 | if (VERBOSE) { 55 | e.printStackTrace(); 56 | } 57 | System.exit(1); 58 | } 59 | } 60 | 61 | private static void downloadFileFromURL(URL wrapperUrl, Path wrapperJarPath) 62 | throws IOException { 63 | log(" - Downloading to: " + wrapperJarPath); 64 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 65 | final String username = System.getenv("MVNW_USERNAME"); 66 | final char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 67 | Authenticator.setDefault(new Authenticator() { 68 | @Override 69 | protected PasswordAuthentication getPasswordAuthentication() { 70 | return new PasswordAuthentication(username, password); 71 | } 72 | }); 73 | } 74 | Path temp = wrapperJarPath 75 | .getParent() 76 | .resolve(wrapperJarPath.getFileName() + "." 77 | + Long.toUnsignedString(ThreadLocalRandom.current().nextLong()) + ".tmp"); 78 | try (InputStream inStream = wrapperUrl.openStream()) { 79 | Files.copy(inStream, temp, StandardCopyOption.REPLACE_EXISTING); 80 | Files.move(temp, wrapperJarPath, StandardCopyOption.REPLACE_EXISTING); 81 | } finally { 82 | Files.deleteIfExists(temp); 83 | } 84 | log(" - Downloader complete"); 85 | } 86 | 87 | private static void log(String msg) { 88 | if (VERBOSE) { 89 | System.out.println(msg); 90 | } 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MyBatis Thymeleaf 3 Support 2 | 3 | [![Java CI](https://github.com/mybatis/thymeleaf-scripting/actions/workflows/ci.yaml/badge.svg)](https://github.com/mybatis/thymeleaf-scripting/actions/workflows/ci.yaml) 4 | [![Coverage Status](https://coveralls.io/repos/github/mybatis/thymeleaf-scripting/badge.svg?branch=master)](https://coveralls.io/github/mybatis/thymeleaf-scripting?branch=master) 5 | [![Maven central](https://maven-badges.herokuapp.com/maven-central/org.mybatis.scripting/mybatis-thymeleaf/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.mybatis.scripting/mybatis-thymeleaf) 6 | [![Sonatype Nexus (Snapshots)](https://img.shields.io/nexus/s/https/oss.sonatype.org/org.mybatis.scripting/mybatis-thymeleaf.svg)](https://oss.sonatype.org/content/repositories/snapshots/org/mybatis/scripting/mybatis-thymeleaf/) 7 | [![License](https://img.shields.io/:license-apache-brightgreen.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) 8 | 9 | ![thymeleaf-scripting](https://mybatis.org/images/mybatis-logo.png) 10 | 11 | MyBatis Thymeleaf 3 Scripting Support. 12 | 13 | ## Introduction 14 | 15 | The mybatis-thymeleaf is a plugin that helps applying a SQL using template provided by Thymeleaf 3. 16 | If you are not familiar with Thymeleaf 3 syntax, you can see the Thymeleaf documentations. 17 | 18 | * https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#textual-template-modes 19 | 20 | ### Simple bindable 2-way SQL 21 | 22 | ```sql 23 | SELECT * FROM names 24 | WHERE id = /*[# mb:p="id"]*/ 1 /*[/]*/ 25 | ``` 26 | 27 | ### Dynamic bindable 2-way SQL 28 | 29 | ```sql 30 | SELECT * FROM names 31 | WHERE 1 = 1 32 | /*[# th:if="${not #lists.isEmpty(ids)}"]*/ 33 | AND id IN ( 34 | /*[# mb:p="ids"]*/ 1 /*[/]*/ 35 | ) 36 | /*[/]*/ 37 | ORDER BY id 38 | ``` 39 | 40 | ### Dynamic bindable SQL(non 2-way) 41 | 42 | ```sql 43 | SELECT * FROM names 44 | WHERE 1 = 1 45 | [# th:if="${not #lists.isEmpty(ids)}"] 46 | AND id IN ( 47 | [# mb:p="ids" /] 48 | ) 49 | [/] 50 | ORDER BY id 51 | ``` 52 | 53 | ## Requirements 54 | 55 | * Java 8, Java 11+ 56 | * MyBatis 3.4.3+ (Recommend to use 3.5+ or 3.4.x latest version) 57 | * Thymeleaf 3.0+ (Recommend to use 3.0.x latest version) 58 | 59 | ## Documentation 60 | 61 | * [Published User's Guide](https://www.mybatis.org/thymeleaf-scripting/user-guide.html) 62 | 63 | 64 | * [Snapshot User's Guide](src/main/asciidoc/user-guide.adoc) 65 | 66 | 67 | ## Related Resources 68 | 69 | * [Quick Start](https://github.com/mybatis/thymeleaf-scripting/wiki/Quick-Start) 70 | * [Code completion](https://github.com/mybatis/thymeleaf-scripting/wiki/Code-completion) 71 | * [Usage on framework](https://github.com/mybatis/thymeleaf-scripting/wiki/Usage-on-framework) 72 | 73 | 74 | ## Contact us 75 | 76 | ### Question 77 | 78 | When there is a question, at first please confirm whether exists same question at following web sites. 79 | 80 | * [Google group for mybatis user(official mailing list)](https://groups.google.com/forum/#!forum/mybatis-user) 81 | * [Stack Overflow tagged mybatis](https://stackoverflow.com/questions/tagged/mybatis) 82 | 83 | If you cannot find a same question, please post new question to the [mailing list](https://groups.google.com/forum/#!newtopic/mybatis-user) or the [Stack Overflow](https://stackoverflow.com/questions/ask?tags=mybatis,mybatis-thymeleaf). 84 | 85 | ### Report and Request 86 | 87 | When you found a bug or want to submit a feature request(new feature or current feature improvement), at first please confirm whether exists same bug or request at following pages. 88 | 89 | * [Issue List](https://github.com/mybatis/thymeleaf-scripting/issues) 90 | * [Pull Request List](https://github.com/mybatis/thymeleaf-scripting/pulls) 91 | 92 | If you cannot find a same report or request, please post new issue to the [issue tracker](https://github.com/mybatis/thymeleaf-scripting/issues/new). 93 | 94 | > **IMPORTANT:** 95 | > 96 | > When you found a security vulnerability, at first please report it to the [mybatis organization members](https://github.com/orgs/mybatis/people) instead of issue tracker. 97 | -------------------------------------------------------------------------------- /src/test/resources/org/mybatis/scripting/thymeleaf/integrationtest/mapper/XmlNameSqlSessionMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 22 | 23 | 24 | 25 | INSERT INTO names (firstName, lastName) 26 | VALUES ( /*[# mb:p='firstName']*/ 'Taro' /*[/]*/, /*[# mb:p='lastName']*/ 'Yamada' /*[/]*/) 27 | 28 | 29 | 30 | 41 | 42 | 43 | 44 | DELETE FROM names 45 | WHERE id = /*[# mb:p='id']*/ 1 /*[/]*/ 46 | 47 | 48 | 51 | 52 | 58 | 59 | 66 | 67 | 73 | 74 | 77 | 78 | 79 | /*[# th:if='${not #lists.isEmpty(list)}']*/ 80 | AND id IN ( /*[# mb:p='list']*/ 1 /*[/]*/ ) 81 | /*[/]*/ 82 | 83 | 84 | 88 | 89 | 92 | 93 | 105 | 106 | 110 | 111 | 114 | 115 | -------------------------------------------------------------------------------- /src/main/java/org/mybatis/scripting/thymeleaf/MyBatisBindingContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf; 17 | 18 | import java.util.ArrayList; 19 | import java.util.HashMap; 20 | import java.util.List; 21 | import java.util.Map; 22 | 23 | import org.thymeleaf.context.IContext; 24 | import org.thymeleaf.engine.IterationStatusVar; 25 | 26 | /** 27 | * The context object for integrating with MyBatis and Thymeleaf template engine. 28 | * 29 | * @author Kazuki Shimizu 30 | * 31 | * @version 1.0.0 32 | */ 33 | public class MyBatisBindingContext { 34 | 35 | static final String CONTEXT_VARIABLE_NAME = "_" + MyBatisBindingContext.class.getName(); 36 | 37 | private final Map customBindVariables = new HashMap<>(); 38 | private final IterationStatusManager iterationStatusManager = new IterationStatusManager(); 39 | private final boolean fallbackParameterObject; 40 | 41 | /** 42 | * Load instance from {@link IContext} provided by Thymeleaf. 43 | * 44 | * @param context 45 | * a context of thymeleaf template processing 46 | * 47 | * @return instance of this class 48 | */ 49 | public static MyBatisBindingContext load(IContext context) { 50 | return (MyBatisBindingContext) context.getVariable(CONTEXT_VARIABLE_NAME); 51 | } 52 | 53 | /** 54 | * Constructor. 55 | * 56 | * @param fallbackParameterObject 57 | * whether use fallback parameter object when parameter is value object 58 | */ 59 | MyBatisBindingContext(boolean fallbackParameterObject) { 60 | this.fallbackParameterObject = fallbackParameterObject; 61 | } 62 | 63 | /** 64 | * Get custom bind variables. 65 | * 66 | * @return custom bind variables 67 | */ 68 | Map getCustomBindVariables() { 69 | return customBindVariables; 70 | } 71 | 72 | /** 73 | * Set a value into custom bind variable. 74 | * 75 | * @param name 76 | * variable name 77 | * @param value 78 | * variable value 79 | */ 80 | public void setCustomBindVariable(String name, Object value) { 81 | customBindVariables.put(name, value); 82 | } 83 | 84 | /** 85 | * Return whether contains specified variable into custom bind variables. 86 | * 87 | * @param name 88 | * variable name 89 | * 90 | * @return If specified variable exists, return {@code true} 91 | */ 92 | public boolean containsCustomBindVariable(String name) { 93 | return customBindVariables.containsKey(name); 94 | } 95 | 96 | /** 97 | * Generate an unique variable name per iteration object.
98 | * Variable name rule is {@code {objectName}_{status list index}_{status.getIndex()}}. 99 | * 100 | * @param objectName 101 | * base object name 102 | * @param status 103 | * iteration status object 104 | * 105 | * @return an unique variable name per iteration object 106 | */ 107 | public String generateUniqueName(String objectName, IterationStatusVar status) { 108 | return iterationStatusManager.generateUniqueName(objectName, status); 109 | } 110 | 111 | /** 112 | * Return whether use fallback parameter object when parameter is value object. 113 | * 114 | * @return If use fallback parameter object, return {@code true} 115 | */ 116 | boolean isFallbackParameterObject() { 117 | return fallbackParameterObject; 118 | } 119 | 120 | private static class IterationStatusManager { 121 | 122 | private final Map> statusListMapping = new HashMap<>(); 123 | 124 | private String generateUniqueName(String objectName, IterationStatusVar status) { 125 | List statusList = statusListMapping.computeIfAbsent(objectName, k -> new ArrayList<>()); 126 | int index; 127 | if (!statusList.contains(status)) { 128 | index = statusList.size(); 129 | statusList.add(status); 130 | } else { 131 | index = statusList.indexOf(status); 132 | } 133 | return objectName + "_" + index + "_" + status.getIndex(); 134 | } 135 | 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /src/main/java/org/mybatis/scripting/thymeleaf/MyBatisDialect.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf; 17 | 18 | import java.util.Arrays; 19 | import java.util.Collections; 20 | import java.util.HashSet; 21 | import java.util.Optional; 22 | import java.util.Set; 23 | 24 | import org.mybatis.scripting.thymeleaf.expression.Likes; 25 | import org.mybatis.scripting.thymeleaf.processor.BindVariableRender; 26 | import org.mybatis.scripting.thymeleaf.processor.MyBatisBindTagProcessor; 27 | import org.mybatis.scripting.thymeleaf.processor.MyBatisParamTagProcessor; 28 | import org.thymeleaf.context.IExpressionContext; 29 | import org.thymeleaf.dialect.AbstractProcessorDialect; 30 | import org.thymeleaf.dialect.IExpressionObjectDialect; 31 | import org.thymeleaf.expression.IExpressionObjectFactory; 32 | import org.thymeleaf.processor.IProcessor; 33 | import org.thymeleaf.standard.StandardDialect; 34 | import org.thymeleaf.templatemode.TemplateMode; 35 | 36 | /** 37 | * The Dialect for integrating with MyBatis.
38 | * This dialect provides following features. This dialect prefix is {@code "mb"} by default. 39 | *
    40 | *
  • {@code #likes} expression : {@link Likes}
  • 41 | *
  • {@code mb:p} attribute tag: {@link MyBatisParamTagProcessor}
  • 42 | *
  • {@code mb:bind} attribute tag : {@link MyBatisBindTagProcessor}
  • 43 | *
44 | * 45 | * @author Kazuki Shimizu 46 | * 47 | * @version 1.0.0 48 | */ 49 | public class MyBatisDialect extends AbstractProcessorDialect implements IExpressionObjectDialect { 50 | 51 | private static final String DEFAULT_PREFIX = "mb"; 52 | 53 | private Likes likes = Likes.newBuilder().build(); 54 | 55 | private BindVariableRender bindVariableRender; 56 | 57 | /** 58 | * Default constructor. 59 | */ 60 | public MyBatisDialect() { 61 | this(DEFAULT_PREFIX); 62 | } 63 | 64 | /** 65 | * Constructor that can be specified the dialect prefix. 66 | * 67 | * @param prefix 68 | * A dialect prefix 69 | */ 70 | public MyBatisDialect(String prefix) { 71 | super("MyBatis Dialect", prefix, StandardDialect.PROCESSOR_PRECEDENCE); 72 | } 73 | 74 | /** 75 | * Set an expression utility object that provide helper method for like feature.
76 | * 77 | * @param likes 78 | * An expression utility object that provide helper method for like feature 79 | */ 80 | public void setLikes(Likes likes) { 81 | this.likes = likes; 82 | } 83 | 84 | /** 85 | * Set a bind variable render. 86 | * 87 | * @param bindVariableRender 88 | * a bind variable render 89 | * 90 | * @since 1.0.2 91 | */ 92 | public void setBindVariableRender(BindVariableRender bindVariableRender) { 93 | this.bindVariableRender = bindVariableRender; 94 | } 95 | 96 | /** 97 | * {@inheritDoc} 98 | */ 99 | @Override 100 | public Set getProcessors(String dialectPrefix) { 101 | return new HashSet<>(Arrays.asList(new MyBatisBindTagProcessor(TemplateMode.TEXT, dialectPrefix), 102 | new MyBatisBindTagProcessor(TemplateMode.CSS, dialectPrefix), 103 | configure(new MyBatisParamTagProcessor(TemplateMode.TEXT, dialectPrefix)), 104 | configure(new MyBatisParamTagProcessor(TemplateMode.CSS, dialectPrefix)))); 105 | } 106 | 107 | private MyBatisParamTagProcessor configure(MyBatisParamTagProcessor processor) { 108 | Optional.ofNullable(bindVariableRender).ifPresent(processor::setBindVariableRender); 109 | return processor; 110 | } 111 | 112 | /** 113 | * {@inheritDoc} 114 | */ 115 | @Override 116 | public IExpressionObjectFactory getExpressionObjectFactory() { 117 | return new MyBatisExpressionObjectFactory(); 118 | } 119 | 120 | private class MyBatisExpressionObjectFactory implements IExpressionObjectFactory { 121 | 122 | /** 123 | * {@inheritDoc} 124 | */ 125 | @Override 126 | public Set getAllExpressionObjectNames() { 127 | return Collections.singleton("likes"); 128 | } 129 | 130 | /** 131 | * {@inheritDoc} 132 | */ 133 | @Override 134 | public Object buildObject(IExpressionContext context, String expressionObjectName) { 135 | return likes; 136 | } 137 | 138 | /** 139 | * {@inheritDoc} 140 | */ 141 | @Override 142 | public boolean isCacheable(String expressionObjectName) { 143 | return true; 144 | } 145 | 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /src/main/java/org/mybatis/scripting/thymeleaf/expression/Likes.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf.expression; 17 | 18 | import java.util.Arrays; 19 | import java.util.Collections; 20 | import java.util.Optional; 21 | import java.util.Set; 22 | import java.util.function.Function; 23 | import java.util.stream.Collectors; 24 | 25 | /** 26 | * The expression utility object that provide helper method for like feature.
27 | * This object can be access using {@code #likes}) as expression utility object. 28 | * 29 | * @author Kazuki Shimizu 30 | * 31 | * @version 1.0.0 32 | */ 33 | public class Likes { 34 | 35 | private char escapeChar = '\\'; 36 | 37 | private Set additionalEscapeTargetChars = Collections.emptySet(); 38 | 39 | private Function escapeClauseSupplier = targetEscapeChar -> "ESCAPE '" + targetEscapeChar + "'"; 40 | 41 | /** 42 | * Construct new instance that corresponds with specified configuration. 43 | */ 44 | private Likes() { 45 | // NOP 46 | } 47 | 48 | /** 49 | * Escape for LIKE condition value.
50 | * By default configuration, this method escape the {@code "%"} and {@code "_"} using {@code "\"}. 51 | * 52 | * @param value 53 | * A target condition value 54 | * 55 | * @return A escaped value 56 | */ 57 | public String escapeWildcard(String value) { 58 | if (value == null || value.isEmpty()) { 59 | return ""; 60 | } 61 | StringBuilder sb = new StringBuilder(value.length() + 16); 62 | for (char c : value.toCharArray()) { 63 | if (c == escapeChar || c == '%' || c == '_' || additionalEscapeTargetChars.contains(c)) { 64 | sb.append(escapeChar); 65 | } 66 | sb.append(c); 67 | } 68 | return sb.toString(); 69 | } 70 | 71 | /** 72 | * Return a escape clause string of LIKE.
73 | * By default configuration, this method return {@code "ESCAPE '\'"}. 74 | * 75 | * @return A escape clause string of LIKE 76 | */ 77 | public String escapeClause() { 78 | return escapeClauseSupplier.apply(escapeChar); 79 | } 80 | 81 | /** 82 | * Creates a new builder instance for {@link Likes}. 83 | * 84 | * @return a new builder instance 85 | */ 86 | public static Builder newBuilder() { 87 | return new Builder(); 88 | } 89 | 90 | /** 91 | * The builder class for {@link Likes}. 92 | */ 93 | public static class Builder { 94 | 95 | private final Likes instance = new Likes(); 96 | 97 | private Builder() { 98 | // NOP 99 | } 100 | 101 | /** 102 | * Set an escape character for wildcard of LIKE.
103 | * The default value is {@code '\'} (backslash) 104 | * 105 | * @param escapeChar 106 | * A escape character 107 | * 108 | * @return A self instance 109 | */ 110 | public Builder escapeChar(Character escapeChar) { 111 | Optional.ofNullable(escapeChar).ifPresent(v -> instance.escapeChar = v); 112 | return this; 113 | } 114 | 115 | /** 116 | * Set additional escape target characters(custom wildcard characters) for LIKE condition.
117 | * The default value is nothing. 118 | * 119 | * @param additionalEscapeTargetChars 120 | * escape target characters(custom wildcard characters) 121 | * 122 | * @return A self instance 123 | */ 124 | public Builder additionalEscapeTargetChars(Character... additionalEscapeTargetChars) { 125 | Optional.ofNullable(additionalEscapeTargetChars) 126 | .ifPresent(v -> instance.additionalEscapeTargetChars = Arrays.stream(v).collect(Collectors.toSet())); 127 | return this; 128 | } 129 | 130 | /** 131 | * Set a format of escape clause.
132 | * The default value is {@code "ESCAPE '%s'"}. 133 | * 134 | * @param escapeClauseFormat 135 | * a format of escape clause 136 | * 137 | * @return A self instance 138 | */ 139 | public Builder escapeClauseFormat(String escapeClauseFormat) { 140 | Optional.ofNullable(escapeClauseFormat) 141 | .ifPresent(v -> instance.escapeClauseSupplier = escapeChar -> String.format(v, escapeChar)); 142 | return this; 143 | } 144 | 145 | /** 146 | * Return a {@link Likes} instance . 147 | * 148 | * @return A {@link Likes} instance corresponding with specified option 149 | */ 150 | public Likes build() { 151 | return instance; 152 | } 153 | 154 | } 155 | 156 | } 157 | -------------------------------------------------------------------------------- /src/test/java/org/mybatis/scripting/thymeleaf/integrationtest/TemplateFilePathProviderMapperNoCacheTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf.integrationtest; 17 | 18 | import java.io.Reader; 19 | import java.sql.Connection; 20 | 21 | import org.apache.ibatis.io.Resources; 22 | import org.apache.ibatis.jdbc.ScriptRunner; 23 | import org.apache.ibatis.mapping.Environment; 24 | import org.apache.ibatis.session.Configuration; 25 | import org.apache.ibatis.session.SqlSession; 26 | import org.apache.ibatis.session.SqlSessionFactory; 27 | import org.apache.ibatis.session.SqlSessionFactoryBuilder; 28 | import org.apache.ibatis.transaction.TransactionFactory; 29 | import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; 30 | import org.hsqldb.jdbc.JDBCDataSource; 31 | import org.junit.jupiter.api.Assertions; 32 | import org.junit.jupiter.api.BeforeAll; 33 | import org.junit.jupiter.api.Test; 34 | import org.junit.jupiter.api.condition.DisabledIfSystemProperty; 35 | import org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriver; 36 | import org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriverConfig; 37 | import org.mybatis.scripting.thymeleaf.integrationtest.domain.Name; 38 | import org.mybatis.scripting.thymeleaf.integrationtest.mapper.TemplateFilePathProviderMapper; 39 | 40 | @DisabledIfSystemProperty(named = "mybatis.version", matches = "3\\.4\\..*|3\\.5\\.0") 41 | class TemplateFilePathProviderMapperNoCacheTest { 42 | private static SqlSessionFactory sqlSessionFactory; 43 | 44 | @BeforeAll 45 | static void setUp() throws Exception { 46 | Class.forName("org.hsqldb.jdbcDriver"); 47 | JDBCDataSource dataSource = new JDBCDataSource(); 48 | dataSource.setUrl("jdbc:hsqldb:mem:db1"); 49 | dataSource.setUser("sa"); 50 | dataSource.setPassword(""); 51 | 52 | try (Connection conn = dataSource.getConnection()) { 53 | try (Reader reader = Resources.getResourceAsReader("create-db.sql")) { 54 | ScriptRunner runner = new ScriptRunner(conn); 55 | runner.setLogWriter(null); 56 | runner.setErrorLogWriter(null); 57 | runner.runScript(reader); 58 | conn.commit(); 59 | } 60 | } 61 | 62 | TransactionFactory transactionFactory = new JdbcTransactionFactory(); 63 | Environment environment = new Environment("development", transactionFactory, dataSource); 64 | 65 | Configuration configuration = new Configuration(environment); 66 | configuration.setMapUnderscoreToCamelCase(true); 67 | configuration.getLanguageRegistry() 68 | .register(new ThymeleafLanguageDriver(ThymeleafLanguageDriverConfig.newInstance(c -> { 69 | c.getTemplateFile().getPathProvider().setPrefix("sql/"); 70 | c.getTemplateFile().getPathProvider().setIncludesPackagePath(false); 71 | c.getTemplateFile().getPathProvider().setCacheEnabled(false); 72 | }))); 73 | configuration.setDefaultScriptingLanguage(ThymeleafLanguageDriver.class); 74 | 75 | configuration.addMapper(TemplateFilePathProviderMapper.class); 76 | sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration); 77 | } 78 | 79 | @Test 80 | void testInsert() { 81 | try (SqlSession sqlSession = sqlSessionFactory.openSession()) { 82 | TemplateFilePathProviderMapper mapper = sqlSession.getMapper(TemplateFilePathProviderMapper.class); 83 | Name name = new Name(); 84 | name.setFirstName("Thymeleaf"); 85 | name.setLastName("MyBatis"); 86 | mapper.insert(name); 87 | 88 | Name loadedName = mapper.findById(name.getId()); 89 | Assertions.assertEquals(name.getFirstName(), loadedName.getFirstName()); 90 | Assertions.assertEquals(name.getLastName(), loadedName.getLastName()); 91 | } 92 | } 93 | 94 | @Test 95 | void testUpdate() { 96 | try (SqlSession sqlSession = sqlSessionFactory.openSession()) { 97 | TemplateFilePathProviderMapper mapper = sqlSession.getMapper(TemplateFilePathProviderMapper.class); 98 | Name name = new Name(); 99 | name.setFirstName("Thymeleaf"); 100 | name.setLastName("MyBatis"); 101 | mapper.insert(name); 102 | 103 | Name updatingName = new Name(); 104 | updatingName.setId(name.getId()); 105 | updatingName.setFirstName("Thymeleaf3"); 106 | mapper.update(updatingName); 107 | 108 | Name loadedName = mapper.findById(name.getId()); 109 | Assertions.assertEquals(updatingName.getFirstName(), loadedName.getFirstName()); 110 | Assertions.assertEquals(name.getLastName(), loadedName.getLastName()); 111 | } 112 | } 113 | 114 | @Test 115 | void testDelete() { 116 | try (SqlSession sqlSession = sqlSessionFactory.openSession()) { 117 | TemplateFilePathProviderMapper mapper = sqlSession.getMapper(TemplateFilePathProviderMapper.class); 118 | Name name = new Name(); 119 | name.setFirstName("Thymeleaf"); 120 | name.setLastName("MyBatis"); 121 | mapper.insert(name); 122 | 123 | mapper.delete(name); 124 | 125 | Name loadedName = mapper.findById(name.getId()); 126 | Assertions.assertNull(loadedName); 127 | } 128 | } 129 | 130 | } 131 | -------------------------------------------------------------------------------- /src/test/java/org/mybatis/scripting/thymeleaf/integrationtest/TemplateFilePathProviderMapperTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf.integrationtest; 17 | 18 | import java.io.Reader; 19 | import java.sql.Connection; 20 | 21 | import org.apache.ibatis.io.Resources; 22 | import org.apache.ibatis.jdbc.ScriptRunner; 23 | import org.apache.ibatis.mapping.Environment; 24 | import org.apache.ibatis.session.Configuration; 25 | import org.apache.ibatis.session.SqlSession; 26 | import org.apache.ibatis.session.SqlSessionFactory; 27 | import org.apache.ibatis.session.SqlSessionFactoryBuilder; 28 | import org.apache.ibatis.transaction.TransactionFactory; 29 | import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; 30 | import org.hsqldb.jdbc.JDBCDataSource; 31 | import org.junit.jupiter.api.AfterAll; 32 | import org.junit.jupiter.api.Assertions; 33 | import org.junit.jupiter.api.BeforeAll; 34 | import org.junit.jupiter.api.Test; 35 | import org.junit.jupiter.api.condition.DisabledIfSystemProperty; 36 | import org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriver; 37 | import org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriverConfig; 38 | import org.mybatis.scripting.thymeleaf.integrationtest.domain.Name; 39 | import org.mybatis.scripting.thymeleaf.integrationtest.mapper.TemplateFilePathProviderMapper; 40 | import org.mybatis.scripting.thymeleaf.support.TemplateFilePathProvider; 41 | 42 | @DisabledIfSystemProperty(named = "mybatis.version", matches = "3\\.4\\..*|3\\.5\\.0") 43 | class TemplateFilePathProviderMapperTest { 44 | private static SqlSessionFactory sqlSessionFactory; 45 | 46 | @BeforeAll 47 | @AfterAll 48 | static void cleanup() { 49 | TemplateFilePathProvider.clearCache(); 50 | } 51 | 52 | @BeforeAll 53 | static void setUp() throws Exception { 54 | Class.forName("org.hsqldb.jdbcDriver"); 55 | JDBCDataSource dataSource = new JDBCDataSource(); 56 | dataSource.setUrl("jdbc:hsqldb:mem:db1"); 57 | dataSource.setUser("sa"); 58 | dataSource.setPassword(""); 59 | 60 | try (Connection conn = dataSource.getConnection()) { 61 | try (Reader reader = Resources.getResourceAsReader("create-db.sql")) { 62 | ScriptRunner runner = new ScriptRunner(conn); 63 | runner.setLogWriter(null); 64 | runner.setErrorLogWriter(null); 65 | runner.runScript(reader); 66 | conn.commit(); 67 | } 68 | } 69 | 70 | TransactionFactory transactionFactory = new JdbcTransactionFactory(); 71 | Environment environment = new Environment("development", transactionFactory, dataSource); 72 | 73 | Configuration configuration = new Configuration(environment); 74 | configuration.setMapUnderscoreToCamelCase(true); 75 | configuration.getLanguageRegistry() 76 | .register(new ThymeleafLanguageDriver(ThymeleafLanguageDriverConfig.newInstance(c -> { 77 | c.getTemplateFile().getPathProvider().setPrefix("sql/"); 78 | c.getTemplateFile().getPathProvider().setIncludesPackagePath(false); 79 | }))); 80 | configuration.setDefaultScriptingLanguage(ThymeleafLanguageDriver.class); 81 | 82 | configuration.addMapper(TemplateFilePathProviderMapper.class); 83 | sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration); 84 | } 85 | 86 | @Test 87 | void testInsert() { 88 | try (SqlSession sqlSession = sqlSessionFactory.openSession()) { 89 | TemplateFilePathProviderMapper mapper = sqlSession.getMapper(TemplateFilePathProviderMapper.class); 90 | Name name = new Name(); 91 | name.setFirstName("Thymeleaf"); 92 | name.setLastName("MyBatis"); 93 | mapper.insert(name); 94 | 95 | Name loadedName = mapper.findById(name.getId()); 96 | Assertions.assertEquals(name.getFirstName(), loadedName.getFirstName()); 97 | Assertions.assertEquals(name.getLastName(), loadedName.getLastName()); 98 | } 99 | } 100 | 101 | @Test 102 | void testUpdate() { 103 | try (SqlSession sqlSession = sqlSessionFactory.openSession()) { 104 | TemplateFilePathProviderMapper mapper = sqlSession.getMapper(TemplateFilePathProviderMapper.class); 105 | Name name = new Name(); 106 | name.setFirstName("Thymeleaf"); 107 | name.setLastName("MyBatis"); 108 | mapper.insert(name); 109 | 110 | Name updatingName = new Name(); 111 | updatingName.setId(name.getId()); 112 | updatingName.setFirstName("Thymeleaf3"); 113 | mapper.update(updatingName); 114 | 115 | Name loadedName = mapper.findById(name.getId()); 116 | Assertions.assertEquals(updatingName.getFirstName(), loadedName.getFirstName()); 117 | Assertions.assertEquals(name.getLastName(), loadedName.getLastName()); 118 | } 119 | } 120 | 121 | @Test 122 | void testDelete() { 123 | try (SqlSession sqlSession = sqlSessionFactory.openSession()) { 124 | TemplateFilePathProviderMapper mapper = sqlSession.getMapper(TemplateFilePathProviderMapper.class); 125 | Name name = new Name(); 126 | name.setFirstName("Thymeleaf"); 127 | name.setLastName("MyBatis"); 128 | mapper.insert(name); 129 | 130 | mapper.delete(name); 131 | 132 | Name loadedName = mapper.findById(name.getId()); 133 | Assertions.assertNull(loadedName); 134 | } 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/org/mybatis/scripting/thymeleaf/PropertyAccessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf; 17 | 18 | import java.beans.BeanInfo; 19 | import java.beans.IntrospectionException; 20 | import java.beans.Introspector; 21 | import java.beans.PropertyDescriptor; 22 | import java.lang.reflect.InvocationTargetException; 23 | import java.util.Map; 24 | import java.util.Optional; 25 | import java.util.Set; 26 | import java.util.concurrent.ConcurrentHashMap; 27 | import java.util.stream.Collectors; 28 | import java.util.stream.Stream; 29 | 30 | /** 31 | * The interface for accessing a property.
32 | * If you want to customize a default {@code PropertyAccessor}, you implements class of this interface and you need to 33 | * specify to a {@link SqlGenerator}.
34 | * 35 | * @author Kazuki Shimizu 36 | * 37 | * @version 1.0.2 38 | */ 39 | public interface PropertyAccessor { 40 | 41 | /** 42 | * Get property names of specified type. 43 | * 44 | * @param type 45 | * a target type 46 | * 47 | * @return property names 48 | */ 49 | Set getPropertyNames(Class type); 50 | 51 | /** 52 | * Get a property type of specified property. 53 | * 54 | * @param type 55 | * a target type 56 | * @param name 57 | * a property name 58 | * 59 | * @return a property type 60 | */ 61 | Class getPropertyType(Class type, String name); 62 | 63 | /** 64 | * Get a property value from specified target object. 65 | * 66 | * @param target 67 | * a target object 68 | * @param name 69 | * a property name 70 | * 71 | * @return a property value 72 | */ 73 | Object getPropertyValue(Object target, String name); 74 | 75 | /** 76 | * Set a property value to the specified target object. 77 | * 78 | * @param target 79 | * a target object 80 | * @param name 81 | * a property name 82 | * @param value 83 | * a property value 84 | */ 85 | void setPropertyValue(Object target, String name, Object value); 86 | 87 | /** 88 | * The built-in property accessors. 89 | */ 90 | enum BuiltIn implements PropertyAccessor { 91 | 92 | /** 93 | * The implementation using Java Beans API provided by JDK. 94 | */ 95 | STANDARD(new StandardPropertyAccessor()); 96 | 97 | private final PropertyAccessor delegate; 98 | 99 | BuiltIn(PropertyAccessor delegate) { 100 | this.delegate = delegate; 101 | } 102 | 103 | /** 104 | * {@inheritDoc} 105 | */ 106 | @Override 107 | public Set getPropertyNames(Class type) { 108 | return delegate.getPropertyNames(type); 109 | } 110 | 111 | /** 112 | * {@inheritDoc} 113 | */ 114 | @Override 115 | public Class getPropertyType(Class type, String name) { 116 | return delegate.getPropertyType(type, name); 117 | } 118 | 119 | /** 120 | * {@inheritDoc} 121 | */ 122 | @Override 123 | public Object getPropertyValue(Object target, String name) { 124 | return delegate.getPropertyValue(target, name); 125 | } 126 | 127 | /** 128 | * {@inheritDoc} 129 | */ 130 | @Override 131 | public void setPropertyValue(Object target, String name, Object value) { 132 | delegate.setPropertyValue(target, name, value); 133 | } 134 | 135 | static class StandardPropertyAccessor implements PropertyAccessor { 136 | 137 | private static Map, Map> cache = new ConcurrentHashMap<>(); 138 | 139 | /** 140 | * {@inheritDoc} 141 | */ 142 | @Override 143 | public Set getPropertyNames(Class type) { 144 | return getPropertyDescriptors(type).keySet(); 145 | } 146 | 147 | /** 148 | * {@inheritDoc} 149 | */ 150 | @Override 151 | public Class getPropertyType(Class type, String name) { 152 | return Optional.ofNullable(getPropertyDescriptors(type).get(name)) 153 | .orElseThrow(() -> new IllegalArgumentException(String.format( 154 | "Does not get a property type because property '%s' not found on '%s' class.", name, type.getName()))) 155 | .getPropertyType(); 156 | } 157 | 158 | /** 159 | * {@inheritDoc} 160 | */ 161 | @Override 162 | public Object getPropertyValue(Object target, String name) { 163 | try { 164 | return Optional.ofNullable(getPropertyDescriptors(target.getClass()).get(name)) 165 | .map(PropertyDescriptor::getReadMethod) 166 | .orElseThrow(() -> new IllegalArgumentException( 167 | String.format("Does not get a property value because property '%s' not found on '%s' class.", name, 168 | target.getClass().getName()))) 169 | .invoke(target); 170 | } catch (IllegalAccessException | InvocationTargetException e) { 171 | throw new IllegalStateException(e); 172 | } 173 | } 174 | 175 | /** 176 | * {@inheritDoc} 177 | */ 178 | @Override 179 | public void setPropertyValue(Object target, String name, Object value) { 180 | try { 181 | Optional.ofNullable(getPropertyDescriptors(target.getClass()).get(name)) 182 | .map(PropertyDescriptor::getWriteMethod) 183 | .orElseThrow(() -> new IllegalArgumentException( 184 | String.format("Does not set a property value because property '%s' not found on '%s' class.", name, 185 | target.getClass().getName()))) 186 | .invoke(target, value); 187 | } catch (IllegalAccessException | InvocationTargetException e) { 188 | throw new IllegalStateException(e); 189 | } 190 | } 191 | 192 | /** 193 | * Clear cache. 194 | *

195 | * This method use by internal processing. 196 | *

197 | */ 198 | static void clearCache() { 199 | cache.clear(); 200 | } 201 | 202 | private static Map getPropertyDescriptors(Class type) { 203 | return cache.computeIfAbsent(type, key -> { 204 | try { 205 | BeanInfo beanInfo = Introspector.getBeanInfo(type); 206 | return Stream.of(beanInfo.getPropertyDescriptors()).filter(x -> !x.getName().equals("class")) 207 | .collect(Collectors.toMap(PropertyDescriptor::getName, v -> v)); 208 | } catch (IntrospectionException e) { 209 | throw new IllegalStateException(e); 210 | } finally { 211 | Introspector.flushFromCaches(type); 212 | } 213 | }); 214 | } 215 | 216 | } 217 | 218 | } 219 | 220 | } 221 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 4.0.0 21 | 22 | 23 | org.mybatis 24 | mybatis-parent 25 | 51 26 | 27 | 28 | 29 | org.mybatis.scripting 30 | mybatis-thymeleaf 31 | 1.1.1-SNAPSHOT 32 | 33 | MyBatis Thymeleaf 34 | Thymeleaf support for MyBatis 35 | https://github.com/mybatis/thymeleaf-scripting/ 36 | 37 | 2018 38 | 39 | 40 | 41 | Kazuki Shimizu 42 | kazuki43zoo@gmail.com 43 | 44 | 45 | 46 | 47 | scm:git:ssh://git@github.com/mybatis/thymeleaf-scripting.git 48 | scm:git:ssh://git@github.com/mybatis/thymeleaf-scripting.git 49 | HEAD 50 | https://github.com/mybatis/thymeleaf-scripting/ 51 | 52 | 53 | GitHub 54 | https://github.com/mybatis/thymeleaf-scripting/issues 55 | 56 | 57 | GitHub 58 | https://github.com/mybatis/thymeleaf-scripting/actions 59 | 60 | 61 | 62 | gh-pages-scm 63 | MyBatis Thymeleaf GitHub Pages 64 | scm:git:ssh://git@github.com/mybatis/thymeleaf-scripting.git 65 | 66 | 67 | 68 | 69 | 70 | 11 71 | 11 72 | 73 | 74 | 3.5.19 75 | 3.1.3.RELEASE 76 | 77 | 78 | 3.2.0 79 | 80 | 81 | org.mybatis.scripting.thymeleaf 82 | 1.0.0 83 | 84 | 85 | 1727226406 86 | 87 | --add-opens java.base/java.lang=ALL-UNNAMED 88 | 89 | 90 | 91 | 92 | org.mybatis 93 | mybatis 94 | ${mybatis.version} 95 | provided 96 | true 97 | 98 | 99 | org.thymeleaf 100 | thymeleaf 101 | ${thymeleaf.version} 102 | 103 | 104 | 105 | org.junit.jupiter 106 | junit-jupiter-engine 107 | 6.0.1 108 | test 109 | 110 | 111 | org.hsqldb 112 | hsqldb 113 | 2.7.4 114 | test 115 | 116 | 117 | com.h2database 118 | h2 119 | 2.4.240 120 | test 121 | 122 | 123 | ch.qos.logback 124 | logback-classic 125 | 1.5.22 126 | test 127 | 128 | 129 | org.springframework 130 | spring-jdbc 131 | 7.0.2 132 | test 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | maven-surefire-plugin 141 | 142 | 143 | ${mybatis.version} 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | org.asciidoctor 152 | asciidoctor-maven-plugin 153 | ${asciidoctor-maven-plugin.version} 154 | 155 | html 156 | ${project.build.directory}/site 157 | 158 | ${project.version} 159 | ${mybatis.version} 160 | 161 | 162 | 163 | 164 | generate-html 165 | 166 | process-asciidoc 167 | 168 | generate-resources 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | eclipse 178 | 179 | 180 | m2e.version 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | org.eclipse.m2e 189 | lifecycle-mapping 190 | 1.0.0 191 | 192 | 193 | 194 | 195 | 196 | org.asciidoctor 197 | asciidoctor-maven-plugin 198 | [${asciidoctor-maven-plugin.version},) 199 | 200 | process-asciidoc 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | -------------------------------------------------------------------------------- /src/main/java/org/mybatis/scripting/thymeleaf/processor/MyBatisParamTagProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 the original author or authors. 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 | * https://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 org.mybatis.scripting.thymeleaf.processor; 17 | 18 | import java.lang.reflect.Array; 19 | import java.util.Collection; 20 | import java.util.function.UnaryOperator; 21 | 22 | import org.mybatis.scripting.thymeleaf.MyBatisBindingContext; 23 | import org.thymeleaf.context.ITemplateContext; 24 | import org.thymeleaf.engine.AttributeName; 25 | import org.thymeleaf.engine.EngineEventUtils; 26 | import org.thymeleaf.engine.IterationStatusVar; 27 | import org.thymeleaf.model.IProcessableElementTag; 28 | import org.thymeleaf.processor.element.AbstractAttributeTagProcessor; 29 | import org.thymeleaf.processor.element.IElementTagStructureHandler; 30 | import org.thymeleaf.standard.expression.IStandardExpression; 31 | import org.thymeleaf.standard.expression.StandardExpressionExecutionContext; 32 | import org.thymeleaf.templatemode.TemplateMode; 33 | 34 | /** 35 | * The processor class for handling the {@code mb:p} tag.
36 | * This processor render bind variable(default: {@code #{…​}}) expression that can parsed data access library and 37 | * register an iteration object to the bind variables. 38 | * 39 | * @author Kazuki Shimizu 40 | * 41 | * @version 1.0.0 42 | */ 43 | public class MyBatisParamTagProcessor extends AbstractAttributeTagProcessor { 44 | 45 | private static final int PRECEDENCE = 1400; 46 | private static final String ATTR_NAME = "p"; 47 | 48 | private final StandardExpressionExecutionContext expressionExecutionContext; 49 | 50 | private UnaryOperator bindVariableRender = BindVariableRender.BuiltIn.MYBATIS; 51 | 52 | /** 53 | * Constructor that can be specified the template mode and dialect prefix. 54 | * 55 | * @param templateMode 56 | * A target template mode 57 | * @param prefix 58 | * A target dialect prefix 59 | */ 60 | public MyBatisParamTagProcessor(final TemplateMode templateMode, final String prefix) { 61 | super(templateMode, prefix, null, false, ATTR_NAME, true, PRECEDENCE, true); 62 | expressionExecutionContext = templateMode == TemplateMode.TEXT ? StandardExpressionExecutionContext.RESTRICTED 63 | : StandardExpressionExecutionContext.NORMAL; 64 | } 65 | 66 | /** 67 | * Set a custom bind variable render function.
68 | * By default, render {@literal #{...}} format. 69 | * 70 | * @param bindVariableRender 71 | * a custom bind variable render function 72 | * 73 | * @since 1.0.2 74 | */ 75 | public void setBindVariableRender(UnaryOperator bindVariableRender) { 76 | this.bindVariableRender = bindVariableRender; 77 | } 78 | 79 | /** 80 | * {@inheritDoc} 81 | */ 82 | @Override 83 | protected void doProcess(ITemplateContext context, IProcessableElementTag tag, AttributeName attributeName, 84 | String attributeValue, IElementTagStructureHandler structureHandler) { 85 | if (attributeValue.contains("${")) { 86 | attributeValue = getExpressionEvaluatedText(context, tag, attributeName, attributeValue); 87 | } 88 | 89 | Pair parameterAndOptionPair = Pair.parse(attributeValue, ','); 90 | String parameterPath = parameterAndOptionPair.left; 91 | String options = parameterAndOptionPair.right; 92 | 93 | Pair objectNameAndPropertyPathPair = Pair.parse(parameterPath, '.'); 94 | String objectName = objectNameAndPropertyPathPair.left; 95 | String nestedPropertyPath = objectNameAndPropertyPathPair.right; 96 | 97 | String body; 98 | String iterationObjectName = objectName + "Stat"; 99 | if (context.containsVariable(iterationObjectName)) { 100 | MyBatisBindingContext bindingContext = MyBatisBindingContext.load(context); 101 | IterationStatusVar iterationStatus = (IterationStatusVar) context.getVariable(iterationObjectName); 102 | String iterationObjectVariableName = bindingContext.generateUniqueName(objectName, iterationStatus); 103 | if (!bindingContext.containsCustomBindVariable(iterationObjectVariableName)) { 104 | bindingContext.setCustomBindVariable(iterationObjectVariableName, iterationStatus.getCurrent()); 105 | } 106 | if (nestedPropertyPath.isEmpty()) { 107 | body = bindVariableRender.apply(iterationObjectVariableName + options); 108 | } else { 109 | Object value = getExpressionEvaluatedValue(context, tag, attributeName, parameterPath); 110 | if (isCollectionOrArray(value)) { 111 | body = generateCollectionBindVariables(value, iterationObjectVariableName + nestedPropertyPath, options); 112 | } else { 113 | body = bindVariableRender.apply(iterationObjectVariableName + nestedPropertyPath + options); 114 | } 115 | } 116 | } else { 117 | Object value = nestedPropertyPath.isEmpty() ? context.getVariable(objectName) 118 | : getExpressionEvaluatedValue(context, tag, attributeName, parameterPath); 119 | if (isCollectionOrArray(value)) { 120 | body = generateCollectionBindVariables(value, parameterPath, options); 121 | } else { 122 | body = bindVariableRender.apply(attributeValue); 123 | } 124 | } 125 | structureHandler.setBody(body, false); 126 | } 127 | 128 | private Object getExpressionEvaluatedValue(ITemplateContext context, IProcessableElementTag tag, 129 | AttributeName attributeName, String parameterValue) { 130 | IStandardExpression expression = EngineEventUtils.computeAttributeExpression(context, tag, attributeName, 131 | "${" + parameterValue + "}"); 132 | return expression.execute(context, this.expressionExecutionContext); 133 | } 134 | 135 | private String getExpressionEvaluatedText(ITemplateContext context, IProcessableElementTag tag, 136 | AttributeName attributeName, String parameterValue) { 137 | IStandardExpression expression = EngineEventUtils.computeAttributeExpression(context, tag, attributeName, 138 | "|" + parameterValue + "|"); 139 | return expression.execute(context, this.expressionExecutionContext).toString(); 140 | } 141 | 142 | private boolean isCollectionOrArray(Object value) { 143 | return value != null && (Collection.class.isAssignableFrom(value.getClass()) || value.getClass().isArray()); 144 | } 145 | 146 | private String generateCollectionBindVariables(Object value, String parameterPath, String options) { 147 | int size = value.getClass().isArray() ? Array.getLength(value) : ((Collection) value).size(); 148 | if (size == 0) { 149 | return "null"; 150 | } else { 151 | StringBuilder sb = new StringBuilder(); 152 | for (int i = 0; i < size; i++) { 153 | if (i != 0) { 154 | sb.append(", "); 155 | } 156 | sb.append(bindVariableRender.apply(parameterPath + "[" + i + "]" + options)); 157 | } 158 | return sb.toString(); 159 | } 160 | } 161 | 162 | private static class Pair { 163 | 164 | private final String left; 165 | private final String right; 166 | 167 | private Pair(String left, String right) { 168 | this.left = left; 169 | this.right = right; 170 | } 171 | 172 | private static Pair parse(String value, char separator) { 173 | int separatorIndex = value.indexOf(separator); 174 | String left; 175 | String right; 176 | if (separatorIndex == -1) { 177 | left = value; 178 | right = ""; 179 | } else { 180 | left = value.substring(0, separatorIndex); 181 | right = value.substring(separatorIndex); 182 | } 183 | return new Pair(left, right); 184 | } 185 | 186 | } 187 | 188 | } 189 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Apache Maven Wrapper startup batch script, version 3.3.2 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 28 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 29 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 30 | @REM e.g. to debug Maven itself, use 31 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 32 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 33 | @REM ---------------------------------------------------------------------------- 34 | 35 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 36 | @echo off 37 | @REM set title of command window 38 | title %0 39 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 40 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 41 | 42 | @REM set %HOME% to equivalent of $HOME 43 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 44 | 45 | @REM Execute a user defined script before this one 46 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 47 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 48 | if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* 49 | if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* 50 | :skipRcPre 51 | 52 | @setlocal 53 | 54 | set ERROR_CODE=0 55 | 56 | @REM To isolate internal variables from possible post scripts, we use another setlocal 57 | @setlocal 58 | 59 | @REM ==== START VALIDATION ==== 60 | if not "%JAVA_HOME%" == "" goto OkJHome 61 | 62 | echo. >&2 63 | echo Error: JAVA_HOME not found in your environment. >&2 64 | echo Please set the JAVA_HOME variable in your environment to match the >&2 65 | echo location of your Java installation. >&2 66 | echo. >&2 67 | goto error 68 | 69 | :OkJHome 70 | if exist "%JAVA_HOME%\bin\java.exe" goto init 71 | 72 | echo. >&2 73 | echo Error: JAVA_HOME is set to an invalid directory. >&2 74 | echo JAVA_HOME = "%JAVA_HOME%" >&2 75 | echo Please set the JAVA_HOME variable in your environment to match the >&2 76 | echo location of your Java installation. >&2 77 | echo. >&2 78 | goto error 79 | 80 | @REM ==== END VALIDATION ==== 81 | 82 | :init 83 | 84 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 85 | @REM Fallback to current working directory if not found. 86 | 87 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 88 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 89 | 90 | set EXEC_DIR=%CD% 91 | set WDIR=%EXEC_DIR% 92 | :findBaseDir 93 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 94 | cd .. 95 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 96 | set WDIR=%CD% 97 | goto findBaseDir 98 | 99 | :baseDirFound 100 | set MAVEN_PROJECTBASEDIR=%WDIR% 101 | cd "%EXEC_DIR%" 102 | goto endDetectBaseDir 103 | 104 | :baseDirNotFound 105 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 106 | cd "%EXEC_DIR%" 107 | 108 | :endDetectBaseDir 109 | 110 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 111 | 112 | @setlocal EnableExtensions EnableDelayedExpansion 113 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 114 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 115 | 116 | :endReadAdditionalConfig 117 | 118 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar" 123 | 124 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 125 | IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B 126 | ) 127 | 128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 130 | if exist %WRAPPER_JAR% ( 131 | if "%MVNW_VERBOSE%" == "true" ( 132 | echo Found %WRAPPER_JAR% 133 | ) 134 | ) else ( 135 | if not "%MVNW_REPOURL%" == "" ( 136 | SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar" 137 | ) 138 | if "%MVNW_VERBOSE%" == "true" ( 139 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 140 | echo Downloading from: %WRAPPER_URL% 141 | ) 142 | 143 | powershell -Command "&{"^ 144 | "$webclient = new-object System.Net.WebClient;"^ 145 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 146 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 147 | "}"^ 148 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ 149 | "}" 150 | if "%MVNW_VERBOSE%" == "true" ( 151 | echo Finished downloading %WRAPPER_JAR% 152 | ) 153 | ) 154 | @REM End of extension 155 | 156 | @REM If specified, validate the SHA-256 sum of the Maven wrapper jar file 157 | SET WRAPPER_SHA_256_SUM="" 158 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 159 | IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B 160 | ) 161 | IF NOT %WRAPPER_SHA_256_SUM%=="" ( 162 | powershell -Command "&{"^ 163 | "Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash;"^ 164 | "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ 165 | "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ 166 | " Write-Error 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ 167 | " Write-Error 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ 168 | " Write-Error 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ 169 | " exit 1;"^ 170 | "}"^ 171 | "}" 172 | if ERRORLEVEL 1 goto error 173 | ) 174 | 175 | @REM Provide a "standardized" way to retrieve the CLI args that will 176 | @REM work with both Windows and non-Windows executions. 177 | set MAVEN_CMD_LINE_ARGS=%* 178 | 179 | %MAVEN_JAVA_EXE% ^ 180 | %JVM_CONFIG_MAVEN_PROPS% ^ 181 | %MAVEN_OPTS% ^ 182 | %MAVEN_DEBUG_OPTS% ^ 183 | -classpath %WRAPPER_JAR% ^ 184 | "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ 185 | %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 186 | if ERRORLEVEL 1 goto error 187 | goto end 188 | 189 | :error 190 | set ERROR_CODE=1 191 | 192 | :end 193 | @endlocal & set ERROR_CODE=%ERROR_CODE% 194 | 195 | if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost 196 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 197 | if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" 198 | if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" 199 | :skipRcPost 200 | 201 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 202 | if "%MAVEN_BATCH_PAUSE%"=="on" pause 203 | 204 | if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% 205 | 206 | cmd /C exit /B %ERROR_CODE% 207 | --------------------------------------------------------------------------------