mybatis mapper问题列表

id出现两次

2018-11-14 16:15:03.833 DEBUG 41432 --- [           main] c.a.i.o.d.mapper.DatvMapper.insert   : ==>  Preparing: INSERT INTO dwi_dav_env ( id,gmt_create,gmt_modified,is_deleted,id,day,count,unit,env_type ) VALUES( ?,?,?,?,?,?,?,?,? )

 

Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Column 'id' specified twice
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:425)
at com.mysql.jdbc.Util.getInstance(Util.java:408)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:943)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3970)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3906)

子类id使用@KeySql时, 父类不能有@GeneratedValue(generator = "JDBC")

子类同时有@GeneratedValue和@KeySql, 生效的是第二个?

mapper找不到

mybatis 指定mapper位置是为了扫描这些interface,并生成对应的proxy对象

不是扫描有哪些PO,  interface会引用PO,只要interface被扫描到了,再具体操作PO的CRUD时一般不会有问题。

有问题原因:1. interface没有被扫描到   2. 自定义了一些sql provider (比如逻辑删除),   proxy生成后 对PO进行检查失败 抛异常了

PO可以继承统一的父类,也可以不继承。

mapper中的方法找不到

扫描后 所有mapper的statement都会解析到Configuration.class中

org.apache.ibatis.session.Configuration#hasStatement(java.lang.String, boolean)

原因 重构时①忘记copy xml文件, ②包名变更, namespace没有对应上   

参考:https://blog.csdn.net/softwarehe/article/details/8889206

mybatis-plus中默认xml扫描位置/mapper/**.xml,如果自定义xml位置 ,要修改mybatis-plus.mapperLocation

 mybatis-spring-boot-starter  mybatis-plus-boot-starter  这两个starter最好不要放在一起

pagehelper-spring-boot-starter中默认引用了mybatis-spring-boot-starter,和mybatis-plus分页拦截器是否有冲突?

public class PageHelperAutoConfiguration {
    @Autowired
    private List<SqlSessionFactory> sqlSessionFactoryList;
}

注入了多个sqlSeesionfactory, 判断pagehelper是否生效,启动时在PageHelperAutoConfiguration中打断点,看看这个自动配置是否生效

mybatis.mapper-locations只配置xml位置   
@MapperScan配置的是xxxmapper.java位置

    java.lang.ArrayStoreException: sun.reflect.annotation.TypeNotPresentExceptionProxy

 原因: PageHelper的autoConfig依赖MybatisAutoConfiguration.class,  但是因为集成mybatis-plus的原因 把mybatis-spring-boot-starter排除掉了, 导致MybatisAutoConfiguration.class实际在项目中不存在,  最终导致jvm类加载异常

解决方式: 把mybatis-spring-boot-starter加回来, mapper扫描使用mybatis-plus和mybatis-spring两种配置格式

save之后,id仍然是null

  1. 没有使用包装类
  2. mapper.xml中自定义了 insert方法, 没有走mybatis-plus的baseMapper方法

mapper中出现同名方法

因为是映射到同一个sqlId, 可能会出错,或者某个方法没有注册

继承多个baseDao时,容易出现这种情况

mybatis plugin

github  page-helper 使用线程ThreadLocal绑定参数

bamidou mybatis-plus直接把参数写到mapper的方法内,不使用ThreadLocal, 充分利用mybatis plugin特性

 

分页问题

mybatis-plus的分页插件,默认没有启用, 需要自己加Configuration 

https://mp.baomidou.com/guide/page.html

数据total为0时,不再加limit   日志中就会看不到limit,    通过PaginationInterceptor debug查看是否启用

 

多参数绑定  xml中以 对象名.(点)对象属性名 的方式访问

结果映射:找不到field  值为null,  定位Column名字不对 

createTime  createdTime

根据db column name ---> set field

xml中的columnname可以是CREATED_TIME或者createdTime  (官方示例), mybatis根据是否有大小写 自动判断是否要用驼峰命名 

@TableField注解并不会处理xml中的result mapping,  列名和属性名对应不上,值就会是null,  这个注解只在使用MP的baseMapper相关方法中有效

使用@Select时, 如果有列名不一致,需要配合使用@Results

 

 

结果映射的其他规则

  1. 返回值是list, 数据库值为null, java中的结果是new ArrayList(), 和hibernate规则一致,不需要判断 if(xx != null)
    org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleResultSets

  2. 返回值是单个Object, 数据库值为null, java中的结果也为null, 需要判断xxx!=null

  3. 数据库值为多个, 直接抛异常TooManyResultsException
    org.apache.ibatis.session.defaults.DefaultSqlSession#selectOne(java.lang.String, java.lang.Object)

  4. 返回基本类型int时 会报NPE异常

ifnull(max(xxx), 0)
  1. 同一个DB COLUMN 可以映射到多个java field
    这个在项目重构过程中 很有用的一个特性。源代码: org.apache.ibatis.executor.resultset.DefaultResultSetHandler#applyPropertyMappings

  2. 自动结果映射 可以混用java field name和DB COLUMN_NAME, 但是有个条件是 resultMap中不能再嵌套 <association property="xxx" resultMap="xxxMap"/> !!!

association和自动映射有冲突, 因为auto-mapping可能会把一个COLLUM映射给两个不通entity的同名字段
https://mybatis.org/mybatis-3/sqlmap-xml.html#Auto-mapping

例子


本来STATUS是自动映射的,没有这一句, 但是随着业务发展 加上了association, 这时auto-mapping失效了 造成status=null

BaseTypeHandler

对应hibernate中的@Type注解,实现类型转换

如果需要Enum类型的数据 insert的时候插入code, select的时候返回text, 则可以自定义一个TypeHandler (同时在mapper中使用typeHandler引用这个类), 实现互转

但并不适用与PreparedStatement.setArray("1", connection.createArrayOf("aa", "bb", "cc"))实现一个?占位符 传递多个参数的情形。
setArray只在少数数据库支持,mysql, oracle都不支持这个JDBC特性

这里也可以用于JSON--> VARCHAR的自动互转

mybatis plus中的@TableField(typeHandler=xxx.class) 需要配合class上的 @TableName(autoResultMap=true)使用,默认autoResultMap=false

对于带有DB function的column 不能用@TableField直接映射,

比如 case when , sum , ... , @TableField("NVL(xxx,yyy)") 在oracle中虽然可以映射成功 (表达式中间不能有空格),但是其他数据库就不一定了,不通用
正确的做法是
方法1:xxx, yyy各映射一个字段, 在应用层实现DB function的功能
方法2:在xml中使用<resultMap>, 不使用BaseMapper中的方法

hibernate的做法是配置一个@Formula(), 而不是@Column https://stackoverflow.com/questions/2986318/how-to-map-calculated-properties-with-jpa-and-hibernate

Logging

org.apache.ibatis.logging.jdbc.ConnectionLogger

org.apache.ibatis.logging.jdbc.PreparedStatementLogger

 

SELECT SQL为什么要加事务

非常简单的GET当然可以不加,
但业务上通常很复杂,同一条sql要执行多次(多个方法要引用),不加事务的话就不能利用到mybatis的一级缓存,多次执行同一条sql,徒然增加了数据库的压力.
加上事务 就可以用到一级缓存了, 这也是hibernate必须加事务的原因(状态管理是另一方面)

transaction selectById

aaa = xxxMapper.selectById()
aaa = xxxMapper.selectById()  # 此调用用到了缓存!!
xxxMapper.updateXXX()

bbb = xxxMapper.selectById()  # 此调用没用缓存!!!, 因为上一步有updateXXX操作,mybatis自动更新策略不使用缓存

include 标签

<include> 是可以传静态参数


<include refId='xxx'>
    <property name='yyy1' value='yyyy2'
</include>

引用property时要用 ${}, 不能用#

bind标签可以调用java的静态代码

ognl能力 @class@method(#{xxxx})

占位符设置默认值

${username:aa_user}
要用${:} 这个才用到OGNl, #{:}这种好像不行

启用并修改成其他的分割符:

mybatis-plus.configuration-properties.org.apache.ibatis.parsing.PropertyParser.enable-default-value=true
mybatis-plus.configuration-properties.org.apache.ibatis.parsing.PropertyParser.default-value-separator=?:

@Param用还是不用

https://stackoverflow.com/questions/59668117/how-to-properly-use-the-param-annotation-of-mybatis

jdk8之后, 单个参数的时候才需要用

多数据源

@MapperScan()中有个sqlSessionFactoryRef,专门用来加载多数据源的

根据不同的目录,创建对应的sessessionFactory就行了
interceptor plugin也是加载到sessionFactory的

MapperScannerConfigurer.postProcessBeanDefinitionRegistry()

mapper是绑定到sessionFactory的, mapper调用的时候就能找到对应datasource的sessionFactory.openSeesion()
https://developpaper.com/analyzing-the-session-mechanism-of-mybatis-from-the-perspective-of-source-code/

一段异常

Caused by: org.apache.ibatis.builder.BuilderException: Error evaluating expression ''. Cause: org.apache.ibatis.ognl.ExpressionSyntaxException: Malformed OGNL expression:  [org.apache.ibatis.ognl.ParseException: Encountered "<EOF>" at line 1, column 0.
Was expecting one of:
    ":" ...
    "not" ...
    "+" ...
    "-" ...
    "~" ...
    "!" ...
    "(" ...
    "true" ...
    "false" ...
    "null" ...
    "#this" ...
    "#root" ...
    "#" ...
    "[" ...
    "{" ...
    "@" ...
    "new" ...
    <IDENT> ...
    <DYNAMIC_SUBSCRIPT> ...
    "\'" ...
    "`" ...
    "\"" ...
    <INT_LITERAL> ...
    <FLT_LITERAL> ...
    ]
	at org.apache.ibatis.scripting.xmltags.OgnlCache.getValue(OgnlCache.java:48)
	at org.apache.ibatis.scripting.xmltags.ExpressionEvaluator.evaluateIterable(ExpressionEvaluator.java:43)
	at org.apache.ibatis.scripting.xmltags.ForEachSqlNode.apply(ForEachSqlNode.java:54)
	at org.apache.ibatis.scripting.xmltags.MixedSqlNode.lambda$apply$0(MixedSqlNode.java:32)
	at java.util.ArrayList.forEach(ArrayList.java:1259)
	at org.apache.ibatis.scripting.xmltags.MixedSqlNode.apply(MixedSqlNode.java:32)
	at org.apache.ibatis.scripting.xmltags.DynamicSqlSource.getBoundSql(DynamicSqlSource.java:39)
	at org.apache.ibatis.mapping.MappedStatement.getBoundSql(MappedStatement.java:305)
	at com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor.intercept(MybatisPlusInterceptor.java:69)
	at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:62)
	at com.sun.proxy.$Proxy316.query(Unknown Source)
	at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:151)
	at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:145)
	at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:140)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:427)
	... 137 common frames omitted
Caused by: org.apache.ibatis.ognl.ExpressionSyntaxException: Malformed OGNL expression: 
	at org.apache.ibatis.ognl.Ognl.parseExpression(Ognl.java:181)
	at org.apache.ibatis.scripting.xmltags.OgnlCache.parseExpression(OgnlCache.java:55)
	at org.apache.ibatis.scripting.xmltags.OgnlCache.getValue(OgnlCache.java:46)
	... 155 common frames omitted
Caused by: org.apache.ibatis.ognl.ParseException: Encountered "<EOF>" at line 1, column 0.
Was expecting one of:


这里解析失败,直接导致ms.getBoundSql返回了null

if test中的String比较

string方法是可以直接调用的

<if test='xxx.indexOf("xxx")>0' > ...

<if test='xxx.contains("xxx")' > ...

对于单个char的比较,需要加toString() 或者用双引号比较。 否则有NumberFormatException
<if test='xxx == "Y"' > ...

<if test="xxx == 'Y'.toString()" > ...

查找param.method()时 大小写不敏感, 但是param.field大小写是敏感的
In MyBatis, the if statement in XML mapping files is case-insensitive when checking against Java methods. This means that param.isXxxMethod() is case-insensitive, and it will match both isXxxMethod and isxxxmethod in Java.

For example, if you have the following if statement in your MyBatis XML mapping file:

<if test="param.isXxxMethod()">
    ...
</if>

It would match both param.isXxxMethod() and param.isxxxmethod() methods in your Java code, regardless of the case of the Xxx part.

However, it is worth mentioning that this case-insensitive behavior only applies when matching against Java methods. If you are using other variables or properties in the if statement, the comparison may be case-sensitive.

posted @ 2018-11-14 16:17  funny_coding  阅读(1123)  评论(0)    收藏  举报
build beautiful things, share happiness