mirror of
https://gitee.com/mybatis-flex/mybatis-flex.git
synced 2025-12-08 01:28:24 +08:00
Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
d2f22dae60
@ -47,6 +47,10 @@ select * from tb_account
|
||||
where id >= 100
|
||||
```
|
||||
|
||||
::: tip 问题:以上示例中,`ACCOUNT.ID.ge(100)` 中的 `ACCOUNT` 是怎么来的?
|
||||
MyBatis-Flex 使用了 APT 技术,这个 `ACCOUNT` 是自动生成的。
|
||||
参考:《[MyBatis-Flex APT 配置](../others/apt.md)》章节。
|
||||
:::
|
||||
|
||||
## select *
|
||||
|
||||
|
||||
@ -24,6 +24,7 @@ MyBatis-Flex 使用了 APT(Annotation Processing Tool)技术,在项目编
|
||||
| processor.baseMapperClass | 自定义 Mapper 的父类 | 全路径类名 | com.mybatisflex.core.BaseMapper |
|
||||
| processor.mappersPackage | 自定义 Mapper 生成的包名 | 合法的包名 | ${entityPackage}.mapper |
|
||||
| processor.tablesNameStyle | 生成辅助类的字段风格 | upperCase, lowerCase<br />upperCamelCase, lowerCamelCase | upperCase |
|
||||
| processor.tablesDefSuffix | 生成的表对应的变量后缀 | string | 空字符串 |
|
||||
| processor.entity.ignoreSuffixes | 过滤 Entity 后缀 | string | - |
|
||||
|
||||
|
||||
|
||||
@ -73,10 +73,12 @@ public class ControllerGenerator implements IGenerator {
|
||||
globalConfig.getTemplateConfig().getTemplate().generate(params, templatePath, controllerJavaFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTemplatePath() {
|
||||
return templatePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTemplatePath(String templatePath) {
|
||||
this.templatePath = templatePath;
|
||||
}
|
||||
|
||||
@ -73,10 +73,12 @@ public class EntityGenerator implements IGenerator {
|
||||
globalConfig.getTemplateConfig().getTemplate().generate(params, templatePath, entityJavaFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTemplatePath() {
|
||||
return templatePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTemplatePath(String templatePath) {
|
||||
this.templatePath = templatePath;
|
||||
}
|
||||
|
||||
@ -73,10 +73,12 @@ public class MapperGenerator implements IGenerator {
|
||||
globalConfig.getTemplateConfig().getTemplate().generate(params, templatePath, mapperJavaFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTemplatePath() {
|
||||
return templatePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTemplatePath(String templatePath) {
|
||||
this.templatePath = templatePath;
|
||||
}
|
||||
|
||||
@ -70,10 +70,12 @@ public class MapperXmlGenerator implements IGenerator {
|
||||
globalConfig.getTemplateConfig().getTemplate().generate(params, templatePath, mapperXmlFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTemplatePath() {
|
||||
return templatePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTemplatePath(String templatePath) {
|
||||
this.templatePath = templatePath;
|
||||
}
|
||||
|
||||
@ -88,10 +88,12 @@ public class PackageInfoGenerator implements IGenerator {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTemplatePath() {
|
||||
return templatePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTemplatePath(String templatePath) {
|
||||
this.templatePath = templatePath;
|
||||
}
|
||||
|
||||
@ -73,10 +73,12 @@ public class ServiceGenerator implements IGenerator {
|
||||
globalConfig.getTemplateConfig().getTemplate().generate(params, templatePath, serviceJavaFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTemplatePath() {
|
||||
return templatePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTemplatePath(String templatePath) {
|
||||
this.templatePath = templatePath;
|
||||
}
|
||||
|
||||
@ -73,10 +73,12 @@ public class ServiceImplGenerator implements IGenerator {
|
||||
globalConfig.getTemplateConfig().getTemplate().generate(params, templatePath, serviceImplJavaFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTemplatePath() {
|
||||
return templatePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTemplatePath(String templatePath) {
|
||||
this.templatePath = templatePath;
|
||||
}
|
||||
|
||||
@ -73,10 +73,12 @@ public class TableDefGenerator implements IGenerator {
|
||||
globalConfig.getTemplateConfig().getTemplate().generate(params, templatePath, tableDefJavaFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTemplatePath() {
|
||||
return templatePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTemplatePath(String templatePath) {
|
||||
this.templatePath = templatePath;
|
||||
}
|
||||
|
||||
@ -34,7 +34,7 @@ public class EnjoyTemplate implements ITemplate {
|
||||
|
||||
public EnjoyTemplate() {
|
||||
engine = Engine.create("mybatis-flex", engine -> {
|
||||
engine.addSharedMethod(StringUtil.class);
|
||||
engine.addSharedStaticMethod(StringUtil.class);
|
||||
engine.setSourceFactory(new FileAndClassPathSourceFactory());
|
||||
});
|
||||
// 以下配置将支持 user.girl 表达式去调用 user 对象的 boolean isGirl() 方法
|
||||
|
||||
@ -41,7 +41,7 @@
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>2.0.31</version>
|
||||
<version>2.0.32</version>
|
||||
<optional>true</optional>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
@ -49,7 +49,7 @@
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2</artifactId>
|
||||
<version>2.0.31</version>
|
||||
<version>2.0.32</version>
|
||||
<optional>true</optional>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
@ -16,7 +16,6 @@
|
||||
package com.mybatisflex.core;
|
||||
|
||||
import com.mybatisflex.core.exception.FlexExceptions;
|
||||
import com.mybatisflex.core.field.FieldQuery;
|
||||
import com.mybatisflex.core.field.FieldQueryBuilder;
|
||||
import com.mybatisflex.core.mybatis.MappedStatementTypes;
|
||||
import com.mybatisflex.core.paginate.Page;
|
||||
@ -27,6 +26,7 @@ import com.mybatisflex.core.table.TableInfoFactory;
|
||||
import com.mybatisflex.core.util.*;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
import org.apache.ibatis.builder.annotation.ProviderContext;
|
||||
import org.apache.ibatis.cursor.Cursor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
@ -345,7 +345,7 @@ public interface BaseMapper<T> {
|
||||
* @return entity 数据
|
||||
*/
|
||||
default T selectOneByQuery(QueryWrapper queryWrapper) {
|
||||
return SqlUtil.getSelectOneResult(selectListByQuery(queryWrapper));
|
||||
return MapperUtil.getSelectOneResult(selectListByQuery(queryWrapper));
|
||||
}
|
||||
|
||||
|
||||
@ -357,7 +357,7 @@ public interface BaseMapper<T> {
|
||||
* @return 数据内容
|
||||
*/
|
||||
default <R> R selectOneByQueryAs(QueryWrapper queryWrapper, Class<R> asType) {
|
||||
return SqlUtil.getSelectOneResult(selectListByQueryAs(queryWrapper, asType));
|
||||
return MapperUtil.getSelectOneResult(selectListByQueryAs(queryWrapper, asType));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -435,11 +435,21 @@ public interface BaseMapper<T> {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
__queryFields(list, consumers);
|
||||
MapperUtil.queryFields(this, list, consumers);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 query 来构建条件查询游标数据 Cursor
|
||||
* 该方法必须在事务中才能正常使用,非事务下无法获取数据
|
||||
*
|
||||
* @param queryWrapper 查询条件
|
||||
* @return 游标数据 Cursor
|
||||
*/
|
||||
@SelectProvider(type = EntitySqlProvider.class, method = "selectListByQuery")
|
||||
Cursor<T> selectCursorByQuery(@Param(FlexConsts.QUERY) QueryWrapper queryWrapper);
|
||||
|
||||
|
||||
/**
|
||||
* 根据 query 来构建条件查询数据列表,要求返回的数据为 asType
|
||||
@ -469,7 +479,7 @@ public interface BaseMapper<T> {
|
||||
if (list == null || list.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
} else {
|
||||
__queryFields(list, consumers);
|
||||
MapperUtil.queryFields(this, list, consumers);
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@ -493,7 +503,7 @@ public interface BaseMapper<T> {
|
||||
* @return 数据量
|
||||
*/
|
||||
default Object selectObjectByQuery(QueryWrapper queryWrapper) {
|
||||
return SqlUtil.getSelectOneResult(selectObjectListByQuery(queryWrapper));
|
||||
return MapperUtil.getSelectOneResult(selectObjectListByQuery(queryWrapper));
|
||||
}
|
||||
|
||||
|
||||
@ -539,17 +549,22 @@ public interface BaseMapper<T> {
|
||||
*/
|
||||
default long selectCountByQuery(QueryWrapper queryWrapper) {
|
||||
List<QueryColumn> selectColumns = CPI.getSelectColumns(queryWrapper);
|
||||
if (CollectionUtil.isEmpty(selectColumns)) {
|
||||
queryWrapper.select(count());
|
||||
}
|
||||
List<Object> objects = selectObjectListByQuery(queryWrapper);
|
||||
Object object = objects == null || objects.isEmpty() ? null : objects.get(0);
|
||||
if (object == null) {
|
||||
return 0;
|
||||
} else if (object instanceof Number) {
|
||||
return ((Number) object).longValue();
|
||||
} else {
|
||||
throw FlexExceptions.wrap("selectCountByQuery error, Can not get number value for queryWrapper: %s", queryWrapper);
|
||||
try {
|
||||
if (CollectionUtil.isEmpty(selectColumns)) {
|
||||
queryWrapper.select(count());
|
||||
}
|
||||
List<Object> objects = selectObjectListByQuery(queryWrapper);
|
||||
Object object = objects == null || objects.isEmpty() ? null : objects.get(0);
|
||||
if (object == null) {
|
||||
return 0;
|
||||
} else if (object instanceof Number) {
|
||||
return ((Number) object).longValue();
|
||||
} else {
|
||||
throw FlexExceptions.wrap("selectCountByQuery error, Can not get number value for queryWrapper: %s", queryWrapper);
|
||||
}
|
||||
} finally {
|
||||
//fixed https://github.com/mybatis-flex/mybatis-flex/issues/49
|
||||
CPI.setSelectColumns(queryWrapper, selectColumns);
|
||||
}
|
||||
}
|
||||
|
||||
@ -712,47 +727,15 @@ public interface BaseMapper<T> {
|
||||
|
||||
if (asType != null) {
|
||||
List<R> records = selectListByQueryAs(queryWrapper, asType);
|
||||
__queryFields(records, consumers);
|
||||
MapperUtil.queryFields(this, records, consumers);
|
||||
page.setRecords(records);
|
||||
} else {
|
||||
List<R> records = (List<R>) selectListByQuery(queryWrapper);
|
||||
__queryFields(records, consumers);
|
||||
MapperUtil.queryFields(this, records, consumers);
|
||||
page.setRecords(records);
|
||||
}
|
||||
return page;
|
||||
}
|
||||
|
||||
|
||||
default <R> void __queryFields(List<R> list, Consumer<FieldQueryBuilder<R>>[] consumers) {
|
||||
if (CollectionUtil.isEmpty(list) || ArrayUtil.isEmpty(consumers) || consumers[0] == null) {
|
||||
return;
|
||||
}
|
||||
list.forEach(entity -> {
|
||||
for (Consumer<FieldQueryBuilder<R>> consumer : consumers) {
|
||||
FieldQueryBuilder<R> fieldQueryBuilder = new FieldQueryBuilder<>(entity);
|
||||
consumer.accept(fieldQueryBuilder);
|
||||
FieldQuery fieldQuery = fieldQueryBuilder.build();
|
||||
QueryWrapper childQuery = fieldQuery.getQueryWrapper();
|
||||
|
||||
FieldWrapper fieldWrapper = FieldWrapper.of(entity.getClass(), fieldQuery.getField());
|
||||
|
||||
Class<?> fieldType = fieldWrapper.getFieldType();
|
||||
Class<?> mappingType = fieldWrapper.getMappingType();
|
||||
|
||||
Object value;
|
||||
if (fieldType.isAssignableFrom(List.class)) {
|
||||
value = selectListByQueryAs(childQuery, mappingType);
|
||||
} else if (fieldType.isAssignableFrom(Set.class)) {
|
||||
value = selectListByQueryAs(childQuery, mappingType);
|
||||
value = new HashSet<>((Collection<?>) value);
|
||||
} else if (fieldType.isArray()) {
|
||||
value = selectListByQueryAs(childQuery, mappingType);
|
||||
value = ((List<?>) value).toArray();
|
||||
} else {
|
||||
value = selectOneByQueryAs(childQuery, mappingType);
|
||||
}
|
||||
fieldWrapper.set(value, entity);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,7 +66,7 @@ public interface IDialect {
|
||||
//////for entity /////
|
||||
String forInsertEntity(TableInfo tableInfo, Object entity, boolean ignoreNulls);
|
||||
|
||||
String forInsertEntityBatch(TableInfo tableInfo, List<Object> entities);
|
||||
String forInsertEntityBatch(TableInfo tableInfo, List<?> entities);
|
||||
|
||||
String forDeleteEntityById(TableInfo tableInfo);
|
||||
|
||||
|
||||
@ -415,7 +415,7 @@ public class CommonsDialectImpl implements IDialect {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String forInsertEntityBatch(TableInfo tableInfo, List<Object> entities) {
|
||||
public String forInsertEntityBatch(TableInfo tableInfo, List<?> entities) {
|
||||
StringBuilder sql = new StringBuilder();
|
||||
sql.append("INSERT INTO ").append(tableInfo.getWrapSchemaAndTableName(this));
|
||||
String[] insertColumns = tableInfo.obtainInsertColumns(null, false);
|
||||
|
||||
@ -17,9 +17,15 @@ package com.mybatisflex.core.dialect.impl;
|
||||
|
||||
import com.mybatisflex.core.dialect.KeywordWrap;
|
||||
import com.mybatisflex.core.dialect.LimitOffsetProcessor;
|
||||
import com.mybatisflex.core.row.Row;
|
||||
import com.mybatisflex.core.table.TableInfo;
|
||||
import com.mybatisflex.core.util.CollectionUtil;
|
||||
import com.mybatisflex.core.util.StringUtil;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
public class OracleDialect extends CommonsDialectImpl {
|
||||
|
||||
@ -56,8 +62,92 @@ public class OracleDialect extends CommonsDialectImpl {
|
||||
public OracleDialect() {
|
||||
this(LimitOffsetProcessor.ORACLE);
|
||||
}
|
||||
|
||||
public OracleDialect(LimitOffsetProcessor limitOffsetProcessor) {
|
||||
super(new KeywordWrap(keywords,"\"","\""),limitOffsetProcessor);
|
||||
super(new KeywordWrap(keywords, "\"", "\""), limitOffsetProcessor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String forInsertEntityBatch(TableInfo tableInfo, List<?> entities) {
|
||||
/**
|
||||
* INSERT ALL
|
||||
* INTO t (col1, col2, col3) VALUES ('val1_1', 'val1_2', 'val1_3')
|
||||
* INTO t (col1, col2, col3) VALUES ('val2_1', 'val2_2', 'val2_3')
|
||||
* INTO t (col1, col2, col3) VALUES ('val3_1', 'val3_2', 'val3_3')
|
||||
* .
|
||||
* .
|
||||
* .
|
||||
* SELECT 1 FROM DUAL;
|
||||
*/
|
||||
StringBuilder sql = new StringBuilder();
|
||||
sql.append("INSERT ALL");
|
||||
String[] insertColumns = tableInfo.obtainInsertColumns(null, false);
|
||||
String[] warpedInsertColumns = new String[insertColumns.length];
|
||||
for (int i = 0; i < insertColumns.length; i++) {
|
||||
warpedInsertColumns[i] = wrap(insertColumns[i]);
|
||||
}
|
||||
|
||||
|
||||
Map<String, String> onInsertColumns = tableInfo.getOnInsertColumns();
|
||||
for (int i = 0; i < entities.size(); i++) {
|
||||
sql.append(" INTO ").append(tableInfo.getWrapSchemaAndTableName(this));
|
||||
sql.append(" (").append(StringUtil.join(", ", warpedInsertColumns)).append(")");
|
||||
sql.append(" VALUES ");
|
||||
|
||||
StringJoiner stringJoiner = new StringJoiner(", ", "(", ")");
|
||||
for (String insertColumn : insertColumns) {
|
||||
if (onInsertColumns != null && onInsertColumns.containsKey(insertColumn)) {
|
||||
//直接读取 onInsert 配置的值,而不用 "?" 代替
|
||||
stringJoiner.add(onInsertColumns.get(insertColumn));
|
||||
} else {
|
||||
stringJoiner.add("?");
|
||||
}
|
||||
}
|
||||
sql.append(stringJoiner);
|
||||
}
|
||||
|
||||
return sql.append(" SELECT 1 FROM DUAL").toString();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String forInsertBatchWithFirstRowColumns(String schema, String tableName, List<Row> rows) {
|
||||
/**
|
||||
* INSERT ALL
|
||||
* INTO t (col1, col2, col3) VALUES ('val1_1', 'val1_2', 'val1_3')
|
||||
* INTO t (col1, col2, col3) VALUES ('val2_1', 'val2_2', 'val2_3')
|
||||
* INTO t (col1, col2, col3) VALUES ('val3_1', 'val3_2', 'val3_3')
|
||||
* .
|
||||
* .
|
||||
* .
|
||||
* SELECT 1 FROM DUAL;
|
||||
*/
|
||||
StringBuilder fields = new StringBuilder();
|
||||
Row firstRow = rows.get(0);
|
||||
Set<String> attrs = firstRow.obtainModifyAttrs();
|
||||
int index = 0;
|
||||
for (String column : attrs) {
|
||||
fields.append(wrap(column));
|
||||
if (index != attrs.size() - 1) {
|
||||
fields.append(", ");
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
StringBuilder sql = new StringBuilder();
|
||||
sql.append("INSERT ALL");
|
||||
|
||||
String tableNameWrap = StringUtil.isNotBlank(schema)
|
||||
? wrap(getRealSchema(schema)) + "." + wrap(getRealTable(tableName))
|
||||
: wrap(getRealTable(tableName));
|
||||
String questionStrings = buildQuestion(attrs.size(), true);
|
||||
|
||||
for (int i = 0; i < rows.size(); i++) {
|
||||
sql.append(" INTO ").append(tableNameWrap);
|
||||
sql.append(" (").append(fields).append(")");
|
||||
sql.append(" VALUES ").append(questionStrings);
|
||||
}
|
||||
|
||||
return sql.append(" SELECT 1 FROM DUAL").toString();
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,6 +34,7 @@ import org.apache.ibatis.executor.keygen.KeyGenerator;
|
||||
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
|
||||
import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
|
||||
import org.apache.ibatis.executor.parameter.ParameterHandler;
|
||||
import org.apache.ibatis.executor.resultset.ResultSetHandler;
|
||||
import org.apache.ibatis.executor.statement.StatementHandler;
|
||||
import org.apache.ibatis.mapping.*;
|
||||
import org.apache.ibatis.session.*;
|
||||
@ -93,6 +94,17 @@ public class FlexConfiguration extends Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement
|
||||
, RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) {
|
||||
// ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler,
|
||||
// resultHandler, boundSql, rowBounds);
|
||||
ResultSetHandler resultSetHandler = new FlexResultSetHandler(executor, mappedStatement, parameterHandler,
|
||||
resultHandler, boundSql, rowBounds);
|
||||
return (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换为 FlexStatementHandler,主要用来为实体类的多主键做支持、和数据审计
|
||||
* FlexStatementHandler 和 原生的 RoutingStatementHandler 对比,没有任何性能影响
|
||||
|
||||
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.mybatisflex.core.mybatis;
|
||||
|
||||
import com.mybatisflex.core.transaction.TransactionContext;
|
||||
import org.apache.ibatis.cursor.Cursor;
|
||||
import org.apache.ibatis.cursor.defaults.DefaultCursor;
|
||||
import org.apache.ibatis.executor.Executor;
|
||||
import org.apache.ibatis.executor.parameter.ParameterHandler;
|
||||
import org.apache.ibatis.executor.resultset.DefaultResultSetHandler;
|
||||
import org.apache.ibatis.mapping.BoundSql;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.session.ResultHandler;
|
||||
import org.apache.ibatis.session.RowBounds;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class FlexResultSetHandler extends DefaultResultSetHandler {
|
||||
|
||||
public FlexResultSetHandler(Executor executor, MappedStatement mappedStatement, ParameterHandler parameterHandler, ResultHandler<?> resultHandler, BoundSql boundSql, RowBounds rowBounds) {
|
||||
super(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException {
|
||||
return new FlexCursor<>(super.handleCursorResultSets(stmt));
|
||||
}
|
||||
|
||||
static class FlexCursor<T> extends DefaultCursor<T> {
|
||||
|
||||
private Cursor originalCursor;
|
||||
|
||||
public FlexCursor(Cursor cursor) {
|
||||
super(null, null, null, null);
|
||||
this.originalCursor = cursor;
|
||||
TransactionContext.holdCursor(cursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
//非事务场景下,通过 releaseCursor 对 cursor 进行 close
|
||||
if (TransactionContext.getXID() == null) {
|
||||
TransactionContext.releaseCursor();
|
||||
}
|
||||
//else 在事务的场景下,由事务主动关闭
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return originalCursor.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConsumed() {
|
||||
return originalCursor.isConsumed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCurrentIndex() {
|
||||
return originalCursor.getCurrentIndex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
try {
|
||||
return originalCursor.iterator();
|
||||
} catch (IllegalStateException e) {
|
||||
if (TransactionContext.getXID() == null) {
|
||||
throw new IllegalStateException(e.getMessage() + " Cursor must use in transaction.");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -37,7 +37,7 @@ public class FlexSqlSessionFactoryBuilder extends SqlSessionFactoryBuilder {
|
||||
@Override
|
||||
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
|
||||
try {
|
||||
// 需要 mybaits v3.5.13+
|
||||
// 需要 mybatis v3.5.13+
|
||||
// https://github.com/mybatis/mybatis-3/commit/d7826d71a7005a8b4d4e0e7a020db0f6c7e253a4
|
||||
XMLConfigBuilder parser = new XMLConfigBuilder(FlexConfiguration.class, reader, environment, properties);
|
||||
return build(parser.parse());
|
||||
@ -59,7 +59,7 @@ public class FlexSqlSessionFactoryBuilder extends SqlSessionFactoryBuilder {
|
||||
@Override
|
||||
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
|
||||
try {
|
||||
// 需要 mybaits v3.5.13+
|
||||
// 需要 mybatis v3.5.13+
|
||||
// https://github.com/mybatis/mybatis-3/commit/d7826d71a7005a8b4d4e0e7a020db0f6c7e253a4
|
||||
XMLConfigBuilder parser = new XMLConfigBuilder(FlexConfiguration.class, inputStream, environment, properties);
|
||||
return build(parser.parse());
|
||||
|
||||
@ -54,6 +54,12 @@ public class QueryColumn implements Serializable {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public QueryColumn(QueryTable queryTable, String name) {
|
||||
SqlUtil.keepColumnSafely(name);
|
||||
this.table = queryTable;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
public QueryTable getTable() {
|
||||
return table;
|
||||
|
||||
@ -16,11 +16,16 @@
|
||||
package com.mybatisflex.core.transaction;
|
||||
|
||||
|
||||
import org.apache.ibatis.cursor.Cursor;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class TransactionContext {
|
||||
|
||||
private TransactionContext() {}
|
||||
|
||||
private static final ThreadLocal<String> XID_HOLDER = new ThreadLocal<>();
|
||||
private static final ThreadLocal<Cursor<?>> CURSOR_HOLDER = new ThreadLocal<>();
|
||||
|
||||
public static String getXID() {
|
||||
return XID_HOLDER.get();
|
||||
@ -28,10 +33,29 @@ public class TransactionContext {
|
||||
|
||||
public static void release() {
|
||||
XID_HOLDER.remove();
|
||||
releaseCursor();
|
||||
}
|
||||
|
||||
public static void hold(String xid) {
|
||||
public static void releaseCursor() {
|
||||
try {
|
||||
Cursor<?> cursor = CURSOR_HOLDER.get();
|
||||
if (cursor != null){
|
||||
try {
|
||||
cursor.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}finally {
|
||||
CURSOR_HOLDER.remove();
|
||||
}
|
||||
}
|
||||
|
||||
public static void holdXID(String xid) {
|
||||
XID_HOLDER.set(xid);
|
||||
}
|
||||
|
||||
public static void holdCursor(Cursor<?> cursor) {
|
||||
CURSOR_HOLDER.set(cursor);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -113,7 +113,7 @@ public class TransactionalManager {
|
||||
} finally {
|
||||
//恢复上一级事务
|
||||
if (currentXID != null) {
|
||||
TransactionContext.hold(currentXID);
|
||||
TransactionContext.holdXID(currentXID);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -147,7 +147,7 @@ public class TransactionalManager {
|
||||
|
||||
public static String startTransactional() {
|
||||
String xid = UUID.randomUUID().toString();
|
||||
TransactionContext.hold(xid);
|
||||
TransactionContext.holdXID(xid);
|
||||
return xid;
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.mybatisflex.core.util;
|
||||
|
||||
import com.mybatisflex.core.BaseMapper;
|
||||
import com.mybatisflex.core.field.FieldQuery;
|
||||
import com.mybatisflex.core.field.FieldQueryBuilder;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import org.apache.ibatis.exceptions.TooManyResultsException;
|
||||
import org.apache.ibatis.session.defaults.DefaultSqlSession;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class MapperUtil {
|
||||
|
||||
public static <R> void queryFields(BaseMapper<?> mapper, List<R> list, Consumer<FieldQueryBuilder<R>>[] consumers) {
|
||||
if (CollectionUtil.isEmpty(list) || ArrayUtil.isEmpty(consumers) || consumers[0] == null) {
|
||||
return;
|
||||
}
|
||||
list.forEach(entity -> {
|
||||
for (Consumer<FieldQueryBuilder<R>> consumer : consumers) {
|
||||
FieldQueryBuilder<R> fieldQueryBuilder = new FieldQueryBuilder<>(entity);
|
||||
consumer.accept(fieldQueryBuilder);
|
||||
FieldQuery fieldQuery = fieldQueryBuilder.build();
|
||||
QueryWrapper childQuery = fieldQuery.getQueryWrapper();
|
||||
|
||||
FieldWrapper fieldWrapper = FieldWrapper.of(entity.getClass(), fieldQuery.getField());
|
||||
|
||||
Class<?> fieldType = fieldWrapper.getFieldType();
|
||||
Class<?> mappingType = fieldWrapper.getMappingType();
|
||||
|
||||
Object value;
|
||||
if (fieldType.isAssignableFrom(List.class)) {
|
||||
value = mapper.selectListByQueryAs(childQuery, mappingType);
|
||||
} else if (fieldType.isAssignableFrom(Set.class)) {
|
||||
value = mapper.selectListByQueryAs(childQuery, mappingType);
|
||||
value = new HashSet<>((Collection<?>) value);
|
||||
} else if (fieldType.isArray()) {
|
||||
value = mapper.selectListByQueryAs(childQuery, mappingType);
|
||||
value = ((List<?>) value).toArray();
|
||||
} else {
|
||||
value = mapper.selectOneByQueryAs(childQuery, mappingType);
|
||||
}
|
||||
fieldWrapper.set(value, entity);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 搬运加改造 {@link DefaultSqlSession#selectOne(String, Object)}
|
||||
*/
|
||||
public static <T> T getSelectOneResult(List<T> list) {
|
||||
if (list == null || list.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
int size = list.size();
|
||||
if (size == 1) {
|
||||
return list.get(0);
|
||||
}
|
||||
throw new TooManyResultsException(
|
||||
"Expected one result (or null) to be returned by selectOne(), but found: " + size);
|
||||
}
|
||||
}
|
||||
@ -15,12 +15,8 @@
|
||||
*/
|
||||
package com.mybatisflex.core.util;
|
||||
|
||||
import org.apache.ibatis.exceptions.TooManyResultsException;
|
||||
import org.apache.ibatis.session.defaults.DefaultSqlSession;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
public class SqlUtil {
|
||||
@ -83,6 +79,12 @@ public class SqlUtil {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 替换 sql 中的问号 ?
|
||||
* @param sql sql 内容
|
||||
* @param params 参数
|
||||
* @return 完整的 sql
|
||||
*/
|
||||
public static String replaceSqlParams(String sql, Object[] params) {
|
||||
if (params != null && params.length > 0) {
|
||||
for (Object value : params) {
|
||||
@ -113,19 +115,6 @@ public class SqlUtil {
|
||||
return sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* 搬运加改造 {@link DefaultSqlSession#selectOne(String, Object)}
|
||||
*/
|
||||
public static <T> T getSelectOneResult(List<T> list) {
|
||||
if (list == null || list.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
int size = list.size();
|
||||
if (size == 1) {
|
||||
return list.get(0);
|
||||
}
|
||||
throw new TooManyResultsException(
|
||||
"Expected one result (or null) to be returned by selectOne(), but found: " + size);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,84 @@
|
||||
package com.mybatisflex.coretest;
|
||||
|
||||
import com.mybatisflex.core.dialect.IDialect;
|
||||
import com.mybatisflex.core.dialect.impl.OracleDialect;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import com.mybatisflex.core.row.Row;
|
||||
import com.mybatisflex.core.table.TableInfoFactory;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.mybatisflex.coretest.table.AccountTableDef.ACCOUNT;
|
||||
|
||||
public class OracleDialectTester {
|
||||
|
||||
|
||||
@Test
|
||||
public void testSelectSql() {
|
||||
QueryWrapper query = new QueryWrapper()
|
||||
.select()
|
||||
.from(ACCOUNT);
|
||||
|
||||
IDialect dialect = new OracleDialect();
|
||||
String sql = dialect.forSelectByQuery(query);
|
||||
System.out.println(sql);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertBatchSql() {
|
||||
List<Account> accounts = new ArrayList<>();
|
||||
Account account1 = new Account();
|
||||
account1.setUserName("michael1");
|
||||
account1.setAge(18);
|
||||
account1.setSex(1);
|
||||
accounts.add(account1);
|
||||
|
||||
Account account2 = new Account();
|
||||
account2.setUserName("michael2");
|
||||
account2.setAge(19);
|
||||
account2.setSex(2);
|
||||
accounts.add(account2);
|
||||
|
||||
Account account3 = new Account();
|
||||
account3.setUserName("michael3");
|
||||
account3.setAge(20);
|
||||
account3.setSex(3);
|
||||
accounts.add(account3);
|
||||
|
||||
|
||||
IDialect dialect = new OracleDialect();
|
||||
String sql = dialect.forInsertEntityBatch(TableInfoFactory.ofEntityClass(Account.class),accounts);
|
||||
System.out.println(sql);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testInsertRowBatchSql() {
|
||||
List<Row> accounts = new ArrayList<>();
|
||||
Row account1 = new Row();
|
||||
account1.set("username","michael1");
|
||||
account1.set("age",18);
|
||||
account1.set("sex",1);
|
||||
accounts.add(account1);
|
||||
|
||||
Row account2 = new Row();
|
||||
account2.set("username","michael2");
|
||||
account2.set("age",18);
|
||||
account2.set("sex",1);
|
||||
accounts.add(account2);
|
||||
|
||||
Row account3 = new Row();
|
||||
account3.set("username","michael3");
|
||||
account3.set("age",18);
|
||||
account3.set("sex",1);
|
||||
accounts.add(account3);
|
||||
|
||||
|
||||
IDialect dialect = new OracleDialect();
|
||||
String sql = dialect.forInsertBatchWithFirstRowColumns(null,"tb_account",accounts);
|
||||
System.out.println(sql);
|
||||
}
|
||||
|
||||
}
|
||||
@ -156,6 +156,9 @@ public class QueryEntityProcessor extends AbstractProcessor {
|
||||
//upperCase, lowerCase, upperCamelCase, lowerCamelCase
|
||||
String tablesNameStyle = props.getProperties().getProperty("processor.tablesNameStyle", "upperCase");
|
||||
|
||||
//包名对应的变量后缀
|
||||
String tablesDefSuffix = props.getProperties().getProperty("processor.tablesDefSuffix", "");
|
||||
|
||||
String[] entityIgnoreSuffixes = props.getProperties().getProperty("processor.entity.ignoreSuffixes", "").split(",");
|
||||
|
||||
|
||||
@ -207,13 +210,15 @@ public class QueryEntityProcessor extends AbstractProcessor {
|
||||
}
|
||||
|
||||
if (allInTables) {
|
||||
String content = buildTablesClass(entitySimpleName, schema, tableName, propertyAndColumns, defaultColumns, tablesNameStyle, null, allInTables);
|
||||
String content = buildTablesClass(entitySimpleName, schema, tableName, propertyAndColumns, defaultColumns, tablesNameStyle
|
||||
, tablesDefSuffix, null, allInTables);
|
||||
tablesContent.append(content);
|
||||
}
|
||||
//每一个 entity 生成一个独立的文件
|
||||
else {
|
||||
String realGenPackage = genTablesPackage == null || genTablesPackage.trim().length() == 0 ? guessPackage.toString() : genTablesPackage;
|
||||
String content = buildTablesClass(entitySimpleName, schema, tableName, propertyAndColumns, defaultColumns, tablesNameStyle, realGenPackage, allInTables);
|
||||
String content = buildTablesClass(entitySimpleName, schema, tableName, propertyAndColumns, defaultColumns, tablesNameStyle
|
||||
, tablesDefSuffix, realGenPackage, allInTables);
|
||||
genClass(genPath, realGenPackage, entitySimpleName + "TableDef", content);
|
||||
}
|
||||
|
||||
@ -332,13 +337,14 @@ public class QueryEntityProcessor extends AbstractProcessor {
|
||||
|
||||
|
||||
private String buildTablesClass(String entityClass, String schema, String tableName, Map<String, String> propertyAndColumns
|
||||
, List<String> defaultColumns, String tablesNameStyle, String realGenPackage, boolean allInTables) {
|
||||
, List<String> defaultColumns, String tablesNameStyle, String tablesDefSuffix, String realGenPackage, boolean allInTables) {
|
||||
|
||||
// tableDefTemplate = " public static final @entityClassTableDef @tableField = new @entityClassTableDef(\"@tableName\");\n";
|
||||
|
||||
String tableDef = tableDefTemplate.replace("@entityClass", entityClass)
|
||||
.replace("@schema", schema)
|
||||
.replace("@tableField", buildName(entityClass, tablesNameStyle))
|
||||
.replace("@tableField", buildName(entityClass, tablesNameStyle)
|
||||
+ (tablesDefSuffix != null ? tablesDefSuffix.trim() : ""))
|
||||
.replace("@tableName", tableName);
|
||||
|
||||
|
||||
|
||||
@ -68,7 +68,7 @@
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid</artifactId>
|
||||
<version>1.2.17</version>
|
||||
<version>1.2.18</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
@ -46,7 +46,7 @@ public class FlexTransactionManager extends AbstractPlatformTransactionManager {
|
||||
@Override
|
||||
protected void doResume(Object transaction, Object suspendedResources) throws TransactionException {
|
||||
String xid = (String) suspendedResources;
|
||||
TransactionContext.hold(xid);
|
||||
TransactionContext.holdXID(xid);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -19,13 +19,16 @@ import com.mybatisflex.core.MybatisFlexBootstrap;
|
||||
import com.mybatisflex.core.audit.AuditManager;
|
||||
import com.mybatisflex.core.audit.ConsoleMessageCollector;
|
||||
import com.mybatisflex.core.audit.MessageCollector;
|
||||
import com.mybatisflex.core.paginate.Page;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import com.mybatisflex.core.row.Db;
|
||||
import org.apache.ibatis.cursor.Cursor;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static com.mybatisflex.test.table.AccountTableDef.ACCOUNT;
|
||||
import static com.mybatisflex.test.table.ArticleTableDef.ARTICLE;
|
||||
|
||||
@ -138,8 +141,30 @@ public class EntityTestStarter {
|
||||
//
|
||||
// List<ArticleDTO> articleDTOS = accountMapper.selectListByQueryAs(asWrapper, ArticleDTO.class);
|
||||
// System.out.println(articleDTOS);
|
||||
Page<ArticleDTO01> paginate = accountMapper.paginateAs(Page.of(1, 10), asWrapper, ArticleDTO01.class);
|
||||
System.out.println(paginate);
|
||||
// Page<ArticleDTO01> paginate = accountMapper.paginateAs(Page.of(1, 10), asWrapper, ArticleDTO01.class);
|
||||
// System.out.println(paginate);
|
||||
|
||||
Db.tx(new Supplier<Boolean>() {
|
||||
@Override
|
||||
public Boolean get() {
|
||||
Cursor<Account> accounts = accountMapper.selectCursorByQuery(asWrapper);
|
||||
System.out.println(accounts.isOpen());
|
||||
for (Account account : accounts) {
|
||||
System.out.println(accounts.isOpen());
|
||||
System.out.println(account);
|
||||
}
|
||||
System.out.println(accounts.isOpen());
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// Cursor<Account> accounts = accountMapper.selectCursorByQuery(asWrapper);
|
||||
// System.out.println(accounts.isOpen());
|
||||
// for (Account account : accounts) {
|
||||
// System.out.println(accounts.isOpen());
|
||||
// System.out.println(account);
|
||||
// }
|
||||
// System.out.println(accounts.isOpen());
|
||||
|
||||
|
||||
// QueryWrapper queryWrapper = new QueryWrapper();
|
||||
|
||||
@ -1 +1,3 @@
|
||||
processor.mappersGenerateEnable = true
|
||||
processor.tablesNameStyle = lowerCase
|
||||
processor.tablesDefSuffix = Def
|
||||
|
||||
@ -14,7 +14,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.mybatisflex.test.model.table.AccountTableDef.ACCOUNT;
|
||||
import static com.mybatisflex.test.model.table.AccountTableDef.accountDef;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(classes = AppConfig.class)
|
||||
@ -33,7 +33,7 @@ public class AccountTest implements WithAssertions {
|
||||
@Test
|
||||
public void testSelectByQuery() {
|
||||
QueryWrapper queryWrapper = QueryWrapper.create()
|
||||
.where(ACCOUNT.AGE.eq(18));
|
||||
.where(accountDef.age.eq(18));
|
||||
List<Account> accounts = accountMapper.selectListByQuery(queryWrapper);
|
||||
assertThat(accounts.size()).isEqualTo(1);
|
||||
assertThat(accounts.get(0).getAge()).isEqualTo(18);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user