Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

DBSessionsImpl.java 4.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2020 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.db;
  21. import java.lang.reflect.Field;
  22. import java.util.Objects;
  23. import java.util.function.Supplier;
  24. import org.apache.ibatis.session.SqlSession;
  25. import org.apache.ibatis.session.defaults.DefaultSqlSession;
  26. import org.sonar.api.utils.log.Logger;
  27. import org.sonar.api.utils.log.Loggers;
  28. import static com.google.common.base.Preconditions.checkState;
  29. import static java.lang.String.format;
  30. import static java.lang.Thread.currentThread;
  31. public class DBSessionsImpl implements DBSessions {
  32. private static final Logger LOG = Loggers.get(DBSessionsImpl.class);
  33. private static final ThreadLocal<Boolean> CACHING_ENABLED = ThreadLocal.withInitial(() -> Boolean.FALSE);
  34. private final ThreadLocal<DelegatingDbSessionSupplier> regularDbSession = ThreadLocal.withInitial(this::buildRegularDbSessionSupplier);
  35. private final ThreadLocal<DelegatingDbSessionSupplier> batchDbSession = ThreadLocal.withInitial(this::buildBatchDbSessionSupplier);
  36. private final MyBatis myBatis;
  37. public DBSessionsImpl(MyBatis myBatis) {
  38. this.myBatis = myBatis;
  39. }
  40. private DelegatingDbSessionSupplier buildRegularDbSessionSupplier() {
  41. return new DelegatingDbSessionSupplier(() -> {
  42. DbSession res = myBatis.openSession(false);
  43. ensureAutoCommitFalse(res);
  44. return res;
  45. });
  46. }
  47. private DelegatingDbSessionSupplier buildBatchDbSessionSupplier() {
  48. return new DelegatingDbSessionSupplier(() -> {
  49. DbSession res = myBatis.openSession(true);
  50. ensureAutoCommitFalse(res);
  51. return res;
  52. });
  53. }
  54. private static void ensureAutoCommitFalse(DbSession dbSession) {
  55. try {
  56. SqlSession sqlSession = dbSession.getSqlSession();
  57. if (sqlSession instanceof DefaultSqlSession) {
  58. Field f = sqlSession.getClass().getDeclaredField("autoCommit");
  59. f.setAccessible(true);
  60. checkState(!((boolean) f.get(sqlSession)), "Autocommit must be false");
  61. }
  62. } catch (NoSuchFieldException | IllegalAccessException e) {
  63. LOG.debug("Failed to check the autocommit status of SqlSession: {}", e);
  64. }
  65. }
  66. @Override
  67. public void enableCaching() {
  68. CACHING_ENABLED.set(Boolean.TRUE);
  69. }
  70. @Override
  71. public DbSession openSession(boolean batch) {
  72. if (!CACHING_ENABLED.get()) {
  73. return myBatis.openSession(batch);
  74. }
  75. if (batch) {
  76. return new NonClosingDbSession(batchDbSession.get().get());
  77. }
  78. return new NonClosingDbSession(regularDbSession.get().get());
  79. }
  80. @Override
  81. public void disableCaching() {
  82. close(regularDbSession, "regular");
  83. close(batchDbSession, "batch");
  84. regularDbSession.remove();
  85. batchDbSession.remove();
  86. CACHING_ENABLED.remove();
  87. }
  88. public void close(ThreadLocal<DelegatingDbSessionSupplier> dbSessionThreadLocal, String label) {
  89. DelegatingDbSessionSupplier delegatingDbSessionSupplier = dbSessionThreadLocal.get();
  90. boolean getCalled = delegatingDbSessionSupplier.isPopulated();
  91. if (getCalled) {
  92. try {
  93. DbSession res = delegatingDbSessionSupplier.get();
  94. res.close();
  95. } catch (Exception e) {
  96. LOG.error(format("Failed to close %s connection in %s", label, currentThread()), e);
  97. }
  98. }
  99. }
  100. /**
  101. * A {@link Supplier} of {@link DelegatingDbSession} which logs whether {@link Supplier#get() get} has been called at
  102. * least once, delegates the actual supplying to the a specific {@link Supplier<NonClosingDbSession>} instance and
  103. * caches the supplied {@link NonClosingDbSession}.
  104. */
  105. private static final class DelegatingDbSessionSupplier implements Supplier<DbSession> {
  106. private final Supplier<DbSession> delegate;
  107. private DbSession dbSession;
  108. DelegatingDbSessionSupplier(Supplier<DbSession> delegate) {
  109. this.delegate = delegate;
  110. }
  111. @Override
  112. public DbSession get() {
  113. if (dbSession == null) {
  114. dbSession = Objects.requireNonNull(delegate.get());
  115. }
  116. return dbSession;
  117. }
  118. boolean isPopulated() {
  119. return dbSession != null;
  120. }
  121. }
  122. }