001/** 002 * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.mybatisflex.core; 017 018import com.mybatisflex.core.datasource.FlexDataSource; 019import com.mybatisflex.core.mybatis.FlexConfiguration; 020import com.mybatisflex.core.mybatis.FlexSqlSessionFactoryBuilder; 021import org.apache.ibatis.logging.Log; 022import org.apache.ibatis.logging.LogFactory; 023import org.apache.ibatis.mapping.Environment; 024import org.apache.ibatis.session.Configuration; 025import org.apache.ibatis.session.SqlSession; 026import org.apache.ibatis.session.SqlSessionFactory; 027import org.apache.ibatis.transaction.TransactionFactory; 028import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; 029import org.apache.ibatis.util.MapUtil; 030 031import javax.sql.DataSource; 032import java.lang.reflect.Proxy; 033import java.util.ArrayList; 034import java.util.List; 035import java.util.Map; 036import java.util.concurrent.ConcurrentHashMap; 037import java.util.concurrent.atomic.AtomicBoolean; 038import java.util.function.Function; 039 040/** 041 * MybatisFlex 的启动类 042 * 043 * <code> 044 * MybatisFlexBootstrap.getInstance() 045 * .setDatasource(...) 046 * .addMapper(...) 047 * .start(); 048 * <p> 049 * <p> 050 * MybatisFlexBootstrap.getInstance() 051 * .execute(...) 052 * </code> 053 */ 054public class MybatisFlexBootstrap { 055 056 protected final AtomicBoolean started = new AtomicBoolean(false); 057 058 protected String environmentId = FlexConsts.NAME; 059 protected TransactionFactory transactionFactory; 060 061 protected FlexDataSource dataSource; 062 protected Configuration configuration; 063 protected List<Class<?>> mappers; 064 065 protected SqlSessionFactory sqlSessionFactory; 066 protected Class<? extends Log> logImpl; 067 068 private final Map<Class<?>, Object> mapperObjects = new ConcurrentHashMap<>(); 069 070 /** 071 * 虽然提供了 getInstance,但也允许用户进行实例化, 072 * 用于创建多个 MybatisFlexBootstrap 实例达到管理多数据源的目的 073 */ 074 public MybatisFlexBootstrap() { 075 076 } 077 078 private static volatile MybatisFlexBootstrap instance; 079 080 public static MybatisFlexBootstrap getInstance() { 081 if (instance == null) { 082 synchronized (MybatisFlexBootstrap.class) { 083 if (instance == null) { 084 instance = new MybatisFlexBootstrap(); 085 } 086 } 087 } 088 return instance; 089 } 090 091 092 public <T> MybatisFlexBootstrap addMapper(Class<T> type) { 093 if (this.mappers == null) { 094 mappers = new ArrayList<>(); 095 } 096 mappers.add(type); 097 return this; 098 } 099 100 101 public MybatisFlexBootstrap start() { 102 if (started.compareAndSet(false, true)) { 103 if (dataSource == null) { 104 throw new IllegalStateException("dataSource can not be null."); 105 } 106 107 //init configuration 108 if (configuration == null) { 109 110 if (transactionFactory == null) { 111 transactionFactory = new JdbcTransactionFactory(); 112 } 113 114 Environment environment = new Environment(environmentId, transactionFactory, dataSource); 115 configuration = new FlexConfiguration(environment); 116 } 117 118 if (logImpl != null) { 119 configuration.setLogImpl(logImpl); 120 } 121 122 //init sqlSessionFactory 123 this.sqlSessionFactory = new FlexSqlSessionFactoryBuilder().build(configuration); 124 125 //init mappers 126 if (mappers != null) { 127 mappers.forEach(configuration::addMapper); 128 } 129 130 LogFactory.getLog(MybatisFlexBootstrap.class).debug("Mybatis-Flex has started."); 131 } 132 133 return this; 134 } 135 136 137 @Deprecated 138 public <R, T> R execute(Class<T> mapperClass, Function<T, R> function) { 139 try (SqlSession sqlSession = openSession()) { 140 T mapper = sqlSession.getMapper(mapperClass); 141 return function.apply(mapper); 142 } 143 } 144 145 146 protected SqlSession openSession() { 147 return sqlSessionFactory.openSession(configuration.getDefaultExecutorType(), true); 148 } 149 150 151 /** 152 * 直接获取 mapper 对象执行 153 * 154 * @param mapperClass 155 * @return mapperObject 156 */ 157 public <T> T getMapper(Class<T> mapperClass) { 158 Object mapperObject = MapUtil.computeIfAbsent(mapperObjects, mapperClass, clazz -> 159 Proxy.newProxyInstance(mapperClass.getClassLoader() 160 , new Class[]{mapperClass} 161 , (proxy, method, args) -> { 162 try (SqlSession sqlSession = openSession()) { 163 T mapper1 = sqlSession.getMapper(mapperClass); 164 return method.invoke(mapper1, args); 165 } 166 })); 167 return (T) mapperObject; 168 } 169 170 171 public String getEnvironmentId() { 172 return environmentId; 173 } 174 175 public MybatisFlexBootstrap setEnvironmentId(String environmentId) { 176 this.environmentId = environmentId; 177 return this; 178 } 179 180 public TransactionFactory getTransactionFactory() { 181 return transactionFactory; 182 } 183 184 public MybatisFlexBootstrap setTransactionFactory(TransactionFactory transactionFactory) { 185 this.transactionFactory = transactionFactory; 186 return this; 187 } 188 189 public DataSource getDataSource() { 190 return dataSource; 191 } 192 193 public MybatisFlexBootstrap setDataSource(DataSource dataSource) { 194 this.dataSource = new FlexDataSource(FlexConsts.NAME, dataSource); 195 return this; 196 } 197 198 public MybatisFlexBootstrap setDataSource(String dataSourceKey, DataSource dataSource) { 199 this.dataSource = new FlexDataSource(dataSourceKey, dataSource); 200 return this; 201 } 202 203 public MybatisFlexBootstrap addDataSource(String dataSourceKey, DataSource dataSource) { 204 if (this.dataSource == null) { 205 this.dataSource = new FlexDataSource(dataSourceKey, dataSource); 206 } else { 207 this.dataSource.addDataSource(dataSourceKey, dataSource); 208 } 209 return this; 210 } 211 212 public Configuration getConfiguration() { 213 return configuration; 214 } 215 216 public MybatisFlexBootstrap setConfiguration(FlexConfiguration configuration) { 217 this.configuration = configuration; 218 this.environmentId = configuration.getEnvironment().getId(); 219 return this; 220 } 221 222 public List<Class<?>> getMappers() { 223 return mappers; 224 } 225 226 227 public SqlSessionFactory getSqlSessionFactory() { 228 return sqlSessionFactory; 229 } 230 231 public MybatisFlexBootstrap setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { 232 this.sqlSessionFactory = sqlSessionFactory; 233 return this; 234 } 235 236 public Class<? extends Log> getLogImpl() { 237 return logImpl; 238 } 239 240 public MybatisFlexBootstrap setLogImpl(Class<? extends Log> logImpl) { 241 this.logImpl = logImpl; 242 return this; 243 } 244}