#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语句无需结束符。