diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 00000000..9c6fb0d2
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,2 @@
+patreon: medcl
+custom: ["https://www.buymeacoffee.com/medcl"]
diff --git a/README.md b/README.md
index 617e0804..f1406443 100644
--- a/README.md
+++ b/README.md
@@ -10,16 +10,9 @@ Versions
IK version | ES version
-----------|-----------
-master | 6.x -> master
-6.3.0| 6.3.0
-6.2.4| 6.2.4
-6.1.3| 6.1.3
-5.6.8| 5.6.8
-5.5.3| 5.5.3
-5.4.3| 5.4.3
-5.3.3| 5.3.3
-5.2.2| 5.2.2
-5.1.2| 5.1.2
+master | 7.x -> master
+6.x| 6.x
+5.x| 5.x
1.10.6 | 2.4.6
1.9.5 | 2.3.5
1.8.1 | 2.2.1
@@ -64,13 +57,13 @@ curl -XPUT http://localhost:9200/index
2.create a mapping
```bash
-curl -XPOST http://localhost:9200/index/fulltext/_mapping -H 'Content-Type:application/json' -d'
+curl -XPOST http://localhost:9200/index/_mapping -H 'Content-Type:application/json' -d'
{
"properties": {
"content": {
"type": "text",
"analyzer": "ik_max_word",
- "search_analyzer": "ik_max_word"
+ "search_analyzer": "ik_smart"
}
}
@@ -80,25 +73,25 @@ curl -XPOST http://localhost:9200/index/fulltext/_mapping -H 'Content-Type:appli
3.index some docs
```bash
-curl -XPOST http://localhost:9200/index/fulltext/1 -H 'Content-Type:application/json' -d'
+curl -XPOST http://localhost:9200/index/_create/1 -H 'Content-Type:application/json' -d'
{"content":"美国留给伊拉克的是个烂摊子吗"}
'
```
```bash
-curl -XPOST http://localhost:9200/index/fulltext/2 -H 'Content-Type:application/json' -d'
+curl -XPOST http://localhost:9200/index/_create/2 -H 'Content-Type:application/json' -d'
{"content":"公安部:各地校车将享最高路权"}
'
```
```bash
-curl -XPOST http://localhost:9200/index/fulltext/3 -H 'Content-Type:application/json' -d'
+curl -XPOST http://localhost:9200/index/_create/3 -H 'Content-Type:application/json' -d'
{"content":"中韩渔警冲突调查:韩警平均每天扣1艘中国渔船"}
'
```
```bash
-curl -XPOST http://localhost:9200/index/fulltext/4 -H 'Content-Type:application/json' -d'
+curl -XPOST http://localhost:9200/index/_create/4 -H 'Content-Type:application/json' -d'
{"content":"中国驻洛杉矶领事馆遭亚裔男子枪击 嫌犯已自首"}
'
```
@@ -106,7 +99,7 @@ curl -XPOST http://localhost:9200/index/fulltext/4 -H 'Content-Type:application/
4.query with highlighting
```bash
-curl -XPOST http://localhost:9200/index/fulltext/_search -H 'Content-Type:application/json' -d'
+curl -XPOST http://localhost:9200/index/_search -H 'Content-Type:application/json' -d'
{
"query" : { "match" : { "content" : "中国" }},
"highlight" : {
@@ -248,13 +241,13 @@ curl -XGET "http://localhost:9200/your_index/_analyze" -H 'Content-Type: applica
4. ik_max_word 和 ik_smart 什么区别?
-ik_max_word: 会将文本做最细粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,中华人民,中华,华人,人民共和国,人民,人,民,共和国,共和,和,国国,国歌”,会穷尽各种可能的组合;
+ik_max_word: 会将文本做最细粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,中华人民,中华,华人,人民共和国,人民,人,民,共和国,共和,和,国国,国歌”,会穷尽各种可能的组合,适合 Term Query;
-ik_smart: 会做最粗粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,国歌”。
+ik_smart: 会做最粗粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,国歌”,适合 Phrase 查询。
Changes
------
-*5.0.0*
+*自 v5.0.0 起*
- 移除名为 `ik` 的analyzer和tokenizer,请分别使用 `ik_smart` 和 `ik_max_word`
diff --git a/config/IKAnalyzer.cfg.xml b/config/IKAnalyzer.cfg.xml
index fe69bb20..0c39dac6 100644
--- a/config/IKAnalyzer.cfg.xml
+++ b/config/IKAnalyzer.cfg.xml
@@ -10,4 +10,12 @@
+
+
+
+ root
+
+ root
+
+ 10
diff --git a/mysql.extend.md b/mysql.extend.md
new file mode 100644
index 00000000..2d6b3688
--- /dev/null
+++ b/mysql.extend.md
@@ -0,0 +1,75 @@
+IK Analysis 扩展词新增mysql同步来源
+=============================
+
+- 支持启动全量加载扩展词
+- 支持热更新扩展词
+
+> mysql 扩展词表结构
+
+
+```mysql
+
+CREATE TABLE `es_lexicon` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '词库id',
+ `create_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `modify_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
+ `lexicon_text` varchar(40) NOT NULL COMMENT '词条关键词',
+ `lexicon_type` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0扩展词库 1停用词库',
+ `lexicon_status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '词条状态 0正常 1暂停使用',
+ `del_flag` tinyint(1) NOT NULL DEFAULT '0' COMMENT '作废标志 0正常 1作废',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='ES远程扩展词库表'
+```
+
+
+
+```IKAnalyzer.cfg.xml```
+
+
+```xml
+
+
+
+ IK Analyzer 扩展配置
+
+
+
+
+
+
+
+
+
+
+
+ root
+
+ 123456
+
+ 10
+
+
+```
+
+> 自行打包 放入elasticsearch plugins 目录即可
+
+启动日志如下:
+
+```
+[2021-06-02T15:16:07,593][INFO ][o.w.a.d.Dictionary ] ======start mysql to reload ik dict.======
+[2021-06-02T15:16:07,828][INFO ][o.w.a.d.Dictionary ] last update mysql ext dic time :2021-05-27T14:36:05.000+0800,fill count:4843 ,disable count:0
+[2021-06-02T15:16:07,837][INFO ][o.w.a.d.Dictionary ] the last reload stop word not found, the last update time :null
+[2021-06-02T15:16:07,838][INFO ][o.w.a.d.Dictionary ] last update mysql stop word time :null,fill count:0 ,disable count:0
+[2021-06-02T15:16:07,838][INFO ][o.w.a.d.Dictionary ] ======reload mysql ik dict finished.======
+[2021-06-02T15:16:17,587][INFO ][o.w.a.d.Dictionary ] ======start mysql to reload ik dict.======
+[2021-06-02T15:16:17,615][INFO ][o.w.a.d.Dictionary ] last update mysql ext dic time :2021-06-01T09:44:50.000+0800,fill count:4842 ,disable count:0
+[2021-06-02T15:16:17,623][INFO ][o.w.a.d.Dictionary ] the last reload stop word not found, the last update time :null
+[2021-06-02T15:16:17,624][INFO ][o.w.a.d.Dictionary ] last update mysql stop word time :null,fill count:0 ,disable count:0
+[2021-06-02T15:16:17,624][INFO ][o.w.a.d.Dictionary ] ======reload mysql ik dict finished.======
+[2021-06-02T15:16:27,596][INFO ][o.w.a.d.Dictionary ] ======start mysql to reload ik dict.======
+[2021-06-02T15:16:27,602][INFO ][o.w.a.d.Dictionary ] the latest update record was not found, the last update time :2021-06-01T09:44:50.000+0800
+[2021-06-02T15:16:27,602][INFO ][o.w.a.d.Dictionary ] last update mysql ext dic time :2021-06-01T09:44:50.000+0800,fill count:0 ,disable count:0
+[2021-06-02T15:16:27,608][INFO ][o.w.a.d.Dictionary ] the last reload stop word not found, the last update time :null
+[2021-06-02T15:16:27,608][INFO ][o.w.a.d.Dictionary ] last update mysql stop word time :null,fill count:0 ,disable count:0
+[2021-06-02T15:16:27,608][INFO ][o.w.a.d.Dictionary ] ======reload mysql ik dict finished.======
+```
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 32fb4e14..7ab39feb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,7 +12,7 @@
2011
- 6.5.0
+ 6.2.3
1.8
${project.basedir}/src/main/assemblies/plugin.xml
analysis-ik
@@ -95,6 +95,11 @@
log4j-api
2.3
+
+ mysql
+ mysql-connector-java
+ 5.1.49
+
org.hamcrest
diff --git a/src/main/assemblies/plugin.xml b/src/main/assemblies/plugin.xml
index 8b6fa594..9bc850c8 100644
--- a/src/main/assemblies/plugin.xml
+++ b/src/main/assemblies/plugin.xml
@@ -39,6 +39,7 @@
true
org.apache.httpcomponents:httpclient
+ mysql:mysql-connector-java
diff --git a/src/main/java/org/elasticsearch/index/analysis/IkTokenizerFactory.java b/src/main/java/org/elasticsearch/index/analysis/IkTokenizerFactory.java
index 17d09806..51773ddd 100644
--- a/src/main/java/org/elasticsearch/index/analysis/IkTokenizerFactory.java
+++ b/src/main/java/org/elasticsearch/index/analysis/IkTokenizerFactory.java
@@ -11,7 +11,7 @@ public class IkTokenizerFactory extends AbstractTokenizerFactory {
private Configuration configuration;
public IkTokenizerFactory(IndexSettings indexSettings, Environment env, String name, Settings settings) {
- super(indexSettings, name, settings);
+ super(indexSettings, name,settings);
configuration=new Configuration(env,settings);
}
diff --git a/src/main/java/org/wltea/analyzer/cfg/Configuration.java b/src/main/java/org/wltea/analyzer/cfg/Configuration.java
index dadd0f20..78e9f5d4 100644
--- a/src/main/java/org/wltea/analyzer/cfg/Configuration.java
+++ b/src/main/java/org/wltea/analyzer/cfg/Configuration.java
@@ -24,6 +24,9 @@ public class Configuration {
//是否启用远程词典加载
private boolean enableRemoteDict=false;
+ //是否启用远程词典加载
+ private boolean enableMysqlDict=false;
+
//是否启用小写处理
private boolean enableLowercase=true;
@@ -36,6 +39,7 @@ public Configuration(Environment env,Settings settings) {
this.useSmart = settings.get("use_smart", "false").equals("true");
this.enableLowercase = settings.get("enable_lowercase", "true").equals("true");
this.enableRemoteDict = settings.get("enable_remote_dict", "true").equals("true");
+ this.enableMysqlDict = settings.get("enable_mysql_dict", "true").equals("true");
Dictionary.initial(this);
@@ -69,6 +73,10 @@ public boolean isEnableRemoteDict() {
return enableRemoteDict;
}
+ public boolean isEnableMysqlDict() {
+ return enableMysqlDict;
+ }
+
public boolean isEnableLowercase() {
return enableLowercase;
}
diff --git a/src/main/java/org/wltea/analyzer/core/AnalyzeContext.java b/src/main/java/org/wltea/analyzer/core/AnalyzeContext.java
index 5bf1ac90..890d9080 100644
--- a/src/main/java/org/wltea/analyzer/core/AnalyzeContext.java
+++ b/src/main/java/org/wltea/analyzer/core/AnalyzeContext.java
@@ -268,13 +268,13 @@ void outputToResult(){
while(l != null){
this.results.add(l);
//字典中无单字,但是词元冲突了,切分出相交词元的前一个词元中的单字
- int innerIndex = index + 1;
+ /*int innerIndex = index + 1;
for (; innerIndex < index + l.getLength(); innerIndex++) {
Lexeme innerL = path.peekFirst();
if (innerL != null && innerIndex == innerL.getBegin()) {
this.outputSingleCJK(innerIndex - 1);
}
- }
+ }*/
//将index移至lexeme后
index = l.getBegin() + l.getLength();
diff --git a/src/main/java/org/wltea/analyzer/db/DBConfigProperties.java b/src/main/java/org/wltea/analyzer/db/DBConfigProperties.java
new file mode 100644
index 00000000..eabb6296
--- /dev/null
+++ b/src/main/java/org/wltea/analyzer/db/DBConfigProperties.java
@@ -0,0 +1,58 @@
+package org.wltea.analyzer.db;
+
+import java.io.Serializable;
+
+/**
+ * @author fsren
+ * @date 2021-05-25
+ */
+public class DBConfigProperties implements Serializable {
+
+ private static final long serialVersionUID = 688310733642302993L;
+ private String dbUrl;
+ private String user;
+ private String password;
+ private Integer reloadInterval;
+
+ public String getDbUrl() {
+ return dbUrl;
+ }
+
+ public void setDbUrl(String dbUrl) {
+ this.dbUrl = dbUrl;
+ }
+
+ public String getUser() {
+ return user;
+ }
+
+ public void setUser(String user) {
+ this.user = user;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public Integer getReloadInterval() {
+ return reloadInterval;
+ }
+
+ public void setReloadInterval(Integer reloadInterval) {
+ this.reloadInterval = reloadInterval;
+ }
+
+ @Override
+ public String toString() {
+ return "DBConfigProperties{" +
+ "dbUrl='" + dbUrl + '\'' +
+ ", user='" + user + '\'' +
+ ", password='" + password + '\'' +
+ ", reloadInterval=" + reloadInterval +
+ '}';
+ }
+}
diff --git a/src/main/java/org/wltea/analyzer/db/DataSourceFactory.java b/src/main/java/org/wltea/analyzer/db/DataSourceFactory.java
new file mode 100644
index 00000000..65f9d032
--- /dev/null
+++ b/src/main/java/org/wltea/analyzer/db/DataSourceFactory.java
@@ -0,0 +1,43 @@
+package org.wltea.analyzer.db;
+
+import com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource;
+import org.apache.logging.log4j.Logger;
+import org.elasticsearch.SpecialPermission;
+import org.wltea.analyzer.help.ESPluginLoggerFactory;
+
+import javax.sql.DataSource;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.sql.SQLException;
+
+/**
+ * @author fsren
+ * @date 2021-05-25
+ */
+public class DataSourceFactory {
+
+
+ private static final Logger logger = ESPluginLoggerFactory.getLogger(DataSourceFactory.class.getName());
+
+
+ public static DataSource getDataSource(DBConfigProperties configProperties) {
+
+ SpecialPermission.check();
+ return AccessController.doPrivileged((PrivilegedAction) () -> {
+ logger.info("load datasource start");
+ MysqlConnectionPoolDataSource dataSource = new MysqlConnectionPoolDataSource();
+ dataSource.setURL(configProperties.getDbUrl());
+ dataSource.setUser(configProperties.getUser());
+ dataSource.setPassword(configProperties.getPassword());
+ dataSource.setAllowMultiQueries(true);
+ try {
+ dataSource.setSocketTimeout(1000);
+ } catch (SQLException ignore) {
+ }
+ logger.info("load datasource end");
+ return dataSource;
+ });
+ }
+
+
+}
diff --git a/src/main/java/org/wltea/analyzer/db/JdbcUtil.java b/src/main/java/org/wltea/analyzer/db/JdbcUtil.java
new file mode 100644
index 00000000..7cea9676
--- /dev/null
+++ b/src/main/java/org/wltea/analyzer/db/JdbcUtil.java
@@ -0,0 +1,92 @@
+package org.wltea.analyzer.db;
+
+import org.wltea.analyzer.db.core.*;
+
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+
+/**
+ * @author fsren
+ * @date 2021-05-26
+ */
+public class JdbcUtil {
+
+ private final ConnectionManager connManager;
+
+
+ /**
+ * 创建JdbcUtils
+ *
+ * @param dataSource 数据源
+ */
+ public JdbcUtil(DataSource dataSource) {
+ connManager = new ConnectionManager(dataSource);
+ }
+
+ /**
+ * 创建语句
+ *
+ * @param conn 连接
+ * @param sql sql语句
+ * @param params sql参数
+ * @return 创建的PreparedStatement对象
+ * @throws SQLException 来自JDBC的异常
+ */
+ private PreparedStatement createPreparedStatement(Connection conn, String sql, Object... params) throws SQLException {
+ PreparedStatement stmt = conn.prepareStatement(sql);
+ for (int i = 0; i < params.length; ++i) {
+ stmt.setObject(i + 1, params[i]);
+ }
+ return stmt;
+ }
+
+ /**
+ * 查询数据库并转换结果集。
+ * 用户可自定义结果集转换器。
+ * 用户也可使用预定义的结果集转换器。
+ *
+ * @param sql sql语句
+ * @param recordMapper 结果集转换器
+ * @param params sql参数
+ * @param resultSetMapper返回的结果类型
+ * @return 成功则返回转换结果,失败则抛出DbException,结果为空则返回空列表
+ * @see RecordMapper
+ * @see ListRecordMapper
+ */
+ public T query(String sql, RecordMapper recordMapper, Object... params) {
+ ResultSet rs = null;
+ PreparedStatement stmt = null;
+ Connection conn = null;
+ try {
+ conn = connManager.getConnection();
+ stmt = createPreparedStatement(conn, sql, params);
+ rs = stmt.executeQuery();
+ return recordMapper.map(new RecordAdapterForResultSet(rs));
+ } catch (Exception e) {
+ throw new RuntimeException(e.getMessage(), e);
+ } finally {
+ connManager.close(conn, stmt, rs);
+ }
+ }
+
+ /**
+ * 查询数据库,对结果集的每一行进行转换,然后将所有行封装成列表。
+ * 用户可自定义行转换器。
+ * 用户也可使用预定义的行转换器。
+ *
+ * @param sql sql语句
+ * @param rowMapper 行转换器
+ * @param params sql参数
+ * @param rowMapper返回的结果类型
+ * @return 成功则返回结果列表,失败则抛出DbException,结果为空则返回空列表
+ * @see RowMapper
+ * @see MapRowMapper
+ */
+ public List queryList(String sql, RowMapper rowMapper, Object... params) {
+ return query(sql, new ListRecordMapper<>(rowMapper), params);
+ }
+}
diff --git a/src/main/java/org/wltea/analyzer/db/Lexicon.java b/src/main/java/org/wltea/analyzer/db/Lexicon.java
new file mode 100644
index 00000000..6b0233a6
--- /dev/null
+++ b/src/main/java/org/wltea/analyzer/db/Lexicon.java
@@ -0,0 +1,51 @@
+package org.wltea.analyzer.db;
+
+import java.io.Serializable;
+import java.sql.Timestamp;
+
+/**
+ * @author fsren
+ * @date 2021-05-26
+ */
+public class Lexicon implements Serializable {
+
+ private static final long serialVersionUID = 7628519160135272308L;
+ /**
+ * 扩展词
+ */
+ private String lexiconText;
+
+ /**
+ * 加载状态 true 代表加载, false 代表屏蔽
+ */
+ private Boolean isFill;
+
+ /**
+ * 最后更新时间
+ */
+ private Timestamp modifyDate;
+
+ public String getLexiconText() {
+ return lexiconText;
+ }
+
+ public void setLexiconText(String lexiconText) {
+ this.lexiconText = lexiconText;
+ }
+
+ public Boolean getFill() {
+ return isFill;
+ }
+
+ public void setFill(Boolean fill) {
+ isFill = fill;
+ }
+
+ public Timestamp getModifyDate() {
+ return modifyDate;
+ }
+
+ public void setModifyDate(Timestamp modifyDate) {
+ this.modifyDate = modifyDate;
+ }
+}
diff --git a/src/main/java/org/wltea/analyzer/db/core/ConnectionManager.java b/src/main/java/org/wltea/analyzer/db/core/ConnectionManager.java
new file mode 100644
index 00000000..0071b04b
--- /dev/null
+++ b/src/main/java/org/wltea/analyzer/db/core/ConnectionManager.java
@@ -0,0 +1,151 @@
+package org.wltea.analyzer.db.core;
+
+import org.elasticsearch.SpecialPermission;
+
+import javax.sql.DataSource;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+/**
+ * 连接管理器
+ * 封装了线程安全的数据库连接
+ *
+ * @author fsren
+ * @date 2021-05-26
+ */
+public class ConnectionManager {
+
+
+ private final DataSource dataSource;
+ private final ThreadLocal connHolder = new ThreadLocal<>();
+
+ public ConnectionManager(DataSource dataSource) {
+ this.dataSource = dataSource;
+ }
+
+ public Connection getConnection() {
+ SpecialPermission.check();
+ return AccessController.doPrivileged((PrivilegedAction) () -> {
+ try {
+ Connection conn = connHolder.get();
+ if (conn == null) {
+ conn = dataSource.getConnection();
+ connHolder.set(conn);
+ }
+ return conn;
+ } catch (SQLException e) {
+ throw new RuntimeException("An error occurred while creating a database connection.", e);
+ }
+ });
+ }
+
+ public void close(Connection conn, Statement stmt) {
+ SpecialPermission.check();
+ AccessController.doPrivileged((PrivilegedAction) () -> {
+ if (stmt != null) {
+ try {
+ stmt.close();
+ } catch (SQLException ignored) {
+ }
+ }
+ if (conn != null) {
+ try {
+ if (conn.getAutoCommit()) {
+ conn.close();
+ connHolder.remove();
+ }
+ } catch (SQLException ignored) {
+ }
+ }
+ return null;
+ });
+
+ }
+
+ public void close(Connection conn, Statement stmt, ResultSet rs) {
+ SpecialPermission.check();
+ AccessController.doPrivileged((PrivilegedAction) () -> {
+ if (rs != null) {
+ try {
+ rs.close();
+ } catch (SQLException ignored) {
+ }
+ }
+ return null;
+ });
+ close(conn, stmt);
+ }
+
+ public void startTransaction() {
+ SpecialPermission.check();
+ AccessController.doPrivileged((PrivilegedAction) () -> {
+ try {
+ Connection conn = connHolder.get();
+ if (conn != null) {
+ conn.close();
+ connHolder.remove();
+ }
+ conn = dataSource.getConnection();
+ conn.setAutoCommit(false);
+ connHolder.set(conn);
+ } catch (SQLException e) {
+ throw new RuntimeException("An error occurred while starting transaction.", e);
+ }
+ return null;
+ });
+
+ }
+
+ public void commit() {
+ SpecialPermission.check();
+ AccessController.doPrivileged((PrivilegedAction) () -> {
+ Connection conn = connHolder.get();
+ if (conn != null) {
+ try {
+ conn.commit();
+ conn.close();
+ connHolder.remove();
+ } catch (SQLException e) {
+ throw new RuntimeException("An error occurred while committing transaction.", e);
+ }
+ }
+ return null;
+ });
+
+ }
+
+ public void rollback() {
+ SpecialPermission.check();
+ AccessController.doPrivileged((PrivilegedAction) () -> {
+ Connection conn = connHolder.get();
+ if (conn != null) {
+ try {
+ conn.rollback();
+ conn.close();
+ connHolder.remove();
+ } catch (SQLException e) {
+ throw new RuntimeException("An error occurred while committing transaction.", e);
+ }
+ }
+ return null;
+ });
+
+ }
+
+ public boolean inTransaction() {
+ SpecialPermission.check();
+ return AccessController.doPrivileged((PrivilegedAction) () -> {
+ Connection conn = connHolder.get();
+ try {
+ return conn != null && !conn.getAutoCommit();
+ } catch (SQLException e) {
+ throw new RuntimeException("An error occurred while getting auto commit.", e);
+ }
+ });
+
+ }
+}
diff --git a/src/main/java/org/wltea/analyzer/db/core/ListRecordMapper.java b/src/main/java/org/wltea/analyzer/db/core/ListRecordMapper.java
new file mode 100644
index 00000000..e10103d0
--- /dev/null
+++ b/src/main/java/org/wltea/analyzer/db/core/ListRecordMapper.java
@@ -0,0 +1,25 @@
+package org.wltea.analyzer.db.core;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author fsren
+ * @date 2021-05-26
+ */
+public class ListRecordMapper implements RecordMapper> {
+ private final RowMapper rowMapper;
+
+ public ListRecordMapper(RowMapper rowMapper) {
+ this.rowMapper = rowMapper;
+ }
+
+ @Override
+ public List map(Record record) {
+ List result = new ArrayList<>();
+ while (record.next()) {
+ result.add(rowMapper.map(record.getCurrentRow()));
+ }
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/wltea/analyzer/db/core/MapRowMapper.java b/src/main/java/org/wltea/analyzer/db/core/MapRowMapper.java
new file mode 100644
index 00000000..b58b02ec
--- /dev/null
+++ b/src/main/java/org/wltea/analyzer/db/core/MapRowMapper.java
@@ -0,0 +1,22 @@
+package org.wltea.analyzer.db.core;
+
+import java.util.Hashtable;
+import java.util.Map;
+
+/**
+ * @author fsren
+ * @date 2021-05-26
+ */
+public class MapRowMapper implements RowMapper