001/*
002 *  Copyright (c) 2022-2025, 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.dialect;
017
018
019import com.mybatisflex.core.FlexGlobalConfig;
020import com.mybatisflex.core.dialect.impl.*;
021import com.mybatisflex.core.util.MapUtil;
022import com.mybatisflex.core.util.ObjectUtil;
023
024import java.util.EnumMap;
025import java.util.Map;
026
027/**
028 * 方言工厂类,用于创建方言
029 */
030public class DialectFactory {
031
032    private DialectFactory() {
033    }
034
035    /**
036     * 数据库类型和方言的映射关系,可以通过其读取指定的方言,亦可能通过其扩展其他方言
037     * 比如,在 mybatis-flex 实现的方言中有 bug 或者 有自己的独立实现,可以添加自己的方言实现到
038     * 此 map 中,用于覆盖系统的方言实现
039     */
040    private static final Map<DbType, IDialect> dialectMap = new EnumMap<>(DbType.class);
041    /**
042     * 通过设置当前线程的数据库类型,以达到在代码执行时随时切换方言的功能
043     */
044    private static final ThreadLocal<DbType> dbTypeThreadLocal = new ThreadLocal<>();
045
046    /**
047     * 获取方言
048     *
049     * @return IDialect
050     */
051    public static IDialect getDialect() {
052        DbType dbType = ObjectUtil.requireNonNullElse(dbTypeThreadLocal.get(),
053            FlexGlobalConfig.getDefaultConfig().getDbType());
054        return MapUtil.computeIfAbsent(dialectMap, dbType, DialectFactory::createDialect);
055    }
056
057    /**
058     * 设置当前线程的 dbType
059     *
060     * @param dbType
061     */
062    public static void setHintDbType(DbType dbType) {
063        dbTypeThreadLocal.set(dbType);
064    }
065
066    /**
067     * 获取当前线程的 dbType
068     *
069     * @return dbType
070     */
071    public static DbType getHintDbType() {
072        return dbTypeThreadLocal.get();
073    }
074
075
076    /**
077     * 清除当前线程的 dbType
078     */
079    public static void clearHintDbType() {
080        dbTypeThreadLocal.remove();
081    }
082
083
084    /**
085     * 可以为某个 dbType 注册(新增或覆盖)自己的方言
086     *
087     * @param dbType  数据库类型
088     * @param dialect 方言的实现
089     */
090    public static void registerDialect(DbType dbType, IDialect dialect) {
091        dialectMap.put(dbType, dialect);
092    }
093
094
095    private static IDialect createDialect(DbType dbType) {
096        switch (dbType) {
097            case MYSQL:
098            case H2:
099            case MARIADB:
100            case GBASE:
101            case OSCAR:
102            case XUGU:
103            case OCEAN_BASE:
104            case CUBRID:
105            case GOLDILOCKS:
106            case CSIIDB:
107            case HIVE:
108            case DORIS:
109                return new CommonsDialectImpl(KeywordWrap.BACK_QUOTE, LimitOffsetProcessor.MYSQL);
110            case CLICK_HOUSE:
111                return new ClickhouseDialectImpl(KeywordWrap.NONE, LimitOffsetProcessor.MYSQL);
112            case GBASE_8S:
113                return new CommonsDialectImpl(KeywordWrap.NONE, LimitOffsetProcessor.MYSQL);
114            case DM:
115                return new DmDialect();
116            case ORACLE:
117                return new OracleDialect();
118            case GAUSS:
119                return new CommonsDialectImpl(KeywordWrap.DOUBLE_QUOTATION, LimitOffsetProcessor.ORACLE);
120            case POSTGRE_SQL:
121            case SQLITE:
122            case HSQL:
123            case KINGBASE_ES:
124            case PHOENIX:
125            case SAP_HANA:
126            case IMPALA:
127            case HIGH_GO:
128            case VERTICA:
129            case REDSHIFT:
130            case OPENGAUSS:
131            case UXDB:
132            case LEALONE:
133                return new CommonsDialectImpl(KeywordWrap.DOUBLE_QUOTATION, LimitOffsetProcessor.POSTGRESQL);
134            case TDENGINE:
135                return new CommonsDialectImpl(KeywordWrap.BACK_QUOTE, LimitOffsetProcessor.POSTGRESQL);
136            case ORACLE_12C:
137                return new OracleDialect(LimitOffsetProcessor.DERBY);
138            case FIREBIRD:
139            case DB2:
140                return new CommonsDialectImpl(KeywordWrap.NONE, LimitOffsetProcessor.DERBY);
141            case DB2_1005:
142                return new DB2105Dialect(KeywordWrap.NONE, DB2105Dialect.DB2105LimitOffsetProcessor.DB2105);
143            case SQLSERVER:
144                return new SqlserverDialectImpl(KeywordWrap.SQUARE_BRACKETS, LimitOffsetProcessor.SQLSERVER);
145            case SQLSERVER_2005:
146                return new Sqlserver2005DialectImpl(KeywordWrap.SQUARE_BRACKETS, LimitOffsetProcessor.SQLSERVER_2005);
147            case INFORMIX:
148                return new CommonsDialectImpl(KeywordWrap.NONE, LimitOffsetProcessor.INFORMIX);
149            case SINODB:
150                return new CommonsDialectImpl(KeywordWrap.DOUBLE_QUOTATION, LimitOffsetProcessor.SINODB);
151            case SYBASE:
152                return new CommonsDialectImpl(KeywordWrap.DOUBLE_QUOTATION, LimitOffsetProcessor.SYBASE);
153            default:
154                return new CommonsDialectImpl();
155        }
156    }
157
158}