MyBatis 工具类编写指南
1. 基础工具类实现
1.1 核心工具类
package com.example.util;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class MyBatisUtil {
private static SqlSessionFactory sqlSessionFactory;
private MyBatisUtil() {}
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
throw new RuntimeException("初始化SqlSessionFactory失败", e);
}
}
public static SqlSessionFactory getSqlSessionFactory() {
return sqlSessionFactory;
}
public static SqlSession openSession() {
return sqlSessionFactory.openSession();
}
public static SqlSession openSession(boolean autoCommit) {
return sqlSessionFactory.openSession(autoCommit);
}
public static void closeSession(SqlSession sqlSession) {
if (sqlSession != null) {
sqlSession.close();
}
}
}
1.2 增强版工具类(支持多数据源)
package com.example.util;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
public class EnhancedMyBatisUtil {
private static final Map<String, SqlSessionFactory> SQL_SESSION_FACTORY_MAP = new HashMap<>();
static {
initSqlSessionFactory("mybatis-config.xml", "default");
}
public static void initSqlSessionFactory(String configFile, String dataSourceName) {
try {
InputStream inputStream = Resources.getResourceAsStream(configFile);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SQL_SESSION_FACTORY_MAP.put(dataSourceName, sqlSessionFactory);
} catch (IOException e) {
throw new RuntimeException("初始化SqlSessionFactory失败: " + dataSourceName, e);
}
}
public static SqlSessionFactory getSqlSessionFactory() {
return getSqlSessionFactory("default");
}
public static SqlSessionFactory getSqlSessionFactory(String dataSourceName) {
SqlSessionFactory factory = SQL_SESSION_FACTORY_MAP.get(dataSourceName);
if (factory == null) {
throw new IllegalArgumentException("未找到数据源: " + dataSourceName);
}
return factory;
}
public static SqlSession openSession() {
return openSession("default");
}
public static SqlSession openSession(String dataSourceName) {
return getSqlSessionFactory(dataSourceName).openSession();
}
public static void closeSession(SqlSession sqlSession) {
if (sqlSession != null) {
try {
sqlSession.close();
} catch (Exception e) {
System.err.println("关闭SqlSession时发生异常: " + e.getMessage());
}
}
}
public static void commitAndClose(SqlSession sqlSession) {
if (sqlSession != null) {
try {
sqlSession.commit();
} finally {
closeSession(sqlSession);
}
}
}
public static void rollbackAndClose(SqlSession sqlSession) {
if (sqlSession != null) {
try {
sqlSession.rollback();
} finally {
closeSession(sqlSession);
}
}
}
}
2. 事务管理工具类
2.1 事务模板工具类
package com.example.util;
import org.apache.ibatis.session.SqlSession;
public class TransactionTemplate {
public static <T> T execute(MyBatisCallback<T> callback) {
SqlSession sqlSession = null;
try {
sqlSession = MyBatisUtil.openSession();
T result = callback.doInSqlSession(sqlSession);
sqlSession.commit();
return result;
} catch (Exception e) {
if (sqlSession != null) {
sqlSession.rollback();
}
throw new RuntimeException("事务执行失败", e);
} finally {
MyBatisUtil.closeSession(sqlSession);
}
}
public static void executeWithoutResult(MyBatisCallbackWithoutResult callback) {
execute(sqlSession -> {
callback.doInSqlSession(sqlSession);
return null;
});
}
public static <T> T executeWithRetry(MyBatisCallback<T> callback, int maxRetries) {
int retryCount = 0;
while (retryCount <= maxRetries) {
try {
return execute(callback);
} catch (Exception e) {
retryCount++;
if (retryCount > maxRetries) {
throw e;
}
try {
Thread.sleep(1000 * retryCount);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new RuntimeException("重试被中断", ie);
}
}
}
throw new RuntimeException("重试次数耗尽");
}
}
@FunctionalInterface
interface MyBatisCallback<T> {
T doInSqlSession(SqlSession sqlSession);
}
@FunctionalInterface
interface MyBatisCallbackWithoutResult {
void doInSqlSession(SqlSession sqlSession);
}
2.2 使用示例
public class UserService {
public void createUserWithProfile(User user, UserProfile profile) {
TransactionTemplate.executeWithoutResult(sqlSession -> {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
UserProfileMapper profileMapper = sqlSession.getMapper(UserProfileMapper.class);
userMapper.insert(user);
profile.setUserId(user.getId());
profileMapper.insert(profile);
});
}
public User getUserByIdWithRetry(Long id) {
return TransactionTemplate.executeWithRetry(sqlSession -> {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
return userMapper.selectById(id);
}, 3);
}
}
3. Mapper 管理工具类
3.1 Mapper 工具类
package com.example.util;
import org.apache.ibatis.session.SqlSession;
import java.util.function.Function;
public class MapperUtil {
public static <T, R> R executeMapper(Class<T> mapperClass, Function<T, R> function) {
try (SqlSession sqlSession = MyBatisUtil.openSession()) {
T mapper = sqlSession.getMapper(mapperClass);
R result = function.apply(mapper);
sqlSession.commit();
return result;
}
}
public static <T> void executeMapper(Class<T> mapperClass, Consumer<T> consumer) {
executeMapper(mapperClass, mapper -> {
consumer.accept(mapper);
return null;
});
}
public static <T> void batchInsert(Class<T> mapperClass, List<?> entities, Consumer<T> insertMethod) {
try (SqlSession sqlSession = MyBatisUtil.openSession(ExecutorType.BATCH)) {
T mapper = sqlSession.getMapper(mapperClass);
for (Object entity : entities) {
insertMethod.accept(mapper, entity);
}
sqlSession.commit();
}
}
}
@FunctionalInterface
interface Consumer<T> {
void accept(T t) throws Exception;
}
3.2 使用示例
public class UserService {
public User findUserById(Long id) {
return MapperUtil.executeMapper(UserMapper.class, mapper ->
mapper.selectById(id)
);
}
public void batchCreateUsers(List<User> users) {
MapperUtil.batchInsert(UserMapper.class, users, (mapper, user) ->
mapper.insert(user)
);
}
public void updateUserEmail(Long id, String email) {
MapperUtil.executeMapper(UserMapper.class, mapper -> {
mapper.updateEmail(id, email);
});
}
}
4. 分页工具类
4.1 分页参数类
package com.example.util;
import java.util.Collections;
import java.util.List;
public class Page<T> {
private int pageNum;
private int pageSize;
private long total;
private int pages;
private List<T> records;
public Page() {
this.records = Collections.emptyList();
}
public Page(int pageNum, int pageSize) {
this();
this.pageNum = pageNum;
this.pageSize = pageSize;
}
public int getPageNum() { return pageNum; }
public void setPageNum(int pageNum) { this.pageNum = pageNum; }
public int getPageSize() { return pageSize; }
public void setPageSize(int pageSize) { this.pageSize = pageSize; }
public long getTotal() { return total; }
public void setTotal(long total) {
this.total = total;
if (pageSize > 0) {
this.pages = (int) (total / pageSize);
if (total % pageSize != 0) {
this.pages++;
}
}
}
public int getPages() { return pages; }
public void setPages(int pages) { this.pages = pages; }
public List<T> getRecords() { return records; }
public void setRecords(List<T> records) { this.records = records; }
public int getStartRow() {
return (pageNum - 1) * pageSize;
}
public boolean hasPrevious() {
return pageNum > 1;
}
public boolean hasNext() {
return pageNum < pages;
}
}
4.2 分页工具类
package com.example.util;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
import java.util.function.BiFunction;
public class PageUtil {
public static <T, M> Page<T> paginate(Class<M> mapperClass,
BiFunction<M, RowBounds, List<T>> queryFunction,
int pageNum, int pageSize) {
return paginate(mapperClass, queryFunction, pageNum, pageSize, null);
}
public static <T, M> Page<T> paginate(Class<M> mapperClass,
BiFunction<M, RowBounds, List<T>> queryFunction,
int pageNum, int pageSize,
TotalCounter<M> totalCounter) {
Page<T> page = new Page<>(pageNum, pageSize);
try (SqlSession sqlSession = MyBatisUtil.openSession()) {
M mapper = sqlSession.getMapper(mapperClass);
if (totalCounter != null) {
long total = totalCounter.count(mapper);
page.setTotal(total);
}
if (page.getTotal() == 0) {
return page;
}
RowBounds rowBounds = new RowBounds((pageNum - 1) * pageSize, pageSize);
List<T> records = queryFunction.apply(mapper, rowBounds);
page.setRecords(records);
return page;
}
}
public static <T> List<T> simplePaginate(BiFunction<SqlSession, RowBounds, List<T>> queryFunction,
int pageNum, int pageSize) {
try (SqlSession sqlSession = MyBatisUtil.openSession()) {
RowBounds rowBounds = new RowBounds((pageNum - 1) * pageSize, pageSize);
return queryFunction.apply(sqlSession, rowBounds);
}
}
}
@FunctionalInterface
interface TotalCounter<M> {
long count(M mapper);
}
4.3 使用示例
public class UserService {
public Page<User> getUsersByPage(int pageNum, int pageSize) {
return PageUtil.paginate(
UserMapper.class,
(mapper, rowBounds) -> mapper.selectByPage(rowBounds),
pageNum,
pageSize,
mapper -> mapper.selectCount()
);
}
public Page<User> searchUsers(String keyword, int pageNum, int pageSize) {
return PageUtil.paginate(
UserMapper.class,
(mapper, rowBounds) -> mapper.search(keyword, rowBounds),
pageNum,
pageSize,
mapper -> mapper.searchCount(keyword)
);
}
}
5. 配置工具类
5.1 动态数据源工具类
package com.example.util;
import org.apache.ibatis.session.SqlSessionFactory;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class DynamicDataSourceUtil {
private static final ThreadLocal<String> DATA_SOURCE_CONTEXT = new ThreadLocal<>();
private static final Map<String, SqlSessionFactory> FACTORY_MAP = new ConcurrentHashMap<>();
public static void setDataSource(String dataSourceName) {
DATA_SOURCE_CONTEXT.set(dataSourceName);
}
public static String getDataSource() {
return DATA_SOURCE_CONTEXT.get();
}
public static void clearDataSource() {
DATA_SOURCE_CONTEXT.remove();
}
public static void registerDataSource(String name, SqlSessionFactory factory) {
FACTORY_MAP.put(name, factory);
}
public static SqlSessionFactory getCurrentSqlSessionFactory() {
String dataSourceName = getDataSource();
if (dataSourceName == null) {
dataSourceName = "default";
}
SqlSessionFactory factory = FACTORY_MAP.get(dataSourceName);
if (factory == null) {
throw new IllegalArgumentException("未找到数据源: " + dataSourceName);
}
return factory;
}
public static <T> T executeInDataSource(String dataSourceName, DataSourceCallback<T> callback) {
String previousDataSource = getDataSource();
try {
setDataSource(dataSourceName);
return callback.execute();
} finally {
if (previousDataSource != null) {
setDataSource(previousDataSource);
} else {
clearDataSource();
}
}
}
}
@FunctionalInterface
interface DataSourceCallback<T> {
T execute();
}
5.2 使用示例
public class MultiDataSourceService {
public void syncUserData(Long userId) {
User user = DynamicDataSourceUtil.executeInDataSource("master", () -> {
try (SqlSession sqlSession = DynamicDataSourceUtil.getCurrentSqlSessionFactory().openSession()) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.selectById(userId);
}
});
DynamicDataSourceUtil.executeInDataSource("slave", () -> {
try (SqlSession sqlSession = DynamicDataSourceUtil.getCurrentSqlSessionFactory().openSession()) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.insertOrUpdate(user);
sqlSession.commit();
}
return null;
});
}
}
6. 最佳实践总结
6.1 工具类设计原则
- 单一职责:每个工具类只负责一个特定功能
- 线程安全:确保在多线程环境下安全使用
- 资源管理:自动管理 SqlSession 等资源的生命周期
- 异常处理:提供统一的异常处理机制
- 灵活扩展:支持自定义回调函数
6.2 配置文件示例
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
<typeAliases>
<package name="com.example.entity"/>
</typeAliases>
<plugins>
</plugins>
</configuration>