#Mybatis示例和常见问题

# Mybatis基础示例

# 数据库脚本

-- 建表语句
CREATE TABLE user1 
(
	id INT PRIMARY KEY,
	name VARCHAR(30) NULL
);
-- 预置5条数据
INSERT INTO USER1 (id, name) VALUES
(1, 'Jone'),
(2, 'Jack',),
(3, 'Tom',),
(4, 'Sandy'),
(5, 'Billie');

# 创建entity

创建User用户的entity,并使其与Table对应。

import lombok.Data;

@Data
public class User {
    private Long id;
    private String name;
   
}

# 创建User的Mapper文件

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.dao.UserMapper">
    <select id="getAllUsers" resultType="com.example.pojo.User">
        select * from user1
    </select>
    <insert id="insertUser" parameterType="com.example.pojo.User">
        insert into user1(id,name) values(#{id},#{name})
    </insert>

</mapper>


import java.util.List;

@Mapper
public interface UserMapper {
    List<User> getAllUsers();
    void insertUser(User user);
}

# 创建单元测试类

public class UserTest {

    @Autowired
    private UserMapper userMapper;
    
    @Test
    public void testAddUser() {
        assertEquals(5, userMapper.getAllUsers().size());// 建表时预置了5条数据
        userMapper.insertUser(new User(20,"zhang"));
        List<User> list = userMapper.getAllUsers();
        assertEquals(6, list.size());
    }
}

# 常见问题

# 自增主键的使用问题

一般情况下YashanDB里自增主键都是通过序列实现的,应用使用自增主键主要分为以下情形:

  • 情形一:插入语句中不带id,让服务端通过序列自动生成id。
  • 情形二:插入语句中不带id,让服务端通过序列自动生成id,并且在语句执行结束后返回所生成的id。

情形一中无需做任何配置,只要数据库里定义表结构时指定自增序列,以及mapper.xml在定义SQL语句时不带主键直接插入即可。

    <insert id="insertUserWithoutId" parameterType="com.example.pojo.User">
        -- 插入时不指定id的值
        insert into user1(name) values(#{name})  
    </insert>

情形二则需要mapper.xml定义时加上如下参数:

参数 默认值 含义
useGeneratedKeys false 是否返回主键,需要返回主键时应设置为true
keyColumn 主键列的列名(表结构里定义的列名)
keyProperty 主键所对应的字段名(Entity里定义的字段名,如果与表结构里定义的相同,可省略)

mapper.xml示例

    <insert id="insertUserReturnKey" parameterType="com.example.pojo.User" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
        insert into user1(name) values(#{name})  -- 插入时不指定id的值
    </insert>

效果如下:

    @Test
    public void testAddUser() {
        User user = new User("zhang"); // 创建user时不指定id的值
        userMapper.insertUserReturnKey(user);// 插入
        assertNotEquals(0,user.getId()); // 执行完插入后,数据库里自动生成的id值会自动回填到user对象中。
    }

YashanDB目前仅支持获取单行插入语句的返回值。在多行插入时,若使用了useGeneratedKeys等参数来返回主键值将会发生报错:

解决办法

  • 去掉useGeneratedKeys等参数。
  • 采取别的办法实现多行插入并获取返回值,例如把多行插入改造成单行插入然后在Java代码里循环处理。

# CLOB序列化成JSON时报错

业务中经常有把查询出来的结果直接返回到前端页面的场景,在这个过程中spring会自动把Entity对象序列化成JSON串。

由于YashanDB的CLOB和BLOB不能被序列化,所以如果Entity对象中含有CLOB和BLOB类型的字段时,此场景下就会报错,错误信息大致如下:

Type definition error: [simple type, class com.yashandb.jdbc.YasLobInputStream]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException:No serializer found for class com.yashandb.jdbc.YasLobInputStream and no properties discovered to create BeanSerializer(to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)(through reference chain: com.lead.common.core.domain.AjaxResult["data"]->java.util.ArrayList[0]->com.lead.common.core.domain.vo.ComboBoxVo["value"]->com.alibaba.druid.proxy.jdbc.ClobProxyImpl["asciiStream"])

解决办法

entity定义时,将数据库中的CLOB类型字段直接定义成String,将数据库中的BLOB类型字段直接定义成byte[],如下所示:

import lombok.Data;

@Data
public class User {
    private Long id;
    private String name;
    private String clobVal;
    private byte[] blobVal;
}

既避免了因为无法序列化而报错的问题,又省去了因为处理LOB类型带来的额外的处理逻辑。

另外,对于返回值为CLOB类型的数据库函数(例如GROUP_CONCAT),在使用时也应用String去承接其返回值。

# useColumnLabel配置项不生效

在Mybatis中,配置项seColumnLabel默认为true,即使用ColumnLabel实现字段映射,如果配置为false则表示使用ColumnName映射。

在YashanDB中,在存在别名的情况下,resultsetmetaData的getColumnLabel和getColumnName接口返回的都是别名(按JDBC标准,getColumnName应该返回列名),useColumnLabel设置为true或false的实际表现完全相同,即只会用别名映射。

例如执行select id userId,name userName from table1;时,不论useColumnLabel设置如何,永远使用userId和userName作为字段名去映射。

# SQL语句结束符

SQL语句的结束符并不固定,具体取决于使用的数据库管理系统和执行环境。通常情况下,SQL语法规则要求SQL语句必须采用分号 (;) 作为结束符,但在Mybatis环境中SQL语句无需结束符。