Mybatis Velocity Scripting Support.
- master(2.1.x) : MyBatis 3.5+, Velocity 2.1+ and Java 8+
- 2.0.x : MyBatis 3.4+, Velocity 2.0 and Java 7+
Velocity scripting plugin for MyBatis
Home Page: http://mybatis.github.io/velocity-scripting
License: Apache License 2.0
Mybatis Velocity Scripting Support.
Configuration class is friendly to spring boot configuration properties.
In this change, add the VelocityLanguageDriverConfig
and add a new constructor on the VelocityLanguageDriver
for passing it. For details, refer to #72.
This change keep the backward compatibility.
Possible StringIndexOutOfBoundsException, if using suffixOverrides with params.maxSuffixLength > params.maxBody in TrimDirective.java line 68.
Example:
1=1
Change to 2.1.0 from 2.1 on master branch. In addition, create 2.0.x branch based on current latest version and update development version to 2.0.1-SNAPSHOT from 2.1-SNAPSHOT.
Trying to use string substitution like described in https://mybatis.org/mybatis-3/sqlmap-xml.html#String_Substitution
UPDATE ${tableName} SET ...
I get the following error:
java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '{tableName}
Is it supported in mybatis-velocity?
Mapper:
<update id="velocity-nested-set" lang="velocity">
#repeat( $_parameter.list $level1)
#set( $foo = $level1)
@{foo}
#end
</update>
Test:
@Test
public void velocityTestNestedSet() throws SQLException, IOException {
expect(connection.getAutoCommit()).andStubReturn(false);
expect(connection.prepareStatement("\n" +
" ?\n" +
" ?\n" +
" ")).andReturn(statement);
statement.setInt(1, 1);
statement.addBatch();
statement.setInt(2, 2);
statement.addBatch();
expect(statement.executeBatch()).andStubReturn(new int[]{2});
statement.close();
connection.setAutoCommit(true);
connection.rollback();
connection.close();
replay();
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(
Resources.getResourceAsStream("configuration.xml"));
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, connection);
List<Integer> param = Arrays.asList(1,2);
sqlSession.insert("velocity-nested-set", param);
sqlSession.flushStatements();
sqlSession.close();
}
Exception:
java.lang.AssertionError:
Unexpected method call setInt(1, 2):
setInt(1, 1): expected: 1, actual: 0
addBatch(): expected: 2, actual: 0
setInt(2, 2): expected: 1, actual: 0
close(): expected: 1, actual: 0
at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:29)
at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:56)
at com.sun.proxy.$Proxy7.setInt(Unknown Source)
at org.apache.ibatis.type.IntegerTypeHandler.setNonNullParameter(IntegerTypeHandler.java:28)
at org.apache.ibatis.type.IntegerTypeHandler.setNonNullParameter(IntegerTypeHandler.java:23)
at org.apache.ibatis.type.BaseTypeHandler.setParameter(BaseTypeHandler.java:46)
at org.apache.ibatis.type.UnknownTypeHandler.setNonNullParameter(UnknownTypeHandler.java:42)
at org.apache.ibatis.type.BaseTypeHandler.setParameter(BaseTypeHandler.java:46)
at org.apache.ibatis.scripting.defaults.DefaultParameterHandler.setParameters(DefaultParameterHandler.java:77)
at org.apache.ibatis.executor.statement.PreparedStatementHandler.parameterize(PreparedStatementHandler.java:77)
at org.apache.ibatis.executor.statement.RoutingStatementHandler.parameterize(RoutingStatementHandler.java:58)
at org.apache.ibatis.executor.BatchExecutor.doUpdate(BatchExecutor.java:68)
at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:100)
at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:75)
at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:148)
at org.apache.ibatis.session.defaults.DefaultSqlSession.insert(DefaultSqlSession.java:137)
The mybatis-velocity 2.1.0 is required with Java 8+.
If we use the #where() directive, a "WHERE" is always prepended at the beginning, even though no condition met.
It's because of the "VelocityWhitespaceGobbling".
Example:
select * from foo
output: select * from foo WHERE
I've change the property key for specifying the additional context attributes for support configuration property on mybatis-spring-boot . The additional.context.attributes
can be use, however it become the deprecated property key since 2.1.0. If you are updating to 2.1+, please change to use the additionalContextAttributes.{name}={value}
instead of it.
The MyBatis Velocity plugin does not allow to put attributes in the Velocity context to be used during the query transformation phase. This feature is very important for existing projects that have code to support SQL and literal parameters transformation.
The idea is to introduce an optional property file where custom attributes can be defined and injected into the Velocity context at run time. The same property file can be used to override the default properties defined in the "velocity.properties" file.
The Velocity 2.1 is required the JDK 8+.
https://issues.apache.org/jira/browse/VELOCITY-869
Version 2.0 fixes this but also raises the minimum java level to 1.7.
There's already a work around to just explicitly include commons-collection 3.2.2+ it seems but users who can't or are unwilling to do that workaround might appreciate another 1.x release
It looks like you cannot access Configuration.getVariables() from the template.
The Driver
is very general name. I will prefer changing to the VelocityLanguageDriver
. The Driver
will be deprecated API.
Mapper:
<update id="velocity-nested-ref" lang="velocity">
#repeat( $_parameter.list $level1)
#repeat( $level1 $level2)
@{level1}
#end
#end
</update>
Test:
@Test
public void velocityTestNestedRef() throws SQLException, IOException {
expect(connection.getAutoCommit()).andStubReturn(false);
expect(connection.prepareStatement("\n" +
" ?\n" +
" ?\n" +
" ?\n" +
" ?\n" +
" ")).andReturn(statement);
replay();
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(
Resources.getResourceAsStream("configuration.xml"));
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, connection);
List<List<Integer>> param = Arrays.asList(Arrays.asList(10,11),Arrays.asList(20, 21));
sqlSession.insert("velocity-nested-ref", param);
sqlSession.flushStatements();
sqlSession.close();
}
Exception:
org.apache.ibatis.exceptions.PersistenceException:
### Error updating database. Cause: org.apache.ibatis.binding.BindingException: Parameter 'level1' not found. Available parameters are [list]
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: ? ? ? ?
### Cause: org.apache.ibatis.binding.BindingException: Parameter 'level1' not found. Available parameters are [list]
at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:23)
at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:150)
at org.apache.ibatis.session.defaults.DefaultSqlSession.insert(DefaultSqlSession.java:137)
at com.luxoft.mybatis.splitter.UpdateSplitterPluginTest.velocityTestNestedRef(UpdateSplitterPluginTest.java:154)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:68)
at org.junit.internal.runners.MethodRoadie.runTestMethod(MethodRoadie.java:107)
at org.unitils.UnitilsJUnit4TestClassRunner$TestListenerInvokingMethodRoadie.runTestMethod(UnitilsJUnit4TestClassRunner.java:174)
at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:88)
at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:96)
at org.unitils.UnitilsJUnit4TestClassRunner$TestListenerInvokingMethodRoadie.runBeforesThenTestThenAfters(UnitilsJUnit4TestClassRunner.java:156)
at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:86)
at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:49)
at org.unitils.UnitilsJUnit4TestClassRunner.invokeTestMethod(UnitilsJUnit4TestClassRunner.java:95)
at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:61)
at org.unitils.UnitilsJUnit4TestClassRunner.access$000(UnitilsJUnit4TestClassRunner.java:44)
at org.unitils.UnitilsJUnit4TestClassRunner$1.run(UnitilsJUnit4TestClassRunner.java:62)
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:33)
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:45)
at org.unitils.UnitilsJUnit4TestClassRunner.run(UnitilsJUnit4TestClassRunner.java:68)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:76)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:195)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:63)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: org.apache.ibatis.binding.BindingException: Parameter 'level1' not found. Available parameters are [list]
at org.apache.ibatis.session.defaults.DefaultSqlSession$StrictMap.get(DefaultSqlSession.java:257)
at org.apache.ibatis.reflection.wrapper.MapWrapper.get(MapWrapper.java:41)
at org.apache.ibatis.reflection.MetaObject.getValue(MetaObject.java:113)
at org.apache.ibatis.scripting.defaults.DefaultParameterHandler.setParameters(DefaultParameterHandler.java:72)
at org.apache.ibatis.executor.statement.PreparedStatementHandler.parameterize(PreparedStatementHandler.java:77)
at org.apache.ibatis.executor.statement.RoutingStatementHandler.parameterize(RoutingStatementHandler.java:58)
at org.apache.ibatis.executor.BatchExecutor.doUpdate(BatchExecutor.java:68)
at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:100)
at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:75)
at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:148)
... 30 more
This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.
These updates have all been created already. Click a checkbox below to force a retry/rebase of any.
.github/workflows/ci.yaml
actions/checkout v4
actions/setup-java v4
.github/workflows/codeql.yml
actions/checkout v4
github/codeql-action v3
github/codeql-action v3
github/codeql-action v3
.github/workflows/coveralls.yaml
actions/checkout v4
actions/setup-java v4
.github/workflows/site.yaml
actions/checkout v4
actions/setup-java v4
JamesIves/github-pages-deploy-action v4.6.0
.github/workflows/sonar.yaml
actions/checkout v4
actions/setup-java v4
.github/workflows/sonatype.yaml
actions/checkout v4
actions/setup-java v4
pom.xml
org.mybatis:mybatis-parent 43
org.mybatis:mybatis 3.5.16
org.apache.velocity:velocity-engine-core 2.3
org.apache.commons:commons-text 1.12.0
org.junit.jupiter:junit-jupiter-engine 5.10.2
org.hsqldb:hsqldb 2.7.2
org.slf4j:slf4j-simple 2.0.13
.mvn/wrapper/maven-wrapper.properties
maven 3.9.6
maven-wrapper 3.2.0
Currently mybatis-scripting supports the #repeat directive which iterates through the first 1000 items in a collection and ignores the rest. Most of the time the #repeat directive will be used to render an IN clause and it is not convenient to partially render a collection. Some of the databases such as Oracle have a limit of 1000 items in their IN clause. One way to get around this limitation is to use an OR clause inside the IN .
For example: "WHERE ( (id IN (1,2,....1000)) OR (id IN (1001,1002 ......2000))) ".
I am proposing to add a block #in(Collection $item columnName) directive to mybatis-scripting to handle any collection size. If the collection size is greater than a 1000 , then items will be grouped by 1000 and OR'ed together. It can be used as follow :
SELECT \* FROM MyTable WHERE #in(_param.ids $id "id") @{id} #endPlease note that we do not have to pass the "IN" , "(" , ")" and ","
here is a example.
select * from user
#where()
#in( $_parameter.ids $id "userid" )
@{id}
#end
#end
it will excute the sql "select * from user" while $_parameter.ids is empty.
the bug causes by this code.
String content = buffer.toString().trim();
if (!"".equals(content)) {
writer.append(this.open);
writer.append(content);
writer.append(this.close);
}
clean(context, o, collector, savedItemKey);
return true;
to fix the bug,we should do this
String content = buffer.toString().trim();
if (!"".equals(content)) {
writer.append(this.open);
writer.append(content);
writer.append(this.close);
}else{
writer.append(this.open);
writer.append(this.open);
writer.append(this.column);
writer.append(" NOT IN ");
writer.append(this.open);
writer.append(" NULL ");
writer.append(this.close);
writer.append(this.close);
writer.append(this.close);
}
clean(context, o, collector, savedItemKey);
return true;
so ti will excute "select * from user where userid not in (null)"
"userid not in (null)" always return false even though the user table have one line which userid have value.
this bug is hidden dangers while the sql is a update or delete operation.
The mybatis 3.5.1 is required the JDK 8+.
The mybatis-velocity 2.0 used deprecated properties as follow:
[main] WARN org.apache.velocity.deprecation - configuration key 'class.resource.loader.class' has been deprecated in favor of 'resource.loader.class.class'
[main] WARN org.apache.velocity.deprecation - configuration key 'resource.loader' has been deprecated in favor of 'resource.loaders'
[main] WARN org.apache.velocity.deprecation - configuration key 'userdirective' has been deprecated in favor of 'runtime.custom_directives'
[main] WARN org.apache.velocity.deprecation - configuration key 'userdirective' has been deprecated in favor of 'runtime.custom_directives'
[main] WARN org.apache.velocity.deprecation - configuration key 'userdirective' has been deprecated in favor of 'runtime.custom_directives'
[main] WARN org.apache.velocity.deprecation - configuration key 'userdirective' has been deprecated in favor of 'runtime.custom_directives'
In current version, template file(vm file) does not support.
For example:
select
id, name, state, country
from
city
where
state = @{state}
@Select("#parse('/mappers/CityMapper-findByState.vm')")
City findByState(@Param("state") String state);
In above case, @{state}
cannot translate to JDBC bind variable (?
).
We will consider to support a template file(.vm
) like as mybatis-freemarker and mybatis-thymeleaf.
It took me 3 hours to find out that it's the reason why my server starts failed.
velocity-scripting/src/site/xdoc/index.xml
Line 301 in 3fe08f3
I just upgraded to Eclipse Oxygen. I had been running velocity-scripting successfully under Neon. When I imported the project to Neon and tried to run a unit test in VelocityLanguageTest, I kept getting initialization errors, which were simply reported as FilterRequest errors. Here's a bug report about FilterRequest hiding the actual problem:
Using the suggested workaround of running JUnit against the file instead of a single test, I discovered that the stack trace was claiming ClassNotFound on AspectJ. So, I added an AspectJ dependency in pom.xml, and that fixed the issue.
I do have AspectJ in Eclipse, but that does not help.
Velocity-scripting plugin should allow the use of user defined custom directives. Currently it is not a supported feature.
The RepeatDirective takes the first 1000 items from the collection and ignores the remaining. It is appending the separator after the last item and before the closing character.
For example :
@{id}
#end
Will generate
column IN (1,.......,1000,)
I've deprecated the userdirective
for specifying user defined custom directive because it can be replaced the runtime.custom_directives
provided by Velocity 2.1. The userdirective
can be use, however it become the deprecated property key since 2.1.0. If you are updating to 2.1+, please change to use the velocity-settings.runtime.custom_directives
or runtime.custom_directives
instead of it.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.