SqlHelper.java 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. /*
  2. * Copyright (c) 2011-2022, baomidou (jobob@qq.com).
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.baomidou.mybatisplus.extension.toolkit;
  17. import com.baomidou.mybatisplus.core.enums.SqlMethod;
  18. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  19. import com.baomidou.mybatisplus.core.metadata.TableInfo;
  20. import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
  21. import com.baomidou.mybatisplus.core.toolkit.*;
  22. import lombok.SneakyThrows;
  23. import org.apache.ibatis.exceptions.PersistenceException;
  24. import org.apache.ibatis.logging.Log;
  25. import org.apache.ibatis.reflection.ExceptionUtil;
  26. import org.apache.ibatis.session.Configuration;
  27. import org.apache.ibatis.session.ExecutorType;
  28. import org.apache.ibatis.session.SqlSession;
  29. import org.apache.ibatis.session.SqlSessionFactory;
  30. import org.mybatis.spring.MyBatisExceptionTranslator;
  31. import org.mybatis.spring.SqlSessionHolder;
  32. import org.mybatis.spring.SqlSessionUtils;
  33. import org.springframework.transaction.support.TransactionSynchronizationManager;
  34. import java.util.Collection;
  35. import java.util.List;
  36. import java.util.Optional;
  37. import java.util.function.BiConsumer;
  38. import java.util.function.BiPredicate;
  39. import java.util.function.Consumer;
  40. import java.util.function.Supplier;
  41. /**
  42. * SQL 辅助类
  43. *
  44. * @author hubin
  45. * @since 2016-11-06
  46. */
  47. public final class SqlHelper {
  48. /**
  49. * 主要用于 service 和 ar
  50. */
  51. public static SqlSessionFactory FACTORY;
  52. /**
  53. * 批量操作 SqlSession
  54. *
  55. * @param clazz 实体类
  56. * @return SqlSession
  57. */
  58. public static SqlSession sqlSessionBatch(Class<?> clazz) {
  59. // TODO 暂时让能用先,但日志会显示Closing non transactional SqlSession,因为这个并没有绑定.
  60. return sqlSessionFactory(clazz).openSession(ExecutorType.BATCH);
  61. }
  62. /**
  63. * 获取SqlSessionFactory
  64. *
  65. * @param clazz 实体类
  66. * @return SqlSessionFactory
  67. * @since 3.3.0
  68. */
  69. public static SqlSessionFactory sqlSessionFactory(Class<?> clazz) {
  70. return GlobalConfigUtils.currentSessionFactory(clazz);
  71. }
  72. /**
  73. * 获取Session
  74. *
  75. * @param clazz 实体类
  76. * @return SqlSession
  77. */
  78. public static SqlSession sqlSession(Class<?> clazz) {
  79. return SqlSessionUtils.getSqlSession(GlobalConfigUtils.currentSessionFactory(clazz));
  80. }
  81. /**
  82. * 获取TableInfo
  83. *
  84. * @param clazz 对象类
  85. * @return TableInfo 对象表信息
  86. */
  87. public static TableInfo table(Class<?> clazz) {
  88. TableInfo tableInfo = TableInfoHelper.getTableInfo(clazz);
  89. Assert.notNull(tableInfo, "Error: Cannot execute table Method, ClassGenericType not found.");
  90. return tableInfo;
  91. }
  92. /**
  93. * 判断数据库操作是否成功
  94. *
  95. * @param result 数据库操作返回影响条数
  96. * @return boolean
  97. */
  98. public static boolean retBool(Integer result) {
  99. return null != result && result >= 1;
  100. }
  101. /**
  102. * 判断数据库操作是否成功
  103. *
  104. * @param result 数据库操作返回影响条数
  105. * @return boolean
  106. */
  107. public static boolean retBool(Long result) {
  108. return null != result && result >= 1;
  109. }
  110. /**
  111. * 返回SelectCount执行结果
  112. *
  113. * @param result ignore
  114. * @return int
  115. */
  116. public static long retCount(Long result) {
  117. return (null == result) ? 0 : result;
  118. }
  119. /**
  120. * 从list中取第一条数据返回对应List中泛型的单个结果
  121. *
  122. * @param list ignore
  123. * @param <E> ignore
  124. * @return ignore
  125. */
  126. public static <E> E getObject(Log log, List<E> list) {
  127. return getObject(() -> log, list);
  128. }
  129. /**
  130. * @since 3.4.3
  131. */
  132. public static <E> E getObject(Supplier<Log> supplier, List<E> list) {
  133. if (CollectionUtils.isNotEmpty(list)) {
  134. int size = list.size();
  135. if (size > 1) {
  136. Log log = supplier.get();
  137. log.warn(String.format("Warn: execute Method There are %s results.", size));
  138. }
  139. return list.get(0);
  140. }
  141. return null;
  142. }
  143. /**
  144. * 执行批量操作
  145. *
  146. * @param entityClass 实体
  147. * @param log 日志对象
  148. * @param consumer consumer
  149. * @return 操作结果
  150. * @since 3.4.0
  151. */
  152. @SneakyThrows
  153. public static boolean executeBatch(Class<?> entityClass, Log log, Consumer<SqlSession> consumer) {
  154. SqlSessionFactory sqlSessionFactory = sqlSessionFactory(entityClass);
  155. SqlSessionHolder sqlSessionHolder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sqlSessionFactory);
  156. boolean transaction = TransactionSynchronizationManager.isSynchronizationActive();
  157. if (sqlSessionHolder != null) {
  158. SqlSession sqlSession = sqlSessionHolder.getSqlSession();
  159. //原生无法支持执行器切换,当存在批量操作时,会嵌套两个session的,优先commit上一个session
  160. //按道理来说,这里的值应该一直为false。
  161. sqlSession.commit(!transaction);
  162. }
  163. SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
  164. if (!transaction) {
  165. log.warn("SqlSession [" + sqlSession + "] Transaction not enabled");
  166. }
  167. try {
  168. consumer.accept(sqlSession);
  169. //非事务情况下,强制commit。
  170. sqlSession.commit(!transaction);
  171. return true;
  172. } catch (Throwable t) {
  173. sqlSession.rollback();
  174. Throwable unwrapped = ExceptionUtil.unwrapThrowable(t);
  175. if (unwrapped instanceof PersistenceException) {
  176. MyBatisExceptionTranslator myBatisExceptionTranslator
  177. = new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true);
  178. Throwable throwable = myBatisExceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
  179. if (throwable != null) {
  180. throw throwable;
  181. }
  182. }
  183. throw ExceptionUtils.mpe(unwrapped);
  184. } finally {
  185. sqlSession.close();
  186. }
  187. }
  188. /**
  189. * 执行批量操作
  190. *
  191. * @param entityClass 实体类
  192. * @param log 日志对象
  193. * @param list 数据集合
  194. * @param batchSize 批次大小
  195. * @param consumer consumer
  196. * @param <E> T
  197. * @return 操作结果
  198. * @since 3.4.0
  199. */
  200. public static <E> boolean executeBatch(Class<?> entityClass, Log log, Collection<E> list, int batchSize, BiConsumer<SqlSession, E> consumer) {
  201. Assert.isFalse(batchSize < 1, "batchSize must not be less than one");
  202. return !CollectionUtils.isEmpty(list) && executeBatch(entityClass, log, sqlSession -> {
  203. int size = list.size();
  204. int i = 1;
  205. for (E element : list) {
  206. consumer.accept(sqlSession, element);
  207. if ((i % batchSize == 0) || i == size) {
  208. sqlSession.flushStatements();
  209. }
  210. i++;
  211. }
  212. });
  213. }
  214. /**
  215. * 批量更新或保存
  216. *
  217. * @param entityClass 实体
  218. * @param log 日志对象
  219. * @param list 数据集合
  220. * @param batchSize 批次大小
  221. * @param predicate predicate(新增条件) notNull
  222. * @param consumer consumer(更新处理) notNull
  223. * @param <E> E
  224. * @return 操作结果
  225. * @since 3.4.0
  226. */
  227. public static <E> boolean saveOrUpdateBatch(Class<?> entityClass, Class<?> mapper, Log log, Collection<E> list, int batchSize, BiPredicate<SqlSession, E> predicate, BiConsumer<SqlSession, E> consumer) {
  228. String sqlStatement = getSqlStatement(mapper, SqlMethod.INSERT_ONE);
  229. return executeBatch(entityClass, log, list, batchSize, (sqlSession, entity) -> {
  230. if (predicate.test(sqlSession, entity)) {
  231. sqlSession.insert(sqlStatement, entity);
  232. } else {
  233. consumer.accept(sqlSession, entity);
  234. }
  235. });
  236. }
  237. /**
  238. * 获取mapperStatementId
  239. *
  240. * @param sqlMethod 方法名
  241. * @return 命名id
  242. * @since 3.4.0
  243. */
  244. public static String getSqlStatement(Class<?> mapper, SqlMethod sqlMethod) {
  245. return mapper.getName() + StringPool.DOT + sqlMethod.getMethod();
  246. }
  247. /**
  248. * 获取Mapper
  249. *
  250. * @param entityClass 实体
  251. * @param <T> 实体类型
  252. * @return Mapper
  253. */
  254. @SuppressWarnings("unchecked")
  255. public static <T> BaseMapper<T> getMapper(Class<T> entityClass) {
  256. Optional.ofNullable(entityClass).orElseThrow(() -> ExceptionUtils.mpe("entityClass can't be null!"));
  257. TableInfo tableInfo = Optional.ofNullable(TableInfoHelper.getTableInfo(entityClass)).orElseThrow(() -> ExceptionUtils.mpe("Can not find TableInfo from Class: \"%s\".", entityClass.getName()));
  258. String namespace = tableInfo.getCurrentNamespace();
  259. Configuration configuration = tableInfo.getConfiguration();
  260. SqlSession sqlSession = sqlSession(entityClass);
  261. BaseMapper<T> mapper;
  262. try {
  263. mapper = (BaseMapper<T>) configuration.getMapper(Class.forName(namespace), sqlSession);
  264. } catch (ClassNotFoundException e) {
  265. throw ExceptionUtils.mpe(e);
  266. }
  267. return mapper;
  268. }
  269. }