/* * The MIT License (MIT) * * Copyright (c) 2014-2023 abel533@gmail.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.github.pagehelper.page; import com.github.pagehelper.AutoDialect; import com.github.pagehelper.Dialect; import com.github.pagehelper.PageException; import com.github.pagehelper.dialect.AbstractHelperDialect; import com.github.pagehelper.dialect.auto.*; import com.github.pagehelper.dialect.helper.*; import com.github.pagehelper.util.ClassUtil; import com.github.pagehelper.util.StringUtil; import org.apache.ibatis.mapping.MappedStatement; import javax.sql.DataSource; import java.util.LinkedHashMap; import java.util.Map; import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantLock; /** * 基础方言信息 * * @author liuzh */ public class PageAutoDialect { private static Map> dialectAliasMap = new LinkedHashMap<>(); private static Map> autoDialectMap = new LinkedHashMap<>(); public static void registerDialectAlias(String alias, Class dialectClass) { dialectAliasMap.put(alias, dialectClass); } static { //注册别名 registerDialectAlias("hsqldb", HsqldbDialect.class); registerDialectAlias("h2", HsqldbDialect.class); registerDialectAlias("phoenix", HsqldbDialect.class); registerDialectAlias("postgresql", PostgreSqlDialect.class); registerDialectAlias("mysql", MySqlDialect.class); registerDialectAlias("mariadb", MySqlDialect.class); registerDialectAlias("sqlite", MySqlDialect.class); registerDialectAlias("herddb", HerdDBDialect.class); registerDialectAlias("oracle", OracleDialect.class); registerDialectAlias("oracle9i", Oracle9iDialect.class); registerDialectAlias("db2", Db2Dialect.class); registerDialectAlias("as400", AS400Dialect.class); registerDialectAlias("informix", InformixDialect.class); //解决 informix-sqli #129,仍然保留上面的 registerDialectAlias("informix-sqli", InformixDialect.class); registerDialectAlias("sqlserver", SqlServerDialect.class); registerDialectAlias("sqlserver2012", SqlServer2012Dialect.class); registerDialectAlias("derby", SqlServer2012Dialect.class); //达梦数据库,https://github.com/mybatis-book/book/issues/43 registerDialectAlias("dm", OracleDialect.class); //阿里云PPAS数据库,https://github.com/pagehelper/Mybatis-PageHelper/issues/281 registerDialectAlias("edb", OracleDialect.class); //神通数据库 registerDialectAlias("oscar", OscarDialect.class); registerDialectAlias("clickhouse", MySqlDialect.class); //瀚高数据库 registerDialectAlias("highgo", HsqldbDialect.class); //虚谷数据库 registerDialectAlias("xugu", HsqldbDialect.class); registerDialectAlias("impala", HsqldbDialect.class); registerDialectAlias("firebirdsql", FirebirdDialect.class); //人大金仓数据库 registerDialectAlias("kingbase", PostgreSqlDialect.class); // 人大金仓新版本kingbase8 registerDialectAlias("kingbase8", PostgreSqlDialect.class); //行云数据库 registerDialectAlias("xcloud", CirroDataDialect.class); //openGauss数据库 registerDialectAlias("opengauss", PostgreSqlDialect.class); //注册 AutoDialect //想要实现和以前版本相同的效果时,可以配置 autoDialectClass=old registerAutoDialectAlias("old", DefaultAutoDialect.class); registerAutoDialectAlias("hikari", HikariAutoDialect.class); registerAutoDialectAlias("druid", DruidAutoDialect.class); registerAutoDialectAlias("tomcat-jdbc", TomcatAutoDialect.class); registerAutoDialectAlias("dbcp", DbcpAutoDialect.class); registerAutoDialectAlias("c3p0", C3P0AutoDialect.class); //不配置时,默认使用 DataSourceNegotiationAutoDialect registerAutoDialectAlias("default", DataSourceNegotiationAutoDialect.class); } public static void registerAutoDialectAlias(String alias, Class autoDialectClass) { autoDialectMap.put(alias, autoDialectClass); } /** * 自动获取dialect,如果没有setProperties或setSqlUtilConfig,也可以正常进行 */ private boolean autoDialect = true; /** * 属性配置 */ private Properties properties; /** * 缓存 dialect 实现,key 有两种,分别为 jdbcurl 和 dialectClassName */ private Map urlDialectMap = new ConcurrentHashMap(); private ReentrantLock lock = new ReentrantLock(); private AbstractHelperDialect delegate; private ThreadLocal dialectThreadLocal = new ThreadLocal(); private AutoDialect autoDialectDelegate; public static String fromJdbcUrl(String jdbcUrl) { final String url = jdbcUrl.toLowerCase(); for (String dialect : dialectAliasMap.keySet()) { if (url.contains(":" + dialect.toLowerCase() + ":")) { return dialect; } } return null; } //获取当前的代理对象 public AbstractHelperDialect getDelegate() { if (delegate != null) { return delegate; } return dialectThreadLocal.get(); } //移除代理对象 public void clearDelegate() { dialectThreadLocal.remove(); } public AbstractHelperDialect getDialectThreadLocal() { return dialectThreadLocal.get(); } public void setDialectThreadLocal(AbstractHelperDialect delegate) { this.dialectThreadLocal.set(delegate); } /** * 反射类 * * @param className * @return * @throws Exception */ public static Class resloveDialectClass(String className) throws Exception { if (dialectAliasMap.containsKey(className.toLowerCase())) { return dialectAliasMap.get(className.toLowerCase()); } else { return Class.forName(className); } } /** * 初始化 helper * * @param dialectClass * @param properties */ public static AbstractHelperDialect instanceDialect(String dialectClass, Properties properties) { AbstractHelperDialect dialect; if (StringUtil.isEmpty(dialectClass)) { throw new PageException("When you use the PageHelper pagination plugin, you must set the helper property"); } try { Class sqlDialectClass = resloveDialectClass(dialectClass); if (AbstractHelperDialect.class.isAssignableFrom(sqlDialectClass)) { dialect = (AbstractHelperDialect) sqlDialectClass.newInstance(); } else { throw new PageException("When using PageHelper, the dialect must be an implementation class that implements the " + AbstractHelperDialect.class.getCanonicalName() + " interface!"); } } catch (Exception e) { throw new PageException("error initializing helper dialectclass[" + dialectClass + "]" + e.getMessage(), e); } dialect.setProperties(properties); return dialect; } /** * 多数据动态获取时,每次需要初始化,还可以运行时指定具体的实现 * * @param ms * @param dialectClass 分页实现,必须是 {@link AbstractHelperDialect} 实现类,可以使用当前类中注册的别名,例如 "mysql", "oracle" */ public void initDelegateDialect(MappedStatement ms, String dialectClass) { if (StringUtil.isNotEmpty(dialectClass)) { AbstractHelperDialect dialect = urlDialectMap.get(dialectClass); if (dialect == null) { lock.lock(); try { if ((dialect = urlDialectMap.get(dialectClass)) == null) { dialect = instanceDialect(dialectClass, properties); urlDialectMap.put(dialectClass, dialect); } } finally { lock.unlock(); } } dialectThreadLocal.set(dialect); } else if (delegate == null) { if (autoDialect) { this.delegate = autoGetDialect(ms); } else { dialectThreadLocal.set(autoGetDialect(ms)); } } } /** * 自动获取分页方言实现 * * @param ms * @return */ public AbstractHelperDialect autoGetDialect(MappedStatement ms) { DataSource dataSource = ms.getConfiguration().getEnvironment().getDataSource(); Object dialectKey = autoDialectDelegate.extractDialectKey(ms, dataSource, properties); if (dialectKey == null) { return autoDialectDelegate.extractDialect(dialectKey, ms, dataSource, properties); } else if (!urlDialectMap.containsKey(dialectKey)) { lock.lock(); try { if (!urlDialectMap.containsKey(dialectKey)) { urlDialectMap.put(dialectKey, autoDialectDelegate.extractDialect(dialectKey, ms, dataSource, properties)); } } finally { lock.unlock(); } } return urlDialectMap.get(dialectKey); } /** * 初始化自定义 AutoDialect * * @param properties */ private void initAutoDialectClass(Properties properties) { String autoDialectClassStr = properties.getProperty("autoDialectClass"); if (StringUtil.isNotEmpty(autoDialectClassStr)) { try { Class autoDialectClass; if (autoDialectMap.containsKey(autoDialectClassStr)) { autoDialectClass = autoDialectMap.get(autoDialectClassStr); } else { autoDialectClass = (Class) Class.forName(autoDialectClassStr); } this.autoDialectDelegate = ClassUtil.newInstance(autoDialectClass, properties); } catch (ClassNotFoundException e) { throw new IllegalArgumentException("Make sure that the AutoDialect implementation class (" + autoDialectClassStr + ") for the autoDialectClass configuration exists!", e); } catch (Exception e) { throw new RuntimeException(autoDialectClassStr + "Class must provide a constructor without parameters", e); } } else { this.autoDialectDelegate = new DataSourceNegotiationAutoDialect(); } } /** * 初始化方言别名 * * @param properties */ private void initDialectAlias(Properties properties) { String dialectAlias = properties.getProperty("dialectAlias"); if (StringUtil.isNotEmpty(dialectAlias)) { String[] alias = dialectAlias.split(";"); for (int i = 0; i < alias.length; i++) { String[] kv = alias[i].split("="); if (kv.length != 2) { throw new IllegalArgumentException("dialectAlias parameter misconfigured," + "Please follow alias1=xx.dialectClass; alias2=dialectClass2!"); } for (int j = 0; j < kv.length; j++) { try { //允许配置如 dm=oracle, 直接引用oracle实现 if (dialectAliasMap.containsKey(kv[1])) { registerDialectAlias(kv[0], dialectAliasMap.get(kv[1])); } else { Class diallectClass = (Class) Class.forName(kv[1]); //允许覆盖已有的实现 registerDialectAlias(kv[0], diallectClass); } } catch (ClassNotFoundException e) { throw new IllegalArgumentException("Make sure the Dialect implementation class configured by dialectAlias exists!", e); } } } } } public void setProperties(Properties properties) { this.properties = properties; //初始化自定义AutoDialect initAutoDialectClass(properties); //使用 sqlserver2012 作为默认分页方式,这种情况在动态数据源时方便使用 String useSqlserver2012 = properties.getProperty("useSqlserver2012"); if (StringUtil.isNotEmpty(useSqlserver2012) && Boolean.parseBoolean(useSqlserver2012)) { registerDialectAlias("sqlserver", SqlServer2012Dialect.class); registerDialectAlias("sqlserver2008", SqlServerDialect.class); } initDialectAlias(properties); //指定的 Helper 数据库方言,和 不同 String dialect = properties.getProperty("helperDialect"); //运行时获取数据源 String runtimeDialect = properties.getProperty("autoRuntimeDialect"); //1.动态多数据源 if (StringUtil.isNotEmpty(runtimeDialect) && "TRUE".equalsIgnoreCase(runtimeDialect)) { this.autoDialect = false; } //2.动态获取方言 else if (StringUtil.isEmpty(dialect)) { autoDialect = true; } //3.指定方言 else { autoDialect = false; this.delegate = instanceDialect(dialect, properties); } } }