Mybatis Common Mapper常见问题解答:StackOverflow精选
【免费下载链接】Mapper Mybatis Common Mapper - Easy to use 项目地址: https://gitcode.com/gh_mirrors/ma/Mapper
引言
你是否在使用Mybatis Common Mapper时遇到过各种令人头疼的异常?是否在StackOverflow上苦苦搜索却找不到满意的答案?本文精选了Mybatis Common Mapper开发中最常见的10个问题,提供专业、详细的解决方案和代码示例,帮助你快速解决开发难题。
读完本文,你将能够:
- 解决多@Id注解导致的MapperException异常
- 正确配置@Table注解实现实体类与数据库表映射
- 处理@Version乐观锁注解使用中的常见问题
- 解决LogicDelete逻辑删除功能的实现问题
- 掌握KeySql注解的正确使用方法
- 解决mapperLocations配置路径问题
- 解决MapperScannerConfigurer配置错误
- 解决继承通用Mapper接口时的类型参数问题
- 解决Example查询中的条件构造问题
- 解决批量操作中的性能问题
1. 多@Id注解导致的MapperException异常
问题描述
在实体类中使用多个@Id注解时,调用deleteByIds或selectByIds方法会抛出MapperException异常:"继承 deleteByIds 方法的实体类[...]中必须只有一个带有 @Id 注解的字段"。
解决方案
Mybatis Common Mapper要求继承deleteByIds或selectByIds方法的实体类只能有一个@Id注解字段。如果需要使用联合主键,应该实现自定义的批量操作方法。
代码示例
错误示例:
public class UserLogin {
@Id
private Integer userId;
@Id
private String username;
// getter和setter
}
正确示例(单主键):
public class User {
@Id
private Integer id;
private String username;
// getter和setter
}
联合主键解决方案:
// 1. 创建复合主键类
public class UserLoginKey implements Serializable {
private Integer userId;
private String username;
// getter、setter和equals、hashCode方法
}
// 2. 在实体类中使用@IdClass注解
@IdClass(UserLoginKey.class)
public class UserLogin {
@Id
private Integer userId;
@Id
private String username;
// 其他字段和getter、setter
}
// 3. 自定义批量操作方法
public interface UserLoginMapper extends Mapper<UserLogin> {
// 自定义批量删除方法
int deleteByIds(@Param("ids") List<UserLoginKey> ids);
}
2. @Table注解配置问题
问题描述
实体类使用@Table注解后,仍然无法正确映射到数据库表,导致SQL语句中表名错误。
解决方案
正确配置@Table注解的name属性,确保与数据库表名一致。如果表名包含特殊字符或关键字,需要使用反引号`包裹。
代码示例
正确示例1:基本用法
@Table(name = "user_info")
public class UserInfo {
@Id
private Integer id;
private String username;
// getter和setter
}
正确示例2:包含特殊字符的表名
@Table(name = "`user`") // 当表名为关键字时使用反引号
public class User {
@Id
private Integer id;
private String username;
// getter和setter
}
正确示例3:schema和catalog配置
@Table(name = "user", schema = "public", catalog = "testdb")
public class User {
@Id
private Integer id;
private String username;
// getter和setter
}
3. @Version乐观锁注解使用问题
问题描述
使用@Version注解实现乐观锁时,抛出"中包含多个带有 @Version 注解的字段,一个类中只能存在一个带有 @Version 注解的字段!"异常,或更新操作未触发版本检查。
解决方案
确保实体类中只有一个@Version注解字段,并且该字段的类型正确。同时,在更新操作中必须包含版本字段。
代码示例
正确示例:
public class User {
@Id
private Integer id;
private String username;
@Version
private Integer version; // 版本号字段,支持Integer、Long、Timestamp等类型
// getter和setter
}
// 使用示例
public void updateUser(UserMapper userMapper, User user) {
try {
int rows = userMapper.updateByPrimaryKeySelective(user);
if (rows == 0) {
throw new OptimisticLockException("更新失败,数据已被其他用户修改");
}
} catch (Exception e) {
// 异常处理
}
}
4. LogicDelete逻辑删除功能问题
问题描述
实现逻辑删除功能后,调用delete方法并未更新逻辑删除字段,而是执行了物理删除。
解决方案
使用@LogicDelete注解标记逻辑删除字段,并确保实体类对应的Mapper接口继承了正确的通用Mapper。
代码示例
实体类示例:
@Table(name = "tb_user")
public class TbUserLogicDelete {
@Id
private Long id;
private String username;
private String password;
@LogicDelete(value = "1", delval = "0")
private Integer isValid; // 逻辑删除字段,1表示有效,0表示删除
// getter和setter
}
Mapper接口示例:
public interface TbUserLogicDeleteMapper extends Mapper<TbUserLogicDelete> {
// 继承通用Mapper接口
}
使用示例:
public void testLogicDelete() {
SqlSession sqlSession = getSqlSession();
try {
TbUserLogicDeleteMapper logicDeleteMapper = sqlSession.getMapper(TbUserLogicDeleteMapper.class);
TbUserLogicDelete tbUserLogicDelete = new TbUserLogicDelete();
tbUserLogicDelete.setUsername("test");
// 执行逻辑删除,实际上会执行UPDATE语句更新isValid字段
Assert.assertEquals(1, logicDeleteMapper.delete(tbUserLogicDelete));
} finally {
sqlSession.close();
}
}
5. KeySql注解使用问题
问题描述
使用@KeySql注解生成主键时,出现主键值未正确生成或生成策略不符合预期的问题。
解决方案
根据数据库类型和主键生成策略,正确配置@KeySql注解的属性。
代码示例
示例1:使用数据库自增主键
public class UserAutoIncrement {
@Id
@KeySql(useGeneratedKeys = true) // 使用数据库自增主键
private Integer id;
private String username;
// getter和setter
}
示例2:使用特定数据库的IDENTITY生成器
public class UserAutoIncrementIdentity {
@Id
@KeySql(dialect = IdentityDialect.MYSQL) // 指定数据库方言
private Integer id;
private String username;
// getter和setter
}
示例3:使用SQL语句生成主键
public class UserSqlBefore {
@Id
@KeySql(sql = "select 12345", order = ORDER.BEFORE) // 插入前执行SQL获取主键
private Integer id;
private String username;
// getter和setter
}
public class UserSqlAfter {
@Id
@KeySql(sql = "SELECT LAST_INSERT_ID()", order = ORDER.AFTER) // 插入后执行SQL获取主键
private Integer id;
private String username;
// getter和setter
}
示例4:使用自定义主键生成器
public class Country {
@Id
@KeySql(genId = SimpleGenId.class) // 使用自定义主键生成器
private String id;
private String name;
// getter和setter
}
// 自定义主键生成器
public class SimpleGenId implements GenId<String> {
@Override
public String genId(String table, String column) {
return UUID.randomUUID().toString().replace("-", "");
}
}
6. mapperLocations配置路径问题
问题描述
在Spring或Spring Boot配置中,设置mapperLocations属性后,Mybatis无法找到映射文件,导致绑定异常。
解决方案
正确配置mapperLocations路径,确保能匹配到所有的Mapper XML文件。注意路径中的通配符使用和资源文件的实际位置。
代码示例
Spring Boot配置示例:
mybatis:
mapper-locations: classpath*:tk/mybatis/mapper/xml/**/*.xml
Spring配置示例:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath*:tk/mybatis/mapper/xml/**/*.xml" />
</bean>
正确的资源文件目录结构:
src/main/resources/
tk/
mybatis/
mapper/
xml/
CountryMapper.xml
UserMapper.xml
7. MapperScannerConfigurer配置错误
问题描述
配置MapperScannerConfigurer后,Spring无法扫描到Mapper接口,导致@Autowired注入失败。
解决方案
正确配置MapperScannerConfigurer的basePackage属性,确保覆盖所有Mapper接口所在的包。同时注意使用tk.mybatis.spring.mapper.MapperScannerConfigurer而非MyBatis原生的类。
代码示例
Spring配置示例:
<bean class="tk.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="tk.mybatis.mapper" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
<property name="markerInterface" value="tk.mybatis.mapper.common.Mapper" />
</bean>
Spring Boot注解配置示例:
@SpringBootApplication
@MapperScan(basePackages = "tk.mybatis.mapper", markerInterface = Mapper.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
8. 继承通用Mapper接口时的类型参数问题
问题描述
创建Mapper接口继承通用Mapper时,如果类型参数指定错误,会导致编译错误或运行时异常。
解决方案
确保Mapper接口正确指定了泛型参数,即实体类类型。
代码示例
正确示例:
public interface CountryMapper extends Mapper<Country> {
// 继承通用Mapper接口,指定实体类类型
}
public interface UserMapper extends Mapper<User>, MySqlMapper<User> {
// 同时继承多个通用接口
}
错误示例:
public interface CountryMapper extends Mapper {
// 错误:未指定泛型参数
}
public interface UserMapper extends Mapper<Long> {
// 错误:泛型参数应为实体类类型,而非基本类型
}
9. Example查询中的条件构造问题
问题描述
使用Example进行复杂条件查询时,出现查询条件不正确或无法构造复杂SQL的问题。
解决方案
正确使用Example和Criteria构造查询条件,注意不同条件方法的正确调用顺序和组合方式。
代码示例
基本查询示例:
public List<Country> selectByExample() {
Example example = new Example(Country.class);
example.createCriteria()
.andEqualTo("continent", "Asia")
.andLike("name", "%China%");
// 添加排序条件
example.orderBy("population").desc();
return countryMapper.selectByExample(example);
}
复杂OR条件示例:
public List<Country> selectWithOrCondition() {
Example example = new Example(Country.class);
Example.Criteria criteria1 = example.createCriteria()
.andEqualTo("continent", "Asia")
.andGreaterThan("population", 100000000);
Example.Criteria criteria2 = example.createCriteria()
.andEqualTo("continent", "Europe")
.andLike("name", "%United%");
example.or(criteria2); // 使用OR组合两个条件
return countryMapper.selectByExample(example);
}
Between条件示例:
public List<Country> selectWithBetween() {
Example example = new Example(Country.class);
example.createCriteria()
.andBetween("population", 10000000, 100000000);
return countryMapper.selectByExample(example);
}
10. 批量操作中的性能问题
问题描述
使用通用Mapper的批量操作方法时,遇到性能问题或内存溢出。
解决方案
对于大量数据的批量操作,应该使用分批处理的方式,避免一次性加载过多数据到内存。同时可以考虑使用Mybatis的BatchExecutor提高性能。
代码示例
分批插入示例:
public void batchInsertUsers(List<User> userList, int batchSize) {
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
try {
for (int i = 0; i < userList.size(); i++) {
userMapper.insert(userList.get(i));
if ((i + 1) % batchSize == 0) {
sqlSession.flushStatements();
sqlSession.clearCache();
}
}
sqlSession.commit();
} catch (Exception e) {
sqlSession.rollback();
throw e;
} finally {
sqlSession.close();
}
}
使用InsertListMapper批量插入:
public interface UserMapper extends Mapper<User>, InsertListMapper<User> {
// 继承InsertListMapper接口获得批量插入能力
}
public void batchInsertUsingInsertList(List<User> userList) {
// InsertListMapper提供的insertList方法
userMapper.insertList(userList);
}
总结
本文详细介绍了Mybatis Common Mapper开发中10个常见问题的解决方案,涵盖了注解使用、配置问题、查询构造和性能优化等方面。掌握这些解决方案可以帮助你快速解决开发中的大部分问题,提高开发效率。
Mybatis Common Mapper作为一个功能强大的MyBatis增强工具,还有很多高级特性值得探索。建议深入阅读官方文档,了解更多最佳实践和性能优化技巧。
附录:常见异常及其解决方案对照表
| 异常信息 | 解决方案 |
|---|---|
| "继承 deleteByIds 方法的实体类[...]中必须只有一个带有 @Id 注解的字段" | 确保实体类中只有一个@Id注解字段,联合主键场景需自定义批量操作方法 |
| "类 [...] 不包含属性 [...],或该属性被@Transient注释!" | 检查Example查询中使用的属性名是否与实体类一致,确保没有被@Transient注解 |
| "中包含多个带有 @Version 注解的字段" | 确保实体类中只有一个@Version注解字段 |
| "not found for parameter: entity" | 检查Mapper接口方法参数是否正确,确保使用@Param注解指定参数名 |
| "Could not find value method on SQL annotation. Cause: java.lang.reflect.InvocationTargetException" | 检查@SelectProvider等注解的方法是否正确实现 |
希望本文能帮助你解决Mybatis Common Mapper使用中的常见问题。如果有其他问题或解决方案,欢迎在评论区分享交流!
【免费下载链接】Mapper Mybatis Common Mapper - Easy to use 项目地址: https://gitcode.com/gh_mirrors/ma/Mapper
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



