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}