Giter Site home page Giter Site logo

mybatis / mybatis-3 Goto Github PK

View Code? Open in Web Editor NEW
19.5K 1.2K 12.7K 143.38 MB

MyBatis SQL mapper framework for Java

Home Page: http://mybatis.github.io/mybatis-3/

License: Apache License 2.0

Java 99.43% CSS 0.24% PLpgSQL 0.16% TSQL 0.16%
sql mybatis java

mybatis-3's People

Contributors

anatoliistepaniuk avatar awxiaoxian2020 avatar cbegin avatar christianpoitras avatar codeingboy avatar dependabot-preview[bot] avatar dependabot[bot] avatar elfhazard avatar emacarron avatar h3adache avatar harawata avatar hazendaz avatar jasonleaster avatar jeffgbutler avatar jingwenlqh avatar kazuki43zoo avatar kezhenxu94 avatar kmoco2am avatar mnesarco avatar nmaves avatar nothingzhl avatar oliverwqcwrw avatar paopaofish avatar peterchenhdu avatar renovate[bot] avatar simonetripodi avatar sogoagain avatar wangdx avatar xdshent avatar zhangshenao avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mybatis-3's Issues

More than one result set with @ResultMap

Right now @resultMap looks like

public @interface ResultMap {
  String value();
}

So it looks like only one resultmap can be specified, but in fact it accepts setting the value like this way:

@resultMap("rm1,rm2")

I suggest changing the signature to:

public @interface ResultMap {
  String[] value();
}

So it is evident that it supports more than one resultMap and will be used like:

@resultMap("rm1") for one
@resultMap({"rm1","rm2"}) for more than one

We should keep compat with the old method.

Add support for log4j 2

Log4j 2 API has changed so in order to support it we need a new wrapper.

Log4j is in beta4 but probably the API will not change.

StackOverflow when using databaseId

gcode: Reported by dcendents, Feb 21 (2 days ago)

Hi,

I found a weird problem with mybatis, spring and declaring statements with databaseId that end up in a StackOverflowError (infinite loop).

mybatis version: 3.1.1
mybatis-spring version: 1.1.1

See the attached project with 2 unit tests. One that fails to load the mybatis configuration, and the second one that force the databaseId to null and the mybatis configuration can be loaded.

I've done a bit of debugging to try and understand the problem and here is what I think is the problem:

(Refer to the attached project for files and content)

  • Mapping A is loaded
    • XMLMapperBuilder.buildStatementFromContext is executed with requiredDatabaseId = h2
      • 3 statements (delete + updates) without databaseId are skipped
      • same 3 statements (delete + updates) with databaseId=h2 are loaded
      • select without databaseId is skipped
    • XMLMapperBuilder.buildStatementFromContext is executed with requiredDatabaseId = null
      • 3 statements (delete + updates) with databaseId=h2 are skipped
      • same 3 statements (delete + updates) without databaseId are skipped because "skip this statement if there is a previous one with a not null databaseId" (databaseIdMatchesCurrent method)
      • select without databaseId is added to the incomplete statements because it includes sql from mapper C
  • Mapping B is loaded, similar to A except:
    • XMLMapperBuilder.buildStatementFromContext is executed with requiredDatabaseId = null
      • 3 statements (delete + updates) without databaseId are parsed, they should be skipped but the following happens instead
        • in databaseIdMatchesCurrent
          • configuration.hasStatement is called with validateIncompleteStatements set to false and returns true
        • configuration.getMappedStatement with default value for validateIncompleteStatements set to true
          • it tries to parse the statement from mapper A (still incomplete) and throws an exception
          • The statement is added to the incomplete statements
  • Mapping C is loaded
    • Again in databaseIdMatchesCurrent it tries to resolve incomplete statements, but this time mybatis knows about mapping C, so when processing the select from mapping A, it also enters into the databaseIdMatchesCurrent which tries to resolve incomplete statements, loads select from A again, and gets into an infinite loop.

The good news is that I think this is a very simple fix, XMLStatementBuilder.databaseIdMatchesCurrent line 130 should set validateIncompleteStatements to false: "MappedStatement previous = this.configuration.getMappedStatement(id, false);"

Also what is strange is I could not reproduce the problem without loading mybatis through spring, but I don't see why that would matter.

Hope the fix is as easy as that and it can be fixed for the next version.

My current workaround is to load the mapping in a different order (B, C and then A).
But there might be a time soon where all our mappings will reference sql fragments from other files and we won't be able to use that workaround.

Thanks

Escape in queries (<update>, <insert>...) property name which are named like operators (lt, gt, ...)

Hello,

I have in my database a column named lt and its counterpart in java is named also like that. However, when I try to test this column ( exemple : <if test="lt != null" > ), I have this stack :
org.apache.ibatis.exceptions.PersistenceException:

Error updating database. Cause: org.apache.ibatis.builder.BuilderException: Error evaluating expression 'lt != null'. Cause: org.apache.ibatis.ognl.ExpressionSyntaxException: Malformed OGNL expression: lt != null [org.apache.ibatis.ognl.ParseException: Encountered "lt" at line 1, column 1.

Was expecting one of:
":" ...
"not" ...
"+" ...
"-" ...
"~" ...
"!" ...
"(" ...
"true" ...
"false" ...
"null" ...
"#this" ...
"#root" ...
"#" ...
"[" ...
"{" ...
"@" ...
"new" ...
...
<DYNAMIC_SUBSCRIPT> ...
"'" ...
"`" ...
""" ...
<INT_LITERAL> ...
<FLT_LITERAL> ...
]

Can you add a way to escape the operator name or maybe tell me that hopefully if it already exists and that I don't know to search.

Thanks in advance.

Column names with a period messes up name mapping

Edit: I've done a little more testing. It looks like any period in the column name messes it up; spaces have nothing to do with it.

I'm using version 3.2.1. I don't think the ResultHandler is necessary, but it's the scenario I had in my environment where I noticed the bug.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="IncorrectColumnNameTest">
    <select id="test" resultType="HashMap">
        select 1 as [Space No Period], 2 as [Space. With Period]
    </select>
</mapper>
public interface IncorrectColumnNameTest 
{
    public void test(ResultHandler resultHandler);
}
public Class IncorrectColumnNameTestDao
{
    public boolean test(FileWriter fileOutput) throws SQLException
    {
        final SqlSession session = MyBatisSession.openSession(con);
        try
        {
            final ConsoleResultHandler fileHandler = new ConsoleResultHandler();
            final DetailsViewExportMapper mapper = session.getMapper(DetailsViewExportMapper.class);
            mapper.test(fileHandler);
        } finally {
            session.close();
        }

        public static class ConsoleResultHandler implements ResultHandler
        {
            public void handleResult(ResultContext rc) 
            { 
                final Map<String,Object> row = (Map<String,Object>)rc.getResultObject();
                System.out.println( StringUtils.join(row.keySet(), ",") );
                System.out.println( StringUtils.join(row.values(), ",") );
            }

            private String join(Collection<? extends Object> data)
            {
                String join = "";
                for (Object o : data)
                {
                    join += o.toString() + ", "
                }
                return join;
            }
        }
    }
}

MyBatis output

905 [main] DEBUG IncorrectColumnNameTest.test - ooo Using Connection [com.mpti.reportlogiq.server.db.TraceConnection@16ef71]
905 [main] DEBUG IncorrectColumnNameTest.test - ==> Preparing: select 1 as [Space No Period], 2 as [Space. With Period]
906 [main] DEBUG IncorrectColumnNameTest.test - ==> Parameters:
937 [main] TRACE IncorrectColumnNameTest.test - <== Columns: Space No Period, Space. With Period
937 [main] TRACE com.mpti.reportlogiq.server.db.mappers.IncorrectColumnNameTest.test - <== Row: 1, 2

Expected output

Space No Period, Space. With Period,
1, 2,

Actual output

Space No Period, Space,
1, { With Period=2},

Performance optimization on lang=raw statements.

When 'raw' language is specified, the statement should be parsed only once to provide better performance. Although the main target of this change is batch operations, non-batch operations may get slight performance improvement as well.

Wrong java type with one-to-one association element

See http://code.google.com/p/mybatis/issues/detail?id=781

Reported by m.geri1974, Feb 20, 2013
Hi i am using the mybatis version mybatis-3.1.1,

I have a problem with one-to-one association element.

To reproduce the problem, create a mapper like the following:

<resultMap id="sampleHashResult" type="hashmap">
<result property="f1" column="f1" />
<result property="f2" column="f2" />
<association property="a1" javaType="java.lang.String"
    column="{param1=f1}" select="associationTest" />
<association property="a2" javaType="java.lang.String"
    column="{param1=f1}" select="associationTest" />
</resultMap>

<select id="sample" resultMap="sampleHashResult">
    SELECT 'field1' as f1, 10000 as f2
</select>

<select id="associationTest" resultType="java.lang.String">
    select 'test'
</select>

Running the code below:

List results = session.selectList("sample");

for (HashMap r : results) {
System.out.println("a1 class: " + r.get("a1").getClass());
System.out.println("a2 class: " + r.get("a2").getClass());
}

I expected to see the following output:

a1 class: class java.lang.String
a2 class: class java.lang.String

While it prints:

a1 class: class java.lang.String
a2 class: class java.util.ArrayList

It seems that after the first use of the 'association', which returns the expected String result type, then it returns an ArrayList of one element containing the String value (and it's an unexpected behaviour for my application).

I attached the full working eclipse project sample. Let me know if you need any further details.

Thanks.

Collections should handle lazy loading

You should implement lazy load collections so that usages of the collection outside of a getter/setter don't require that equals or hashcode be called to lazy load.

Hibernate does this using a custom collection if the field is one of the collection interfaces.

Method applyItem(DynamicContext context, Object o, int i) in class ForEachSqlNode pollute the context specified

Mybatis version: 3.2.2

If use 'foreach' in mapper.xml, one item will be put into context by applyItem(DynamicContext context, Object o, int i) method and maybe result in wrong boundSql if there is a parameter of statement with the same name as the item name.

Here is an example:

  1. Method in Mapper interface:
    public List getList(@param("ids")List ids,@param("id")String id);
  2. Sql in Mapper xml
    mappper_xml
  3. If input parameters ids=['a','b'] and id=null, sql in boundSql will be:
    select * from CUSTOMER WHERE CID IN (?, ?) and CID = ?
    which shoud be
    select * from CUSTOMER WHERE CID IN (?, ?)

Problem abount generated keys with batch executor

Here is my batch insert demo code.

mybatis sqlSessionTemplate config

<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg index="0" ref="sqlSessionFactory" />
    <constructor-arg index="1" value="BATCH" />
</bean>

user table

-- I use mysql
create table t_user (
    id int unsigned auto_increment not null,
    name varchar(50) not null,
    constraint pk_user primary key (id)
)
autoincrement 101;

entity

public class User {
    private Long id;
    private String name;

    // getters & setters
    ....
}

mapper

public interface UserMapper {

    @Select("select * from t_user where id = #{id}")
    User find(Long id);

    @Insert("insert into t_user(name) values(#{name})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    void insert(User user);
}

junit test

    @Test
    public void testBatchInsert() {
        User user1 = new User();
        user1.setName("user 1");

        User user2 = new User();
        user2.setName("user 2");

        userMapper.insert(user1);
        userMapper.insert(user2);

        // Here will print:
        // user1 id: null
        // user2 id: null
        System.out.println("user1 id: " + user1.getId());
        System.out.println("user2 id: " + user2.getId());

        // Make a query, this can make mybatis flush batch statements,
        // so that we can get the generated keys.
        // It looks so strange, but it works.
        userMapper.find(null);

        // Here will print (xxx is the generated id):
        // user1 id: xxx
        // user2 id: xxx
        System.out.println("user1 id: " + user1.getId());
        System.out.println("user2 id: " + user2.getId());
    }

I don't know why mybatis doesn't support insert with list/array parameters like ibatis does.
If this feature is supported, mybatis can flush batch statements at the end of the insert operation.
Then we can get the generated keys without strange code.

mybatis-spring OSGi imports - Spring batch should be optional #2

The old issue is still actual.

Here is the log

JBossFuse:admin@root> start 127
Error executing command: Error starting bundles:
        Unable to start bundle 127: Unresolved constraint in bundle org.mybatis.mybatis-spring [127]: Unable to resolve 127.0: missing requirement [127.0] osgi.wiring.package; (osgi.wiring.package=org.springframework.batch.item)
JBossFuse:admin@root> headers 127

MyBatis-Spring (127)
--------------------
Specification-Title = MyBatis-Spring
Archiver-Version = Plexus Archiver
Tool = Bnd-1.50.0
Specification-Version = 1.2.0
Specification-Vendor = MyBatis.org
Implementation-Version = 1.2.0
Build-Jdk = 1.6.0_37
Implementation-Build-Date = 2013-02-20 20:58:18+0100
Implementation-Vendor-Id = org.mybatis
Created-By = Apache Maven Bundle Plugin
Implementation-Title = MyBatis-Spring
Manifest-Version = 1.0
Bnd-LastModified = 1361390302535
X-Compile-Target-JDK = 1.5
Include-Resource = META-INF/LICENSE=LICENSE,META-INF/NOTICE=NOTICE,META-INF/spring.handlers=src/main/java/META-INF/spring.handlers,META-INF/spring.schemas=src/main/java/META-INF/spring.schemas,org/mybatis/spring/config/mybatis-spring-1.2.xsd=src/main/java/org/mybatis/spring/config/mybatis-spring-1.2.xsd
Built-By = stripodi
X-Compile-Source-JDK = 1.5
Implementation-Vendor = MyBatis.org

Bundle-Vendor = MyBatis.org
Bundle-Name = MyBatis-Spring
Bundle-DocURL = http://www.mybatis.org/spring/
Bundle-Description = An easy-to-use Spring3 bridge for MyBatis sql mapping framework.
Bundle-SymbolicName = org.mybatis.mybatis-spring
Bundle-Version = 1.2.0
Bundle-License = http://www.apache.org/licenses/LICENSE-2.0.txt
Bundle-ManifestVersion = 2

DynamicImport-Package =
        *
Import-Package =
        javax.sql,
        org.apache.commons.logging,
        org.apache.ibatis.builder.xml,
        org.apache.ibatis.exceptions,
        org.apache.ibatis.executor,
        org.apache.ibatis.logging,
        org.apache.ibatis.mapping,
        org.apache.ibatis.plugin,
        org.apache.ibatis.reflection,
        org.apache.ibatis.reflection.factory,
        org.apache.ibatis.reflection.wrapper,
        org.apache.ibatis.session,
        org.apache.ibatis.transaction,
        org.apache.ibatis.type,
        org.springframework.batch;resolution:=optional,
        org.springframework.batch.item,
        org.springframework.batch.item.database,
        org.springframework.beans,
        org.springframework.beans.factory,
        org.springframework.beans.factory.annotation,
        org.springframework.beans.factory.config,
        org.springframework.beans.factory.support,
        org.springframework.beans.factory.xml,
        org.springframework.context,
        org.springframework.context.annotation,
        org.springframework.context.event,
        org.springframework.context.support,
        org.springframework.core,
        org.springframework.core.annotation,
        org.springframework.core.io,
        org.springframework.core.type,
        org.springframework.core.type.classreading,
        org.springframework.core.type.filter,
        org.springframework.dao,
        org.springframework.dao.support,
        org.springframework.jdbc.datasource,
        org.springframework.jdbc.support,
        org.springframework.transaction.support,
        org.springframework.util,
        org.w3c.dom
Export-Package =
        org.mybatis.spring.annotation;version=1.2.0,
        org.mybatis.spring.mapper;version=1.2.0,
        org.mybatis.spring.batch;version=1.2.0,
        org.mybatis.spring.transaction;version=1.2.0,
        org.mybatis.spring.config;version=1.2.0,
        org.mybatis.spring;version=1.2.0,
        org.mybatis.spring.support;version=1.2.0

As you can see

    org.springframework.batch;resolution:=optional,
    org.springframework.batch.item,
    org.springframework.batch.item.database,

only org.springframework.batch has optional resolution.

The pom.xml should be changed like the following

<osgi.import>org.springframework.batch*;resolution:=optional,*</osgi.import> 

Pay attention to additional * after the package name.

Wrong dependency on Log4j in MyBatis 3.2

MyBatis 3.2 does not startup without log4j in the classpath.

Caused by: java.lang.NoClassDefFoundError: org/apache/log4j/Priority
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:266)
at org.apache.ibatis.io.ClassLoaderWrapper.classForName(ClassLoaderWrapper.java:176)
at org.apache.ibatis.io.ClassLoaderWrapper.classForName(ClassLoaderWrapper.java:87)
at org.apache.ibatis.io.Resources.classForName(Resources.java:254)
at org.apache.ibatis.type.TypeAliasRegistry.registerAlias(TypeAliasRegistry.java:153)
at org.apache.ibatis.session.Configuration.(Configuration.java:173)

Allow <set> to trim comma prefix

Propose to change set node to allow it trim comma prefix

public class SetSqlNode extends TrimSqlNode {

  public SetSqlNode(Configuration configuration,SqlNode contents) {
    super(configuration, contents, "SET", ",", null, ",");
  }

}

Reason:
It is natural for users migrated from ibatis to write code like this

UPDATE user
<set>
    <if test="name != null">
        name=#{name}
    </if>
    <if test="desc != null">
        ,desc=#{desc}
    </if>
</set>

Instead of

UPDATE user
<set>
    <if test="name != null">
        name=#{name},
    </if>
    <if test="desc != null">
        desc=#{desc}
    </if>
</set>

The added feature will not break anything, although may use some cpu cycles and slow down a bit.

PooledDataSource does not retry acquiring a real connection if an SQLException occurs during connection establishment

When the PooledDataSource pops a new connection it checks if the new connection is bad and if so, it tries to acquire another connection until it failed (maximumIdleConnection + 3) times. Only then it throws an SQLException stating that no connection can be established.

However if a real connection is not created at all because of an SQLException during the attempt to create the Connection, the PooledDataSource will not retry acquiring a good Connectiona again because the SQLException is not caught by the PooledDataSource.

I would expect that both cases are treated the same. Thus marking the connection acquisition attempt as bad and retrying it until the connection establishment failed (maximumIdleConnection + 3) times. Only then should an exception be thrown.

Mapping Oracle SPs using Association

Hi Team,

I have an issue and I don't know how to resolve it. I read all docs and
didn't find any clue, but doing a fix at the code. (I guess)

I need to map the resultmap from a SP with another one, using Oracle
Procedures (This mean I receive the CURSOR in an OUT parameter).

So, I have no idea how to map it in the association, since the select which
do the call to the nested SP receives a Map), because that was the only way
I found to call a Oracle SP.

This is my code:

{call EEE.GET_EEE_DETAIL ( rs,jdbcType=CURSOR,mode=OUT,resultMap=EEEResultmap,javaType=java.sql.ResultSet}, {id, jdbcType=NUMERIC} )}

Please any advice!
Thank you in advance!

JdbcType cannot be resolved!

When I use mybatis-3.2.1 version, I find an issue when mybatis framework is resolving jdbc type. There is code fragment as below.

Mapper xml file:

update upc status = #{status,jdbcType=VARCHAR } where uidpk = #{uidPk,jdbcType=BIGINT}

When I invoke this statement, an error occurs, java.lang.IllegalArgumentException: No enum const class org.apache.ibatis.type.JdbcType.VARCHAR. Finally, I find this error is thrown from org.apache.ibatis.builder.resolveJdbcType, because parameter's jdbcType has a blank space on the right of VARCHAR. But I couldn't find the same issue in mybatis version 3.1.1.

shotsnap

3.2.x release: NestedResultSetHandler.handleRowValues discarding row.

I ran into a bug in which I expected a result set of about 3600 rows and get 1. The row I get back is the last row of the result set I see in Oracle SQL Developer. I've traced the issue to handleRowValues() in NestedResultSetHandler. Based on tracing thru that code in the debugger the row value gets set by the line:

rowValue = getRowValue(rs, discriminatedResultMap, rowKey, rowKey, null, resultColumnCache, partialObject);

but then nothing actually happens with the value. It would appear that the if statement right after should be within the bracket directly above it (existing source below):

while (shouldProcessMoreRows(rs, resultContext, rowBounds)) {
  final ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rs, resultMap, null);
  final CacheKey rowKey = createRowKey(discriminatedResultMap, rs, null, resultColumnCache);
  Object partialObject = objectCache.get(rowKey);
  if (partialObject == null && rowValue != null) { // issue #542 delay calling ResultHandler until object ends
    if (mappedStatement.isResultOrdered()) objectCache.clear(); // issue #577 clear memory if ordered
    callResultHandler(resultHandler, resultContext, rowValue);
  } 
  rowValue = getRowValue(rs, discriminatedResultMap, rowKey, rowKey, null, resultColumnCache, partialObject);
}
if (rowValue != null) callResultHandler(resultHandler, resultContext, rowValue);

In the 3.1.1 release the rowValue is bieng put into another collection:

while (shouldProcessMoreRows(rs, resultContext, rowBounds)) {
  final ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rs, resultMap, null);
  final CacheKey rowKey = createRowKey(discriminatedResultMap, rs, null, resultColumnCache);
  final boolean knownValue = objectCache.containsKey(rowKey);
  Object rowValue = getRowValue(rs, discriminatedResultMap, rowKey, resultColumnCache);
  if (!knownValue) {
    resultContext.nextResultObject(rowValue);
    resultHandler.handleResult(resultContext);
  }
}

EDIT: maybe not quite that simple as I didn't look at the source enough before posting. Based on more debugging the callResultHandler inside the if statement (thats inside the while loop) is never getting called. That would explain why i am only seeing the last row of the resultset in the returned collection. its being put in there by the second if.

Support for pluggable scripting engines in expression evaluation

This issue is a continuation of http://code.google.com/p/mybatis/issues/detail?id=583. That issue addressed scripting engines in SQL statement construction, and left expression evaluation for a later time. This issue addresses expression evaluation. This issue was also recently discussed on the MyBatis User mailing list in a thread with subject "Expression evaluation".

To be clear, we are talking about #{} and ${} expressions. The former are parameters, while the latter can be used anywhere.

3.2.2 SAXParseException includes row and column of error, but should have filename too

Below is a typical stack trace caused by a syntax error in a mapper xml file. It is not particularly helpful for finding the error. In this particular case, the opening double-quote of a select id was missing. In a typical project, there will be multiple mapper xml files, so the error message should identify the culprit.

org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.builder.BuilderException: Error creating document instance.  Cause: org.xml.sax.SAXParseException; lineNumber: 690; columnNumber: 14; Open quote is expected for attribute "{1}" associated with an  element type  "id".
    at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:106) ~[mybatis-3.2.2.jar:3.2.2]
    at org.apache.ibatis.builder.xml.XMLConfigBuilder.parse(XMLConfigBuilder.java:89) ~[mybatis-3.2.2.jar:3.2.2]
    at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:77) ~[mybatis-3.2.2.jar:3.2.2]
    at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:67) ~[mybatis-3.2.2.jar:3.2.2]
Caused by: org.apache.ibatis.builder.BuilderException: Error creating document instance.  Cause: org.xml.sax.SAXParseException; lineNumber: 690; columnNumber: 14; Open quote is expected for attribute "{1}" associated with an  element type  "id".
    at org.apache.ibatis.parsing.XPathParser.createDocument(XPathParser.java:253) ~[mybatis-3.2.2.jar:3.2.2]
    at org.apache.ibatis.parsing.XPathParser.<init>(XPathParser.java:122) ~[mybatis-3.2.2.jar:3.2.2]
    at org.apache.ibatis.builder.xml.XMLMapperBuilder.<init>(XMLMapperBuilder.java:74) ~[mybatis-3.2.2.jar:3.2.2]
    at org.apache.ibatis.builder.xml.XMLMapperBuilder.<init>(XMLMapperBuilder.java:69) ~[mybatis-3.2.2.jar:3.2.2]
    at org.apache.ibatis.builder.annotation.MapperAnnotationBuilder.loadXmlResource(MapperAnnotationBuilder.java:158) ~[mybatis-3.2.2.jar:3.2.2]
    at org.apache.ibatis.builder.annotation.MapperAnnotationBuilder.parse(MapperAnnotationBuilder.java:113) ~[mybatis-3.2.2.jar:3.2.2]
    at org.apache.ibatis.binding.MapperRegistry.addMapper(MapperRegistry.java:66) ~[mybatis-3.2.2.jar:3.2.2]
    at org.apache.ibatis.binding.MapperRegistry.addMappers(MapperRegistry.java:91) ~[mybatis-3.2.2.jar:3.2.2]
    at org.apache.ibatis.binding.MapperRegistry.addMappers(MapperRegistry.java:99) ~[mybatis-3.2.2.jar:3.2.2]
    at org.apache.ibatis.session.Configuration.addMappers(Configuration.java:631) ~[mybatis-3.2.2.jar:3.2.2]
    at org.apache.ibatis.builder.xml.XMLConfigBuilder.mapperElement(XMLConfigBuilder.java:308) ~[mybatis-3.2.2.jar:3.2.2]
    at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:104) ~[mybatis-3.2.2.jar:3.2.2]
    ... 30 common frames omitted
Caused by: org.xml.sax.SAXParseException: Open quote is expected for attribute "{1}" associated with an  element type  "id".
    at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(Unknown Source) ~[na:1.7.0_17]
    at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.fatalError(Unknown Source) ~[na:1.7.0_17]
    at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(Unknown Source) ~[na:1.7.0_17]
    at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(Unknown Source) ~[na:1.7.0_17]
    at com.sun.org.apache.xerces.internal.impl.XMLScanner.reportFatalError(Unknown Source) ~[na:1.7.0_17]
    at com.sun.org.apache.xerces.internal.impl.XMLScanner.scanAttributeValue(Unknown Source) ~[na:1.7.0_17]
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanAttribute(Unknown Source) ~[na:1.7.0_17]
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanStartElement(Unknown Source) ~[na:1.7.0_17]
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(Unknown Source) ~[na:1.7.0_17]
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(Unknown Source) ~[na:1.7.0_17]
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source) ~[na:1.7.0_17]
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source) ~[na:1.7.0_17]
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source) ~[na:1.7.0_17]
    at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(Unknown Source) ~[na:1.7.0_17]
    at com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(Unknown Source) ~[na:1.7.0_17]
    at com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(Unknown Source) ~[na:1.7.0_17]
    at org.apache.ibatis.parsing.XPathParser.createDocument(XPathParser.java:251) ~[mybatis-3.2.2.jar:3.2.2]
    ... 41 common frames omitted

List insertion using foreach loop through mybatis

Hi

i have issue while inserting list through my batis

How to use foreach loop while insertion

Code ๐Ÿ‘Ž
Mapper:

INSERT INTO DataTable (TenantID)
VALUES

      <foreach item="Service" collection="ListItems" separator=",">
          (#{Service.TenantID})   
      </foreach>

  </insert>

method in service class

public void insert()
{

        Wrapper obj = new Wrapper();
        List<Service> listField = new List<Service>();

        obj.ListItems = new List<Service>();
        Service objObjectField = new Service();
        objObjectField.TenantID = "1";
        objObjectField.ObjectID = "13";
        objObjectField.Slot0 = "text";
        obj.ListItems.Add(objObjectField);


        var objReturnValue = Mapper.Instance().Insert("insert", obj);

    }


Thanks 
Sravanthi

Cglib should not be required if lazy loading is not enabled

Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.531 sec <<< FAILURE!
org.mybatis.scripting.velocity.use.VelocityLanguageTest Time elapsed: 0.53 sec <<< ERROR!
org.apache.ibatis.exceptions.PersistenceException:

Error building SqlSession.

The error may exist in SQL Mapper Configuration

Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.builder.BuilderException: Error creating instance. Cause: java.lang.IllegalStateException: Cannot enable lazy loading because CGLIB is not available. Add CGLIB to your classpath.

at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:23)
at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:51)
at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:35)
at org.mybatis.scripting.velocity.use.VelocityLanguageTest.setUp(VelocityLanguageTest.java:59)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:236)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:134)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:113)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:103)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:74)

Caused by: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.builder.BuilderException: Error creating instance. Cause: java.lang.IllegalStateException: Cannot enable lazy loading because CGLIB is not available. Add CGLIB to your classpath.
at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:106)
at org.apache.ibatis.builder.xml.XMLConfigBuilder.parse(XMLConfigBuilder.java:89)
at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:49)
... 23 more
Caused by: org.apache.ibatis.builder.BuilderException: Error creating instance. Cause: java.lang.IllegalStateException: Cannot enable lazy loading because CGLIB is not available. Add CGLIB to your classpath.
at org.apache.ibatis.builder.BaseBuilder.createInstance(BaseBuilder.java:91)
at org.apache.ibatis.builder.xml.XMLConfigBuilder.settingsElement(XMLConfigBuilder.java:198)
at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:100)
... 25 more
Caused by: java.lang.IllegalStateException: Cannot enable lazy loading because CGLIB is not available. Add CGLIB to your classpath.
at org.apache.ibatis.executor.loader.CglibProxyFactory.(CglibProxyFactory.java:50)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:525)
at java.lang.Class.newInstance0(Class.java:372)
at java.lang.Class.newInstance(Class.java:325)
at org.apache.ibatis.builder.BaseBuilder.createInstance(BaseBuilder.java:89)
... 27 more
Caused by: java.lang.ClassNotFoundException: Cannot find class: net.sf.cglib.proxy.Enhancer
at org.apache.ibatis.io.ClassLoaderWrapper.classForName(ClassLoaderWrapper.java:188)
at org.apache.ibatis.io.ClassLoaderWrapper.classForName(ClassLoaderWrapper.java:87)
at org.apache.ibatis.io.Resources.classForName(Resources.java:254)
at org.apache.ibatis.executor.loader.CglibProxyFactory.(CglibProxyFactory.java:48)

Logging improvements

Two additions for logging:

  • Show how many rows have been updated by an insert/update/delete
  • Show the total received rows in a select

IndexOutOfBounds when a select key returns no data

Follows the trace. If select key contains no data MyBatis should not fail.

org.apache.ibatis.exceptions.PersistenceException: 
### Error updating database.  Cause: org.apache.ibatis.executor.ExecutorException: Error selecting key or setting result to parameter object. Cause: java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
### The error may exist in org/apache/ibatis/submitted/selectkey/Table2.xml
### The error may involve org.apache.ibatis.submitted.selectkey.Table2.insertNoValuesInSelectKey!selectKey-Inline
### The error occurred while setting parameters
### SQL: select * from table2 where name = 'xxx'
### Cause: org.apache.ibatis.executor.ExecutorException: Error selecting key or setting result to parameter object. Cause: java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
    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 org.apache.ibatis.submitted.selectkey.SelectKeyTest.testInsertTable3(SelectKeyTest.java:118)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: org.apache.ibatis.executor.ExecutorException: Error selecting key or setting result to parameter object. Cause: java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
    at org.apache.ibatis.executor.keygen.SelectKeyGenerator.processGeneratedKeys(SelectKeyGenerator.java:69)
    at org.apache.ibatis.executor.keygen.SelectKeyGenerator.processAfter(SelectKeyGenerator.java:47)
    at org.apache.ibatis.executor.statement.PreparedStatementHandler.update(PreparedStatementHandler.java:45)
    at org.apache.ibatis.executor.statement.RoutingStatementHandler.update(RoutingStatementHandler.java:66)
    at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:45)
    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)
    ... 26 more
Caused by: java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
    at java.util.ArrayList.rangeCheck(ArrayList.java:604)
    at java.util.ArrayList.get(ArrayList.java:382)
    at org.apache.ibatis.executor.keygen.SelectKeyGenerator.processGeneratedKeys(SelectKeyGenerator.java:65)
    ... 33 more

Empty namespace is allowed in mapper XML

Allowing an empty namespace can lead to many problems especially with StrictMap.

There is no logical reason to use an empty namespace inside a mapper. The only use I can think of is for very small project with only one mapper file. Even in this case, a namespace is still required to use an interface for the mapper.

The last comments on this issue shows a potential additional problem: https://code.google.com/p/mybatis/issues/detail?id=125

Index variable is not reset when multiple foreach loops are used in a single insert

I have multiple for each loops in a single insert as shown in Mapper.xml below. In 3.1.1 and before, the resulting sql would look like

insert into users (id, name, first_attr_1, first_attr_2, second_attr_1, second_attr_2) ...

In 3.2.0, an exception is thrown because the resulting sql is:

insert into users(id, name, first_attr_1, first_attr_2, second_attr_3, second_attr_4) ...

It seems that the index is not being reset before each foreach. A patch with a test for this issue is at https://gist.github.com/jgorinsky/5126131

Mapper.xml

<insert id="insertUser" parameterType="org.apache.ibatis.submitted.multipleiterates.User">
        insert into users
        (id,
        name,
        <foreach item="attr" index="index" collection="firstAttr" separator=",">
            first_attr_${index + 1}
        </foreach>,
        <foreach item="attr" index="index" collection="secondAttr" separator=",">
            second_attr_${index + 1}
        </foreach>
        )
        values(
        1,
        'User1',
        <foreach item="attr" index="index" collection="firstAttr" separator=",">
            #{attr}
        </foreach>,
        <foreach item="attr" index="index" collection="secondAttr" separator=",">
            #{attr}
        </foreach>
        )
    </insert>

Proposal for an enum type handler which persists specified value rather than ordinal or name.

MyBatis provides only EnumOridinalTypeHandler and EnumTypeHandler, but application (especially enterprise application) tends to persist specified value for each enum literal. It is welcome to provide a generic "EnumValueTypeHandler" to ease the problem.

In my implementation including:

  • an EnumValue annotation which is used to specify persistable value for enum literal
  • IEnumAdapter interface which define how to convert int value between enum literal
  • a generic concrete EnumAdapter implementation which maintains a fast-index to enum literals since most int value of enum literal falls into a narrow range of [0..N] with few exception (i.e., -1 or 999) for unkonw/undefined one(s).
  • EnumValueTypeHandler which can resolve an IEnumAdapter to execute convertion.

UnPooledDataSource driverProperties not work

private Connection doGetConnection(String username, String password) throws SQLException {
Properties props = new Properties(driverProperties);

props not a copy of driverProperties

Enum name collision when using typeAliases for packages in mybatis 3.2.0

Following situation:

In package 'com.example.beans'
there are two classes:

package com.example.beans;

class A {
  public static enum State {
    ON,
    OFF
  }
}
package com.example.beans;

class B {
  public static enum State {
    FRESH,
    ROTTEN
  }
}

In the mybatis configuration xml file:

<typeAliases>
  <package name="com.example.beans" />
</typeAliases>

This leads to following exception:

[..]
org.apache.ibatis.type.TypeException: The alias 'State' is already mapped to the value 'com.example.beans.A$State'.
at org.apache.ibatis.type.TypeAliasRegistry.registerAlias(TypeAliasRegistry.java:146)
[..]

A solution for me (no idea what sideeffects this may cause) is to replace line 127 in TypeAliasRegistry.java

if (!type.isAnonymousClass() && !type.isInterface()) {

with

if (!type.isAnonymousClass() && !type.isInterface()  && !type.isEnum()) {

Fail when a mapped column does not exist

MyBatis intentionally omits mapping when a mapped column is not found in a result set.

The idea behind this is that resultmaps are reusable so you can intentionally omit some columns in an statement.

Unit tests should cover the wrong mappigs, that is, those mappings with a wrong column (CUSTEMER instead of CUSTOMER). But... of course, it would be desirable to set up mybatis so it fails in case a column is not found.

For that purpose I think we can introduce a new property "strictColumnMapping" so that an exception is thrown if a mapped column is not present in the result set.

support for two-level model classes generation (abstract base class and stub class)

I could not find it anywhere if mybatis already supports such scenario or is it possible with plugins... The case is: I want to generate two cvlasses for every model/table, for example for table 'table' I want to have:

abstract class BaseTable {
//all mybatis generated stuff goes here
}
class Table extends BaseTable {
//empty
}

no - the point is that subsequent generations can overwrite BaseTable class, but final Table class where developers can put some logic must be left intact,
mappers should by default return always 'Table' class instances and operate on that level.

is it possible with current mybatis, with some plugins, or with custom code modyfications? Or maybe it could be built-in in mybatis next release? :)

Space in typeAlias results in the IllegalArgumentException: "Result Maps collection already contains value for"

If you have a trailing space in the alias attribute of a typeAlias definition, like this:

<typeAliases>
  <typeAlias alias="User "  type="com.example.domain.User" />
</typeAliases>

at runtime you get an exception like this:

java.lang.IllegalArgumentException: Result Maps collection already contains value for com.example.persistence.UserMapper.mapper_resultMap[xxx]_association[xxx]

where the map and association referenced in the exception are ones defined in your mapper.

Removing the trailing space fixes the problem, but it took a long time to track down.

Giving an option to save and restore parsed MappedStatement in binary data

Parsing lots of SQL statements from mapper XML file is a time consuming job (up to 10+ seconds for my case). It is annoying to keep user waiting each time at startup of a desktop application.

If MappedStatement which contains parsed SQL statement can be saved and restored in binary data without parsing again, it is very helpful and welcome.

MyBatis 3.2.0 - Disabled lazy loading but CGLIB is still required to be on classpath

Hi

I am in the process of upgrading camel-mybatis from 3.1.1 to 3.2.0 and hit a problem.

Caused by: org.apache.ibatis.exceptions.PersistenceException:

Error building SqlSession.

The error may exist in SQL Mapper Configuration

Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.builder.BuilderException: Error creating instance. Cause: java.lang.IllegalStateException: Cannot enable lazy loading because CGLIB is not available. Add CGLIB to your classpath.

I do not have CGLIB on my classpath, nor do I want to. I did not have it on my classpath with MyBatis 3.1.1.

So I added an option to my settings to explicit disable lazy loading as follows

<settings>
    <!-- disable lazy loading for testing as we do not want CGLIB on our classpath -->
    <setting name="lazyLoadingEnabled" value="false"/>
    <setting name="useGeneratedKeys" value="false"/>
</settings>

But I still get that failure.

Debugging the code I get to this point

org.apache.ibatis.builder.xml.XMLConfigBuilder#settingsElement

line 198 has:
configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory", "CGLIB")));

I wonder if there is some way to run MyBatis without any proxy factory being CGLIB or JAVASSITS?

Generation With "Long" As Column Not Working

Greetings!

When I have a table with the following attributes:
Table Name: location

Columns:

  1. location_id (int)
  2. location_name (varchar)
  3. latt (float)
  4. long (float)
  5. int (int)
  6. double (float)
  7. string (varchar)

I'm running SQL Server and using the MyBatis Generator plugin for Eclipse along with the following plugins:

  • ToStringPlugin
  • SerializablePlugin
  • EqualsHashCodePlugin

When I go to generate off of my table described above it successfully generates all of the objects but in this case my Location object is incomplete.

What's happening is that the long column has both the getter (getLong()) and setter (setLong()) created but there is no method body that is generated.

Here's the code for Location.java that is generated

import java.io.Serializable;

public class Location implements Serializable {

    /**
     * This field was generated by MyBatis Generator. This field corresponds to the database column location.location_id
     * @mbggenerated  Thu May 30 14:28:41 EDT 2013
     */
    private Integer locationId;
    /**
     * This field was generated by MyBatis Generator. This field corresponds to the database column location.location_name
     * @mbggenerated  Thu May 30 14:28:41 EDT 2013
     */
    private String locationName;
    /**
     * This field was generated by MyBatis Generator. This field corresponds to the database column location.latt
     * @mbggenerated  Thu May 30 14:28:41 EDT 2013
     */
    private Double latt;
    /**
     * This field was generated by MyBatis Generator. This field corresponds to the database column location.Integer
     * @mbggenerated  Thu May 30 14:28:41 EDT 2013
     */
    private Integer integer;
    /**
     * This field was generated by MyBatis Generator. This field corresponds to the database column location.String
     * @mbggenerated  Thu May 30 14:28:41 EDT 2013
     */
    private String string;
    /**
     * This field was generated by MyBatis Generator. This field corresponds to the database table location
     * @mbggenerated  Thu May 30 14:28:41 EDT 2013
     */
    private static final long serialVersionUID = 1L;

    /**
     * This method was generated by MyBatis Generator. This method returns the value of the database column location.location_id
     * @return  the value of location.location_id
     * @mbggenerated  Thu May 30 14:28:41 EDT 2013
     */
    public Integer getLocationId() {
        return locationId;
    }

    /**
     * This method was generated by MyBatis Generator. This method sets the value of the database column location.location_id
     * @param locationId  the value for location.location_id
     * @mbggenerated  Thu May 30 14:28:41 EDT 2013
     */
    public void setLocationId(Integer locationId) {
        this.locationId = locationId;
    }

    /**
     * This method was generated by MyBatis Generator. This method returns the value of the database column location.location_name
     * @return  the value of location.location_name
     * @mbggenerated  Thu May 30 14:28:41 EDT 2013
     */
    public String getLocationName() {
        return locationName;
    }

    /**
     * This method was generated by MyBatis Generator. This method sets the value of the database column location.location_name
     * @param locationName  the value for location.location_name
     * @mbggenerated  Thu May 30 14:28:41 EDT 2013
     */
    public void setLocationName(String locationName) {
        this.locationName = locationName == null ? null : locationName.trim();
    }

    /**
     * This method was generated by MyBatis Generator. This method returns the value of the database column location.latt
     * @return  the value of location.latt
     * @mbggenerated  Thu May 30 14:28:41 EDT 2013
     */
    public Double getLatt() {
        return latt;
    }

    /**
     * This method was generated by MyBatis Generator. This method sets the value of the database column location.latt
     * @param latt  the value for location.latt
     * @mbggenerated  Thu May 30 14:28:41 EDT 2013
     */
    public void setLatt(Double latt) {
        this.latt = latt;
    }

    /**
     * This method was generated by MyBatis Generator. This method returns the value of the database column location.long
     * @return  the value of location.long
     * @mbggenerated  Thu May 30 14:28:41 EDT 2013
     */
    public Double getLong() {
    }

    /**
     * This method was generated by MyBatis Generator. This method sets the value of the database column location.long
     * @param  long the value for location.long
     * @mbggenerated  Thu May 30 14:28:41 EDT 2013
     */
    public void setLong() {
    }

    /**
     * This method was generated by MyBatis Generator. This method returns the value of the database column location.int
     * @return  the value of location.int
     * @mbggenerated  Thu May 30 14:28:41 EDT 2013
     */
    public Integer getInt() {
    }

    /**
     * This method was generated by MyBatis Generator. This method sets the value of the database column location.int
     * @param  int the value for location.int
     * @mbggenerated  Thu May 30 14:28:41 EDT 2013
     */
    public void setInt() {
    }

    /**
     * This method was generated by MyBatis Generator. This method returns the value of the database column location.Integer
     * @return  the value of location.Integer
     * @mbggenerated  Thu May 30 14:28:41 EDT 2013
     */
    public Integer getInteger() {
        return integer;
    }

    /**
     * This method was generated by MyBatis Generator. This method sets the value of the database column location.Integer
     * @param integer  the value for location.Integer
     * @mbggenerated  Thu May 30 14:28:41 EDT 2013
     */
    public void setInteger(Integer integer) {
        this.integer = integer;
    }

    /**
     * This method was generated by MyBatis Generator. This method returns the value of the database column location.String
     * @return  the value of location.String
     * @mbggenerated  Thu May 30 14:28:41 EDT 2013
     */
    public String getString() {
        return string;
    }

    /**
     * This method was generated by MyBatis Generator. This method sets the value of the database column location.String
     * @param string  the value for location.String
     * @mbggenerated  Thu May 30 14:28:41 EDT 2013
     */
    public void setString(String string) {
        this.string = string == null ? null : string.trim();
    }

    /**
     * This method was generated by MyBatis Generator. This method returns the value of the database column location.double
     * @return  the value of location.double
     * @mbggenerated  Thu May 30 14:28:41 EDT 2013
     */
    public Double getDouble() {
    }

    /**
     * This method was generated by MyBatis Generator. This method sets the value of the database column location.double
     * @param  double the value for location.double
     * @mbggenerated  Thu May 30 14:28:41 EDT 2013
     */
    public void setDouble() {
    }

    /**
     * This method was generated by MyBatis Generator. This method corresponds to the database table location
     * @mbggenerated  Thu May 30 14:28:41 EDT 2013
     */
    @Override
    public boolean equals(Object that) {
        if (this == that) {
            return true;
        }
        if (that == null) {
            return false;
        }
        if (getClass() != that.getClass()) {
            return false;
        }
        Location other = (Location) that;
        return (this.getLocationId() == null ? other.getLocationId() == null : this.getLocationId().equals(other.getLocationId()))
                && (this.getLocationName() == null ? other.getLocationName() == null : this.getLocationName().equals(other.getLocationName()))
                && (this.getLatt() == null ? other.getLatt() == null : this.getLatt().equals(other.getLatt()))
                && (this.getLong() == null ? other.getLong() == null : this.getLong().equals(other.getLong()))
                && (this.getInt() == null ? other.getInt() == null : this.getInt().equals(other.getInt()))
                && (this.getInteger() == null ? other.getInteger() == null : this.getInteger().equals(other.getInteger()))
                && (this.getString() == null ? other.getString() == null : this.getString().equals(other.getString()))
                && (this.getDouble() == null ? other.getDouble() == null : this.getDouble().equals(other.getDouble()));
    }

    /**
     * This method was generated by MyBatis Generator. This method corresponds to the database table location
     * @mbggenerated  Thu May 30 14:28:41 EDT 2013
     */
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((getLocationId() == null) ? 0 : getLocationId().hashCode());
        result = prime * result + ((getLocationName() == null) ? 0 : getLocationName().hashCode());
        result = prime * result + ((getLatt() == null) ? 0 : getLatt().hashCode());
        result = prime * result + ((getLong() == null) ? 0 : getLong().hashCode());
        result = prime * result + ((getInt() == null) ? 0 : getInt().hashCode());
        result = prime * result + ((getInteger() == null) ? 0 : getInteger().hashCode());
        result = prime * result + ((getString() == null) ? 0 : getString().hashCode());
        result = prime * result + ((getDouble() == null) ? 0 : getDouble().hashCode());
        return result;
    }

    /**
     * This method was generated by MyBatis Generator. This method corresponds to the database table location
     * @mbggenerated  Thu May 30 14:28:41 EDT 2013
     */
    @Override
    public String toString() {
    }
}

As can be seen above the getters and setters for columns with the name long, int, and double are not properly generated.

Can mybatis support mapper xml extend feature?

For example, I have one common mapper 'CommonUserMapper':

  • CommonUserMapper.java (interface)
  • CommonUserMapper.xml (mapper xml)

Now I want to add some custom methods without changing CommonUserMapper.
So I write a new mapper called CustomUserMapper extends from CommonUserMapper:

  • CustomUserMapper.java (interface)
  • CustomUserMapper.xml (mapper xml)

For this working, I can copy all the codes from the CommonUserMapper.xml to CustomUserMapper.xml.
Of course I can reference some sqls by namespace + ids, but I can't reference the whole select, insert, update, delete...right? So I have to write many duplicate codes.

If the mapper xml can be extended, I think to do this work is more easy.
e.g.

<mapper namespace="demo.CustomUserMapper" extends="demo.CommonUserMapper">

    <!-- Now I don't have to copy other statements used by
         the methods extended from CommonUserMapper.java -->
    <!-- I just define the custom statements -->
    <select id="findByXxx" parameterType="string" resultType="User">
        <!-- findColumn is defined in CommonUserMapper.xml -->
        <include refid="findColumn" />
        where xxx = #{value}
    </select>
    ...
</mapper>

Missing TypeHandler returns null return than throwing an exception

I run into an issue where I mapped a result in a resultMap to Joda's DateTime but didn't register a TypeHandler. Rather than failing with a new error message, it looks like the org.apache.ibatis.executor.resultset.FastResultSetHandler class is returning null in the getPropertyMappingValue method (somewhere near line 316).

This code should be more like this:

  protected Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
    final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
    if (propertyMapping.getNestedQueryId() != null) {
      return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
    } else if (typeHandler != null) {
      final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
      return typeHandler.getResult(rs, column);
    }
    throw new TypeHandlerException("Unknown type " + propertyMapping.getJavaType() + ". You need to register a TypeHandler for this type for MyBatis to correctly convert the result.");
  }

Instead, it looks like this (notice the return null at the end):

  protected Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
    final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
    if (propertyMapping.getNestedQueryId() != null) {
      return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
    } else if (typeHandler != null) {
      final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
      return typeHandler.getResult(rs, column);
    }
    return null;
  }

Ignore

Please ignore this issue. I didn't know that an issue is automatically created for a pull request

"less than" in Select annotations

Reported by mgbckr, Today (6 hours ago)
What version of the MyBatis are you using?
MyBatis 3.2.0, MyBatis-Spring 1.2.0

Please describe the problem. Unit tests are best!
When I use the "less than" sign "<" in a Select annotations, I get an exception is thrown when initializing the application context. If I use "<" it works. In previous MyBatis versions using "<" was working. I think it is counter-intuitive to be forced to use XML entities in Java code if not explicitly documented.

What is the expected output? What do you see instead?
No exception :)

Can you provide stack trace, logs, error messages that are displayed?
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mapper' defined in file [/media/data/Projects/EveryAware/workspaces/development/mybatis-invalid-annotation2/target/classes/de/fstyle/test/mybatis/invalid/annotation/mapper/Mapper.class]: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: org.apache.ibatis.builder.BuilderException: Could not find value method on SQL annotation. Cause: org.apache.ibatis.builder.BuilderException: Error creating document instance. Cause: org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 38; The content of elements must consist of well-formed character data or markup.
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1486)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:524)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:461)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:295)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:292)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:608)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:932)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:479)
at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:139)
at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:83)
at de.fstyle.test.mybatis.invalid.annotation.InvalidAnnotationTest.test(InvalidAnnotationTest.java:17)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:616)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.lang.IllegalArgumentException: org.apache.ibatis.builder.BuilderException: Could not find value method on SQL annotation. Cause: org.apache.ibatis.builder.BuilderException: Error creating document instance. Cause: org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 38; The content of elements must consist of well-formed character data or markup.
at org.mybatis.spring.mapper.MapperFactoryBean.checkDaoConfig(MapperFactoryBean.java:98)
at org.springframework.dao.support.DaoSupport.afterPropertiesSet(DaoSupport.java:44)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1545)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1483)
... 35 more
Caused by: org.apache.ibatis.builder.BuilderException: Could not find value method on SQL annotation. Cause: org.apache.ibatis.builder.BuilderException: Error creating document instance. Cause: org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 38; The content of elements must consist of well-formed character data or markup.
at org.apache.ibatis.builder.annotation.MapperAnnotationBuilder.getSqlSourceFromAnnotations(MapperAnnotationBuilder.java:399)
at org.apache.ibatis.builder.annotation.MapperAnnotationBuilder.parseStatement(MapperAnnotationBuilder.java:241)
at org.apache.ibatis.builder.annotation.MapperAnnotationBuilder.parse(MapperAnnotationBuilder.java:120)
at org.apache.ibatis.binding.MapperRegistry.addMapper(MapperRegistry.java:62)
at org.apache.ibatis.session.Configuration.addMapper(Configuration.java:628)
at org.mybatis.spring.mapper.MapperFactoryBean.checkDaoConfig(MapperFactoryBean.java:95)
... 38 more
Caused by: org.apache.ibatis.builder.BuilderException: Error creating document instance. Cause: org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 38; The content of elements must consist of well-formed character data or markup.
at org.apache.ibatis.parsing.XPathParser.createDocument(XPathParser.java:253)
at org.apache.ibatis.parsing.XPathParser.(XPathParser.java:112)
at org.apache.ibatis.scripting.xmltags.XMLScriptBuilder.(XMLScriptBuilder.java:44)
at org.apache.ibatis.scripting.xmltags.XMLLanguageDriver.createSqlSource(XMLLanguageDriver.java:39)
at org.apache.ibatis.builder.annotation.MapperAnnotationBuilder.buildSqlSourceFromStrings(MapperAnnotationBuilder.java:409)
at org.apache.ibatis.builder.annotation.MapperAnnotationBuilder.getSqlSourceFromAnnotations(MapperAnnotationBuilder.java:392)
... 43 more
Caused by: org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 38; The content of elements must consist of well-formed character data or markup.
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:198)
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.fatalError(ErrorHandlerWrapper.java:177)
at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:391)
at com.sun.org.apache.xerces.internal.impl.XMLScanner.reportFatalError(XMLScanner.java:1404)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.startOfMarkup(XMLDocumentFragmentScannerImpl.java:2583)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2680)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:625)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:488)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:819)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:748)
at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:123)
at com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(DOMParser.java:239)
at com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(DocumentBuilderImpl.java:288)
at org.apache.ibatis.parsing.XPathParser.createDocument(XPathParser.java:251)
... 48 more

Please provide any additional information below.
See: https://code.google.com/p/fstyle-test/source/browse/#git%2Fbug%2Fmybatis-invalid-annotation

Mybatis tries to instantiate an interface

I have an scenario where we have a Multimap. That Multimap is an interface that extends from java.util.Collection. What it does is that every element that you put in there gets added to an internal, so every call to add method a key is extracted from the element and a put is called in beneath. We have an implementation which is presented to Mybatis in an association:

<resultMap id="result" type="ObjectId" autoMapping="false"> <collection property="associationCollection" javaType="CollectionMultiMapImpl" ofType="CollectionType" column="ObjectId" select="AssociationCollection.getByObjectId"/> </resultMap>

It was working fine a couple of times but in one case the association was getting loaded by the org.apache.ibatis.executor.BaseExecutor.DeferredLoad.DeferredLoad.load() and looked that the javaType that was specified in the mapping were not provied to the DeferredLoad, trying to create a new instance of the interface Multimap and of course failing.

I'm using Mybatis 3.1.1 with a DerbyDB under java 6.

NullPointerException when use 'bind' Element

version 3.2.2
we use a 'bind' Element to define match pattern, such as:
<bind name="name_pattern" value="'%' + _parameter.getName() + '%'" />
name is a field of the parameter, if name is null, method @org.apache.ibatis.ognl.OgnlOps.add() will throw a NullPointerException.

Make bean-class configurabe in MapperScannerConfigurer

We need to extend MapperFactoryBean in order to do additional checks (override) method checkDaoConfig().

Unluckily the MapperScannerConfigurer does not provide an option to specify the bean-class to use:

definition.setBeanClass(MapperFactoryBean.class);

SqlSessionTemplate/SqlSessionInterceptor should use public get-methdos

The (private) inner class SqlSessionInterceptor refers to (SqlSessionTemplate's) sqlSessionFactory, executorType and exceptionTranslator directly. Imho the (already existing) plublic get-methods should be used.

We have a scenario where we have a "RoutingSqlSessionFactory" (similar to spring's RoutingDatasource) - therefore we need to override getSqlSessionFactory() in order to return the correct SqlSessionFactory depending on a thread-local. We had to copy SqlSessionTemplate because (private class) SqlSessionProxy does not use the public get-methods.

Performance optimization on ResultFlag(s)

MyBatis (3.2.2) keeps a List to hold ResultFlag in ResultMapping, which can add flags and test later whether containing a flag or not. Instead of List, Set is better for the job which will contribute a little for performance improvement.

And my proposal is a specified BitSet holding ResultFlag as below to replace List.


package org.apache.ibatis.mapping;

import java.util.BitSet;

public class ResultFlagSet extends BitSet {

    public ResultFlagSet() {
        super(ResultFlag.values().length);
    }

    public ResultFlagSet(ResultFlagSet base) {
        this();
        this.or(base);
    }

    public void add(ResultFlag flag) {
        set(flag.ordinal(), true);
    }

    public void clear(ResultFlag flag) {
        set(flag.ordinal(), false);
    }

    public boolean contains(ResultFlag flag) {
        return get(flag.ordinal());
    }

}

Maps inside <foreach> - ${index} is replaced as 0,1,2... not map key

MyBatis version 3.2.1

SELECT * FROM TABLE WHERE
<foreach collection="filters" index="key" item="value" separator="AND">
 UPPER(TRIM(${key})) = UPPER(TRIM(#{value}))
</foreach>

filters is Map<String,String>
In example above ${key} is replaced by ordinal number - 0, 1, 2 etc.
When I change to #{key} it is replaced by true map key but query is not usable since I need column name, not property.

Maybe it's good idea to use value.key/value.value and leave index for ordinal numbering?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.