├── .gitignore ├── .editorconfig ├── huitool-core ├── src │ ├── test │ │ ├── resources │ │ │ ├── schema.sql │ │ │ └── data.sql │ │ └── java │ │ │ └── com │ │ │ └── huitool │ │ │ ├── data │ │ │ ├── City.java │ │ │ ├── mybatis │ │ │ │ └── SQLSelectClauseTests.java │ │ │ ├── CityRepository.java │ │ │ └── SpringDataJdbcTests.java │ │ │ └── JsonTests.java │ └── main │ │ └── java │ │ ├── com │ │ └── huitool │ │ │ ├── util │ │ │ ├── Functions.java │ │ │ └── LambdaUtils.java │ │ │ ├── data │ │ │ └── mybatis │ │ │ │ └── plugins │ │ │ │ ├── pagination │ │ │ │ ├── DataPage.java │ │ │ │ ├── SqlDialect.java │ │ │ │ ├── support │ │ │ │ │ ├── HSQLDBDialect.java │ │ │ │ │ ├── PostgreSQLDialect.java │ │ │ │ │ ├── MySQLDialect.java │ │ │ │ │ ├── OracleDialect.java │ │ │ │ │ └── AbstractSqlDialect.java │ │ │ │ └── PaginationInterceptor.java │ │ │ │ └── jdbc │ │ │ │ ├── expression │ │ │ │ ├── Expression.java │ │ │ │ ├── NotNullExpression.java │ │ │ │ ├── NullExpression.java │ │ │ │ ├── NotInExpression.java │ │ │ │ ├── SimpleExpression.java │ │ │ │ ├── BetweenExpression.java │ │ │ │ ├── InExpression.java │ │ │ │ └── Expressions.java │ │ │ │ ├── Table.java │ │ │ │ ├── Column.java │ │ │ │ ├── SqlColumn.java │ │ │ │ └── dml │ │ │ │ └── SQLSelectClause.java │ │ │ └── web │ │ │ └── ServletExceptionHandler.java │ │ └── org │ │ └── springframework │ │ └── data │ │ └── jdbc │ │ ├── repository │ │ ├── query │ │ │ └── MybatisQuery.java │ │ └── support │ │ │ ├── MybatisJdbcRepositoryFactoryBean.java │ │ │ ├── DelegatingJdbcQueryLookupStrategy.java │ │ │ └── DelegatingJdbcRepositoryFactory.java │ │ └── mybatis │ │ └── support │ │ ├── MybatisQueryMethod.java │ │ └── MybatisRepositoryQuery.java └── pom.xml ├── huitool-spring-boot-autoconfigure ├── src │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── spring.factories │ │ └── java │ │ └── com │ │ └── huitool │ │ └── autoconfigure │ │ ├── WebAutoConfiguration.java │ │ ├── MybatisAutoConfiguration.java │ │ └── JdbcRepositoryAutoConfiguration.java └── pom.xml ├── huitool-spring-boot-starter └── pom.xml └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | *.ipr 3 | *.iws 4 | .idea/ 5 | target/ 6 | pom.xml.tag 7 | pom.xml.releaseBackup 8 | pom.xml.next 9 | release.properties 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root=true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | trim_trailing_whitespace = true 9 | -------------------------------------------------------------------------------- /huitool-core/src/test/resources/schema.sql: -------------------------------------------------------------------------------- 1 | drop table if exists city; 2 | 3 | create table city ( 4 | id integer generated by default as identity(start with 1) not null primary key, 5 | name varchar(100), 6 | state varchar(100), 7 | country varchar(100) 8 | ); 9 | 10 | -------------------------------------------------------------------------------- /huitool-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 | com.huitool.autoconfigure.WebAutoConfiguration,\ 3 | com.huitool.autoconfigure.JdbcRepositoryAutoConfiguration,\ 4 | com.huitool.autoconfigure.MybatisAutoConfiguration -------------------------------------------------------------------------------- /huitool-core/src/main/java/com/huitool/util/Functions.java: -------------------------------------------------------------------------------- 1 | package com.huitool.util; 2 | 3 | import java.io.Serializable; 4 | import java.util.function.Function; 5 | 6 | /** 7 | * <描述信息> 8 | */ 9 | public class Functions { 10 | @FunctionalInterface 11 | public interface SerializableFunction extends Function, Serializable {} 12 | } 13 | -------------------------------------------------------------------------------- /huitool-core/src/main/java/com/huitool/data/mybatis/plugins/pagination/DataPage.java: -------------------------------------------------------------------------------- 1 | package com.huitool.data.mybatis.plugins.pagination; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.ToString; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * <描述信息> 11 | */ 12 | @Getter 13 | @ToString 14 | @AllArgsConstructor 15 | public class DataPage{ 16 | private final long total; 17 | private final List rows; 18 | } 19 | -------------------------------------------------------------------------------- /huitool-core/src/main/java/com/huitool/data/mybatis/plugins/pagination/SqlDialect.java: -------------------------------------------------------------------------------- 1 | package com.huitool.data.mybatis.plugins.pagination; 2 | 3 | import org.apache.ibatis.mapping.MappedStatement; 4 | 5 | import java.sql.Connection; 6 | import java.sql.SQLException; 7 | 8 | /** 9 | * <描述信息> 10 | */ 11 | public interface SqlDialect { 12 | String getName(); 13 | String getLimitString(String sql, int offset, int limit); 14 | int getCount(MappedStatement ms, Connection connection, Object parameterObject) throws SQLException; 15 | } 16 | -------------------------------------------------------------------------------- /huitool-core/src/test/java/com/huitool/data/City.java: -------------------------------------------------------------------------------- 1 | package com.huitool.data; 2 | 3 | import com.huitool.data.mybatis.plugins.jdbc.Table; 4 | import lombok.Data; 5 | import org.springframework.data.annotation.Id; 6 | import org.springframework.data.annotation.Transient; 7 | 8 | /** 9 | * <描述信息> 10 | */ 11 | @Data 12 | @Table("city") 13 | public class City { 14 | @Id 15 | private Long id; 16 | private String name; 17 | private String state; 18 | private String country; 19 | @Transient 20 | private String remark; 21 | } 22 | -------------------------------------------------------------------------------- /huitool-core/src/main/java/com/huitool/data/mybatis/plugins/jdbc/expression/Expression.java: -------------------------------------------------------------------------------- 1 | package com.huitool.data.mybatis.plugins.jdbc.expression; 2 | 3 | 4 | import com.huitool.data.mybatis.plugins.jdbc.SqlColumn; 5 | 6 | public interface Expression { 7 | default Object getValue() { 8 | return null; 9 | } 10 | 11 | default Object[] getValues() { 12 | return null; 13 | } 14 | 15 | default String render(String tableAliases, int index) { 16 | return ""; 17 | } 18 | 19 | SqlColumn getColumn(); 20 | } 21 | -------------------------------------------------------------------------------- /huitool-core/src/main/java/org/springframework/data/jdbc/repository/query/MybatisQuery.java: -------------------------------------------------------------------------------- 1 | package org.springframework.data.jdbc.repository.query; 2 | 3 | import org.springframework.data.annotation.QueryAnnotation; 4 | 5 | import java.lang.annotation.Documented; 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | /** 12 | * <描述信息> 13 | */ 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Target(ElementType.METHOD) 16 | @QueryAnnotation 17 | @Documented 18 | public @interface MybatisQuery { 19 | } 20 | -------------------------------------------------------------------------------- /huitool-core/src/main/java/com/huitool/data/mybatis/plugins/jdbc/Table.java: -------------------------------------------------------------------------------- 1 | package com.huitool.data.mybatis.plugins.jdbc; 2 | 3 | import org.springframework.core.annotation.AliasFor; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * <描述信息> 12 | */ 13 | @Target({ElementType.TYPE}) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @org.springframework.data.relational.core.mapping.Table("") 16 | public @interface Table { 17 | @AliasFor(annotation = org.springframework.data.relational.core.mapping.Table.class) 18 | String value(); 19 | } 20 | -------------------------------------------------------------------------------- /huitool-core/src/main/java/com/huitool/data/mybatis/plugins/jdbc/expression/NotNullExpression.java: -------------------------------------------------------------------------------- 1 | package com.huitool.data.mybatis.plugins.jdbc.expression; 2 | 3 | import com.huitool.data.mybatis.plugins.jdbc.SqlColumn; 4 | import org.springframework.util.StringUtils; 5 | 6 | public class NotNullExpression extends NullExpression { 7 | public NotNullExpression(SqlColumn sqlColumn) { 8 | super(sqlColumn); 9 | } 10 | 11 | @Override 12 | public String render(String tableAliases, int index) { 13 | String name = StringUtils.hasText(tableAliases) ? String.format("%s.%s", tableAliases, column.name()) : column.name(); 14 | return String.format("%s is not null", name); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /huitool-core/src/main/java/com/huitool/data/mybatis/plugins/jdbc/expression/NullExpression.java: -------------------------------------------------------------------------------- 1 | package com.huitool.data.mybatis.plugins.jdbc.expression; 2 | 3 | import com.huitool.data.mybatis.plugins.jdbc.SqlColumn; 4 | import org.springframework.util.StringUtils; 5 | 6 | public class NullExpression implements Expression { 7 | protected SqlColumn column; 8 | 9 | public NullExpression(SqlColumn sqlColumn) { 10 | this.column = sqlColumn; 11 | } 12 | 13 | @Override 14 | public SqlColumn getColumn() { 15 | return this.column; 16 | } 17 | 18 | @Override 19 | public String render(String tableAliases, int index) { 20 | String name = StringUtils.hasText(tableAliases) ? String.format("%s.%s", tableAliases, column.name()) : column.name(); 21 | return String.format("%s is null", name); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /huitool-core/src/test/resources/data.sql: -------------------------------------------------------------------------------- 1 | insert into city (name, state, country) values ('San Francisco', 'CA', 'US'); 2 | insert into city (name, state, country) values ('Jinan', 'Shandong', 'China'); 3 | insert into city (name, state, country) values ('Qindao', 'Shandong', 'China'); 4 | insert into city (name, state, country) values ('Yantai', 'Shandong', 'China'); 5 | insert into city (name, state, country) values ('Taian', 'Shandong', 'China'); 6 | insert into city (name, state, country) values ('Weifang', 'Shandong', 'China'); 7 | insert into city (name, state, country) values ('Zibo', 'Shandong', 'China'); 8 | insert into city (name, state, country) values ('Dongying', 'Shandong', 'China'); 9 | insert into city (name, state, country) values ('Yantai', 'Shandong', 'China'); 10 | insert into city (name, state, country) values ('Weihai', 'Shandong', 'China'); 11 | insert into city (name, state, country) values ('Zaozhuang', 'Shandong', 'China'); 12 | -------------------------------------------------------------------------------- /huitool-core/src/main/java/com/huitool/data/mybatis/plugins/jdbc/Column.java: -------------------------------------------------------------------------------- 1 | package com.huitool.data.mybatis.plugins.jdbc; 2 | 3 | import org.springframework.core.annotation.AliasFor; 4 | 5 | import java.lang.annotation.Documented; 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | /** 12 | * <描述信息> 13 | */ 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Target({ ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE }) 16 | @Documented 17 | @org.springframework.data.relational.core.mapping.Column 18 | public @interface Column { 19 | @AliasFor(annotation = org.springframework.data.relational.core.mapping.Column.class) 20 | String value() default ""; 21 | 22 | @AliasFor(annotation = org.springframework.data.relational.core.mapping.Column.class) 23 | String keyColumn() default ""; 24 | } 25 | -------------------------------------------------------------------------------- /huitool-core/src/test/java/com/huitool/data/mybatis/SQLSelectClauseTests.java: -------------------------------------------------------------------------------- 1 | package com.huitool.data.mybatis; 2 | 3 | import com.huitool.data.City; 4 | import com.huitool.data.mybatis.plugins.jdbc.SqlColumn; 5 | import com.huitool.data.mybatis.plugins.jdbc.dml.SQLSelectClause; 6 | import org.junit.Test; 7 | 8 | /** 9 | * <描述信息> 10 | */ 11 | public class SQLSelectClauseTests { 12 | @Test 13 | public void testSelect(){ 14 | SQLSelectClause selectClause = new SQLSelectClause(); 15 | System.out.println(selectClause.from(City.class).getSQL()); 16 | } 17 | 18 | @Test 19 | public void testSelectAliases(){ 20 | final SqlColumn NAME = SqlColumn.of(City::getName); 21 | System.out.println( 22 | new SQLSelectClause() 23 | .from(City.class) 24 | .where(SqlColumn.of(City::getId).eq(1L)) 25 | .where(NAME.eq("Shandong")) 26 | .getSQL() 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /huitool-core/src/main/java/com/huitool/data/mybatis/plugins/pagination/support/HSQLDBDialect.java: -------------------------------------------------------------------------------- 1 | package com.huitool.data.mybatis.plugins.pagination.support; 2 | 3 | /** 4 | * <描述信息> 5 | */ 6 | public class HSQLDBDialect extends AbstractSqlDialect { 7 | @Override 8 | public String getName() { 9 | return "hsqldb"; 10 | } 11 | 12 | @Override 13 | public String getLimitString(String sql, int offset, int limit) { 14 | return getLimitString(sql, offset, Integer.toString(offset), Integer.toString(limit)); 15 | } 16 | 17 | private String getLimitString(String sql, int offset, String offsetPlaceholder, String limitPlaceholder) { 18 | boolean hasOffset = offset > 0; 19 | return new StringBuilder() 20 | .append(getLineSql(sql)) 21 | .insert(sql.toLowerCase().indexOf("select") + 6, hasOffset ? " limit " + offsetPlaceholder + " " + limitPlaceholder : " top " + limitPlaceholder) 22 | .toString(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /huitool-core/src/main/java/com/huitool/data/mybatis/plugins/pagination/support/PostgreSQLDialect.java: -------------------------------------------------------------------------------- 1 | package com.huitool.data.mybatis.plugins.pagination.support; 2 | 3 | /** 4 | * <描述信息> 5 | */ 6 | public class PostgreSQLDialect extends AbstractSqlDialect { 7 | @Override 8 | public String getName() { 9 | return "postgresql"; 10 | } 11 | 12 | @Override 13 | public String getLimitString(String sql, int offset, int limit) { 14 | return this.getLimitString(sql, offset, Integer.toString(offset), Integer.toString(limit)); 15 | } 16 | 17 | private String getLimitString(String sql, int offset, String offsetPlaceholder, String limitPlaceholder) { 18 | StringBuilder sb = new StringBuilder(this.getLineSql(sql)); 19 | sb.append(" limit "); 20 | 21 | if (offset > 0) { 22 | sb.append(limitPlaceholder).append(" offset ").append(offsetPlaceholder); 23 | } else { 24 | sb.append(limitPlaceholder); 25 | } 26 | 27 | return sb.toString(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /huitool-core/src/main/java/com/huitool/data/mybatis/plugins/pagination/support/MySQLDialect.java: -------------------------------------------------------------------------------- 1 | package com.huitool.data.mybatis.plugins.pagination.support; 2 | 3 | /** 4 | * <描述信息> 5 | */ 6 | public class MySQLDialect extends AbstractSqlDialect { 7 | @Override 8 | public String getName() { 9 | return "mysql"; 10 | } 11 | 12 | @Override 13 | public String getLimitString(String sql, int offset, int limit) { 14 | return getLimitString(sql, offset, Integer.toString(offset), Integer.toString(limit)); 15 | } 16 | 17 | private String getLimitString(final String sql, final int offset, 18 | final String offsetPlaceholder, final String limitPlaceholder) { 19 | StringBuilder sb = new StringBuilder(getLineSql(sql)); 20 | sb.append(" limit "); 21 | if (offset > 0) { 22 | sb.append(offsetPlaceholder).append(",").append(limitPlaceholder); 23 | } else { 24 | sb.append(limitPlaceholder); 25 | } 26 | 27 | return sb.toString(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /huitool-spring-boot-autoconfigure/src/main/java/com/huitool/autoconfigure/WebAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.huitool.autoconfigure; 2 | 3 | import com.huitool.web.ServletExceptionHandler; 4 | import org.springframework.boot.autoconfigure.AutoConfigureAfter; 5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; 7 | import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.web.servlet.DispatcherServlet; 11 | 12 | /** 13 | * <描述信息> 14 | */ 15 | @Configuration 16 | @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) 17 | @ConditionalOnClass({DispatcherServlet.class }) 18 | @AutoConfigureAfter(WebMvcAutoConfiguration.class) 19 | public class WebAutoConfiguration { 20 | 21 | @Bean 22 | public ServletExceptionHandler servletExceptionHandler(){ 23 | return new ServletExceptionHandler(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /huitool-core/src/test/java/com/huitool/data/CityRepository.java: -------------------------------------------------------------------------------- 1 | package com.huitool.data; 2 | 3 | import com.huitool.data.mybatis.plugins.pagination.DataPage; 4 | import com.huitool.data.mybatis.plugins.pagination.PaginationInterceptor; 5 | import org.apache.ibatis.annotations.Mapper; 6 | import org.apache.ibatis.annotations.Select; 7 | import org.apache.ibatis.session.RowBounds; 8 | import org.springframework.data.jdbc.repository.query.MybatisQuery; 9 | import org.springframework.data.repository.CrudRepository; 10 | 11 | import java.util.List; 12 | import java.util.Optional; 13 | 14 | /** 15 | * <描述信息> 16 | */ 17 | @Mapper 18 | public interface CityRepository extends CrudRepository { 19 | @MybatisQuery 20 | @Select("select * from city where id=#{id}") 21 | Optional selectOne(Long id); 22 | 23 | @MybatisQuery 24 | @Select("select * from city") 25 | List selectList(RowBounds rowBounds); 26 | 27 | default DataPage selectList(int offset, int limit){ 28 | List rows = selectList(new RowBounds(offset, limit)); 29 | return new DataPage<>(PaginationInterceptor.getTotalElements(), rows); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /huitool-spring-boot-autoconfigure/src/main/java/com/huitool/autoconfigure/MybatisAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.huitool.autoconfigure; 2 | 3 | import com.huitool.data.mybatis.plugins.pagination.PaginationInterceptor; 4 | import org.apache.ibatis.session.SqlSessionFactory; 5 | import org.mybatis.spring.SqlSessionFactoryBean; 6 | import org.mybatis.spring.SqlSessionTemplate; 7 | import org.springframework.boot.autoconfigure.AutoConfigureAfter; 8 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 9 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Configuration; 12 | 13 | /** 14 | * <描述信息> 15 | */ 16 | @Configuration 17 | @ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class }) 18 | @ConditionalOnBean(SqlSessionTemplate.class) 19 | @AutoConfigureAfter(org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration.class) 20 | public class MybatisAutoConfiguration { 21 | @Bean 22 | public PaginationInterceptor paginationInterceptor(){ 23 | return new PaginationInterceptor(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /huitool-core/src/main/java/com/huitool/data/mybatis/plugins/jdbc/expression/NotInExpression.java: -------------------------------------------------------------------------------- 1 | package com.huitool.data.mybatis.plugins.jdbc.expression; 2 | 3 | import com.huitool.data.mybatis.plugins.jdbc.SqlColumn; 4 | import org.springframework.util.StringUtils; 5 | 6 | import java.util.stream.IntStream; 7 | 8 | import static java.util.stream.Collectors.joining; 9 | 10 | public class NotInExpression extends InExpression { 11 | public NotInExpression(SqlColumn column, Object[] values) { 12 | super(column, values); 13 | } 14 | 15 | @Override 16 | public String render(String tableAliases, int index) { 17 | String name = StringUtils.hasText(tableAliases) ? String.format("%s.%s", tableAliases, column.name()) : column.name(); 18 | return '(' + IntStream.range(0, values.length) 19 | .mapToObj(i -> { 20 | if(column.jdbcType() == null){ 21 | return String.format("%s%s#{arg1.p%d[%d]}", name, "<>", index, i, column.jdbcType()); 22 | }else{ 23 | return String.format("%s%s#{arg1.p%d[%d],jdbcType=%s}", name, "<>", index, i, column.jdbcType()); 24 | } 25 | }) 26 | .collect(joining(" and ")) + ')'; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /huitool-core/src/main/java/com/huitool/data/mybatis/plugins/jdbc/expression/SimpleExpression.java: -------------------------------------------------------------------------------- 1 | package com.huitool.data.mybatis.plugins.jdbc.expression; 2 | 3 | import com.huitool.data.mybatis.plugins.jdbc.SqlColumn; 4 | import org.springframework.util.StringUtils; 5 | 6 | public class SimpleExpression implements Expression { 7 | protected SqlColumn column; 8 | private String op; 9 | private Object value; 10 | 11 | public SimpleExpression(SqlColumn column, Object value, String op) { 12 | this.column = column; 13 | this.op = op; 14 | this.value = value; 15 | } 16 | 17 | @Override 18 | public Object getValue() { 19 | return this.value; 20 | } 21 | 22 | @Override 23 | public SqlColumn getColumn() { 24 | return this.column; 25 | } 26 | 27 | @Override 28 | public String render(String tableAliases, int index) { 29 | String name = StringUtils.hasText(tableAliases) ? String.format("%s.%s", tableAliases, column.name()) : column.name(); 30 | if(column.jdbcType() == null){ 31 | return String.format("%s%s#{arg1.p%d}", name, op, index, column.jdbcType()); 32 | }else{ 33 | return String.format("%s%s#{arg1.p%d,jdbcType=%s}", name, op, index, column.jdbcType()); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /huitool-core/src/test/java/com/huitool/JsonTests.java: -------------------------------------------------------------------------------- 1 | package com.huitool; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | 9 | import java.util.Date; 10 | 11 | /** 12 | * <描述信息> 13 | */ 14 | public class JsonTests { 15 | private ObjectMapper mapper; 16 | 17 | @Before 18 | public void setup(){ 19 | mapper = new ObjectMapper(); 20 | } 21 | 22 | @Test 23 | public void test() throws JsonProcessingException { 24 | Foo foo = new Foo(2.1234, new Date()); 25 | System.out.println(mapper.writeValueAsString(foo)); 26 | } 27 | 28 | @JsonFormat(pattern = "#.00", shape = JsonFormat.Shape.OBJECT) 29 | static class Foo{ 30 | @JsonFormat 31 | private Double value; 32 | @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") 33 | private Date creatTime; 34 | 35 | public Foo(Double value, Date creatTime) { 36 | this.value = value; 37 | this.creatTime = creatTime; 38 | } 39 | 40 | public Double getValue() { 41 | return value; 42 | } 43 | 44 | public Date getCreatTime() { 45 | return creatTime; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /huitool-spring-boot-starter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 4.0.0 7 | 8 | huitool-spring-boot-starter 9 | 10 | 11 | huitool-spring-boot 12 | com.huitool 13 | 0.1-SNAPSHOT 14 | 15 | 16 | 17 | 18 | com.huitool 19 | huitool-spring-boot-autoconfigure 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-web 25 | 26 | 27 | 28 | org.springframework.data 29 | spring-data-jdbc 30 | 31 | 32 | 33 | org.mybatis.spring.boot 34 | mybatis-spring-boot-starter 35 | 36 | 37 | -------------------------------------------------------------------------------- /huitool-core/src/main/java/org/springframework/data/jdbc/repository/support/MybatisJdbcRepositoryFactoryBean.java: -------------------------------------------------------------------------------- 1 | package org.springframework.data.jdbc.repository.support; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.context.ApplicationContext; 5 | import org.springframework.data.relational.core.mapping.RelationalMappingContext; 6 | import org.springframework.data.repository.core.support.RepositoryFactorySupport; 7 | 8 | /** 9 | * <描述信息> 10 | */ 11 | public class MybatisJdbcRepositoryFactoryBean extends JdbcRepositoryFactoryBean { 12 | private RelationalMappingContext mappingContext; 13 | private ApplicationContext context; 14 | 15 | 16 | public MybatisJdbcRepositoryFactoryBean(Class repositoryInterface) { 17 | super(repositoryInterface); 18 | } 19 | 20 | @Override 21 | protected RepositoryFactorySupport doCreateRepositoryFactory() { 22 | return new DelegatingJdbcRepositoryFactory((JdbcRepositoryFactory) super.doCreateRepositoryFactory(), context, mappingContext); 23 | } 24 | 25 | @Autowired 26 | protected void setMappingContext(RelationalMappingContext mappingContext) { 27 | super.setMappingContext(mappingContext); 28 | this.mappingContext = mappingContext; 29 | } 30 | 31 | @Autowired 32 | public void setContext(ApplicationContext context) { 33 | this.context = context; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /huitool-core/src/main/java/com/huitool/data/mybatis/plugins/jdbc/expression/BetweenExpression.java: -------------------------------------------------------------------------------- 1 | package com.huitool.data.mybatis.plugins.jdbc.expression; 2 | 3 | import com.huitool.data.mybatis.plugins.jdbc.SqlColumn; 4 | import org.springframework.util.StringUtils; 5 | 6 | public class BetweenExpression implements Expression { 7 | private SqlColumn column; 8 | private Object low; 9 | private Object high; 10 | 11 | public BetweenExpression(SqlColumn column, Object low, Object high) { 12 | this.column = column; 13 | this.low = low; 14 | this.high = high; 15 | } 16 | 17 | public Object getLow() { 18 | return low; 19 | } 20 | 21 | public Object getHigh() { 22 | return high; 23 | } 24 | 25 | @Override 26 | public SqlColumn getColumn() { 27 | return this.column; 28 | } 29 | 30 | @Override 31 | public String render(String tableAliases, int index) { 32 | String name = StringUtils.hasText(tableAliases) ? String.format("%s.%s", tableAliases, column.name()) : column.name(); 33 | if(column.jdbcType() == null){ 34 | return String.format("%s between #{arg1.p%d-low} and #{arg1.p%d-high}", name, index, index); 35 | }else{ 36 | return String.format("%s between #{arg1.p%d-low,jdbcType=%s} and #{arg1.p%d-high,jdbcType=%s}", name, index, column.jdbcType(), index, column.jdbcType()); 37 | } 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /huitool-core/src/main/java/com/huitool/data/mybatis/plugins/pagination/support/OracleDialect.java: -------------------------------------------------------------------------------- 1 | package com.huitool.data.mybatis.plugins.pagination.support; 2 | 3 | /** 4 | * <描述信息> 5 | */ 6 | public class OracleDialect extends AbstractSqlDialect { 7 | @Override 8 | public String getName() { 9 | return "mysql"; 10 | } 11 | 12 | @Override 13 | public String getLimitString(String sql, int offset, int limit) { 14 | return null; 15 | } 16 | 17 | private String getLimitString(final String sql, final int offset, 18 | final String offsetPlaceholder, final String limitPlaceholder) { 19 | String _sql = getLineSql(sql); 20 | StringBuilder pagingSelect = new StringBuilder(); 21 | if (offset >= 0) { 22 | pagingSelect.append("select * from ( select row_.*, rownum rownum_ from ( "); 23 | } else { 24 | pagingSelect.append("select * from ( "); 25 | } 26 | pagingSelect.append(_sql); 27 | if (offset >= 0) { 28 | String endString = offsetPlaceholder + "+" + limitPlaceholder; 29 | pagingSelect.append(" ) row_ ) where rownum_ <= ") 30 | .append(endString).append(" and rownum_ > ").append(offsetPlaceholder); 31 | } else { 32 | pagingSelect.append(" ) where rownum <= ").append(limitPlaceholder); 33 | } 34 | 35 | return pagingSelect.toString(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /huitool-core/src/main/java/com/huitool/data/mybatis/plugins/jdbc/expression/InExpression.java: -------------------------------------------------------------------------------- 1 | package com.huitool.data.mybatis.plugins.jdbc.expression; 2 | 3 | import com.huitool.data.mybatis.plugins.jdbc.SqlColumn; 4 | import org.springframework.util.StringUtils; 5 | 6 | import java.util.stream.IntStream; 7 | 8 | import static java.util.stream.Collectors.joining; 9 | 10 | public class InExpression implements Expression { 11 | protected SqlColumn column; 12 | protected Object[] values; 13 | 14 | public InExpression(SqlColumn column, Object[] values) { 15 | this.column = column; 16 | this.values = values; 17 | } 18 | 19 | @Override 20 | public Object[] getValues() { 21 | return values; 22 | } 23 | 24 | @Override 25 | public SqlColumn getColumn() { 26 | return this.column; 27 | } 28 | 29 | @Override 30 | public String render(String tableAliases, int index) { 31 | String name = StringUtils.hasText(tableAliases) ? String.format("%s.%s", tableAliases, column.name()) : column.name(); 32 | return '(' + IntStream.range(0, values.length) 33 | .mapToObj(i -> { 34 | if(column.jdbcType() == null){ 35 | return String.format("%s%s#{arg1.p%d[%d]}", name, "=", index, i, column.jdbcType()); 36 | }else{ 37 | return String.format("%s%s#{arg1.p%d[%d],jdbcType=%s}", name, "=", index, i, column.jdbcType()); 38 | } 39 | }) 40 | .collect(joining(" or ")) + ')'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /huitool-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 4.0.0 7 | 8 | 9 | huitool-spring-boot 10 | com.huitool 11 | 0.1-SNAPSHOT 12 | 13 | 14 | huitool-core 15 | 16 | 17 | 18 | org.projectlombok 19 | lombok 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-web 25 | true 26 | 27 | 28 | 29 | org.mybatis.spring.boot 30 | mybatis-spring-boot-starter 31 | true 32 | 33 | 34 | 35 | org.springframework.data 36 | spring-data-jdbc 37 | true 38 | 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-test 43 | test 44 | 45 | 46 | 47 | org.mybatis.spring.boot 48 | mybatis-spring-boot-starter-test 49 | test 50 | 51 | 52 | 53 | org.hsqldb 54 | hsqldb 55 | test 56 | 57 | 58 | -------------------------------------------------------------------------------- /huitool-spring-boot-autoconfigure/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 4.0.0 7 | 8 | huitool-spring-boot-autoconfigure 9 | 10 | 11 | huitool-spring-boot 12 | com.huitool 13 | 0.1-SNAPSHOT 14 | 15 | 16 | 17 | 18 | com.huitool 19 | huitool-core 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter 25 | compile 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-configuration-processor 31 | true 32 | 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-starter-web 37 | true 38 | 39 | 40 | 41 | org.springframework.data 42 | spring-data-jdbc 43 | true 44 | 45 | 46 | 47 | org.mybatis.spring.boot 48 | mybatis-spring-boot-starter 49 | true 50 | 51 | 52 | 53 | javax.servlet 54 | javax.servlet-api 55 | provided 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /huitool-core/src/main/java/org/springframework/data/jdbc/mybatis/support/MybatisQueryMethod.java: -------------------------------------------------------------------------------- 1 | package org.springframework.data.jdbc.mybatis.support; 2 | 3 | import org.apache.ibatis.session.SqlSession; 4 | import org.springframework.beans.BeansException; 5 | import org.springframework.context.ApplicationContext; 6 | import org.springframework.core.annotation.AnnotationUtils; 7 | import org.springframework.data.jdbc.repository.query.Modifying; 8 | import org.springframework.data.projection.ProjectionFactory; 9 | import org.springframework.data.repository.core.RepositoryMetadata; 10 | import org.springframework.data.repository.query.QueryMethod; 11 | 12 | import java.lang.reflect.InvocationTargetException; 13 | import java.lang.reflect.Method; 14 | 15 | /** 16 | * <描述信息> 17 | */ 18 | public class MybatisQueryMethod extends QueryMethod { 19 | private final Method method; 20 | private final SqlSession sqlSession; 21 | 22 | public MybatisQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory factory, ApplicationContext context) { 23 | super(method, metadata, factory); 24 | this.method = method; 25 | try{ 26 | this.sqlSession = context.getBean(SqlSession.class); 27 | }catch (BeansException e){ 28 | throw new IllegalStateException(String.format("You have annotated @MybatisQuery on method:%s ,but no org.mybatis.spring.SqlSessionTemplate provided.", method.getName())); 29 | } 30 | } 31 | 32 | /** 33 | * Returns whether the query method is a modifying one. 34 | * 35 | * @return if it's a modifying query, return {@code true}. 36 | */ 37 | @Override 38 | public boolean isModifyingQuery() { 39 | return AnnotationUtils.findAnnotation(method, Modifying.class) != null; 40 | } 41 | 42 | Object invoke(Object[] args) throws InvocationTargetException, IllegalAccessException { 43 | if(!this.sqlSession.getConfiguration().getMapperRegistry().hasMapper(method.getDeclaringClass())){ 44 | this.sqlSession.getConfiguration().addMapper(method.getDeclaringClass()); 45 | } 46 | Object mapper = this.sqlSession.getMapper(method.getDeclaringClass()); 47 | return method.invoke(mapper, args); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /huitool-core/src/main/java/org/springframework/data/jdbc/repository/support/DelegatingJdbcQueryLookupStrategy.java: -------------------------------------------------------------------------------- 1 | package org.springframework.data.jdbc.repository.support; 2 | 3 | import org.springframework.context.ApplicationContext; 4 | import org.springframework.data.jdbc.mybatis.support.MybatisQueryMethod; 5 | import org.springframework.data.jdbc.mybatis.support.MybatisRepositoryQuery; 6 | import org.springframework.data.jdbc.repository.query.MybatisQuery; 7 | import org.springframework.data.projection.ProjectionFactory; 8 | import org.springframework.data.relational.core.mapping.RelationalMappingContext; 9 | import org.springframework.data.repository.core.NamedQueries; 10 | import org.springframework.data.repository.core.RepositoryMetadata; 11 | import org.springframework.data.repository.query.QueryLookupStrategy; 12 | import org.springframework.data.repository.query.RepositoryQuery; 13 | 14 | import java.lang.reflect.Method; 15 | 16 | /** 17 | * <描述信息> 18 | */ 19 | public class DelegatingJdbcQueryLookupStrategy implements QueryLookupStrategy { 20 | private QueryLookupStrategy delegate; 21 | private ApplicationContext context; 22 | private RelationalMappingContext mappingContext; 23 | 24 | public DelegatingJdbcQueryLookupStrategy(QueryLookupStrategy delegate, ApplicationContext context, RelationalMappingContext mappingContext) { 25 | this.delegate = delegate; 26 | this.context = context; 27 | this.mappingContext = mappingContext; 28 | } 29 | 30 | @Override 31 | public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, ProjectionFactory factory, NamedQueries namedQueries) { 32 | if(method.isAnnotationPresent(MybatisQuery.class)){ 33 | return createMybatisRepositoryQuery(method, metadata, factory); 34 | }else{ 35 | return delegate.resolveQuery(method, metadata, factory, namedQueries); 36 | } 37 | } 38 | 39 | private RepositoryQuery createMybatisRepositoryQuery(Method method, RepositoryMetadata metadata, ProjectionFactory projectionFactory) { 40 | MybatisQueryMethod mybatisQueryMethod = new MybatisQueryMethod(method, metadata, projectionFactory, context); 41 | return new MybatisRepositoryQuery(context, mappingContext, mybatisQueryMethod); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /huitool-core/src/test/java/com/huitool/data/SpringDataJdbcTests.java: -------------------------------------------------------------------------------- 1 | package com.huitool.data; 2 | 3 | import com.huitool.data.mybatis.plugins.pagination.DataPage; 4 | import com.huitool.data.mybatis.plugins.pagination.PaginationInterceptor; 5 | import org.apache.ibatis.session.RowBounds; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.mybatis.spring.boot.test.autoconfigure.AutoConfigureMybatis; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.SpringBootConfiguration; 12 | import org.springframework.boot.test.autoconfigure.data.jdbc.DataJdbcTest; 13 | import org.springframework.context.annotation.Bean; 14 | import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories; 15 | import org.springframework.data.jdbc.repository.support.MybatisJdbcRepositoryFactoryBean; 16 | import org.springframework.test.context.junit4.SpringRunner; 17 | 18 | import java.util.Optional; 19 | 20 | /** 21 | * <描述信息> 22 | */ 23 | @RunWith(SpringRunner.class) 24 | @AutoConfigureMybatis 25 | @DataJdbcTest(properties = {"logging.level.com.huitool=debug"}) 26 | public class SpringDataJdbcTests { 27 | @Autowired 28 | private CityRepository cityRepository; 29 | 30 | @EnableJdbcRepositories(basePackages = "com.huitool.data", repositoryFactoryBeanClass = MybatisJdbcRepositoryFactoryBean.class) 31 | @SpringBootConfiguration 32 | public static class DataJdbcConfig{ 33 | @Bean 34 | public PaginationInterceptor paginationInterceptor(){ 35 | return new PaginationInterceptor(); 36 | } 37 | } 38 | 39 | @Test 40 | public void contextLoads() { 41 | } 42 | 43 | @Test 44 | public void testDataJdbc(){ 45 | Optional optional = cityRepository.findById(1L); 46 | Assert.assertEquals(1L, optional.get().getId().longValue()); 47 | } 48 | 49 | @Test 50 | public void testMybatisMapper(){ 51 | Optional optional1 = cityRepository.findById(1L); 52 | Optional optional2 = cityRepository.selectOne(1L); 53 | Assert.assertEquals(optional1.get(), optional2.get()); 54 | } 55 | 56 | @Test 57 | public void testSelectPaging(){ 58 | DataPage page = cityRepository.selectList(0, 5); 59 | Assert.assertEquals(5, page.getRows().size()); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /huitool-core/src/main/java/org/springframework/data/jdbc/repository/support/DelegatingJdbcRepositoryFactory.java: -------------------------------------------------------------------------------- 1 | package org.springframework.data.jdbc.repository.support; 2 | 3 | import org.springframework.context.ApplicationContext; 4 | import org.springframework.data.relational.core.mapping.RelationalMappingContext; 5 | import org.springframework.data.repository.core.EntityInformation; 6 | import org.springframework.data.repository.core.RepositoryInformation; 7 | import org.springframework.data.repository.core.RepositoryMetadata; 8 | import org.springframework.data.repository.core.support.RepositoryFactorySupport; 9 | import org.springframework.data.repository.query.QueryLookupStrategy; 10 | import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; 11 | import org.springframework.lang.Nullable; 12 | 13 | import java.util.Optional; 14 | 15 | /** 16 | * <描述信息> 17 | */ 18 | public class DelegatingJdbcRepositoryFactory extends RepositoryFactorySupport { 19 | private JdbcRepositoryFactory delegate; 20 | private ApplicationContext context; 21 | private RelationalMappingContext mappingContext; 22 | 23 | public DelegatingJdbcRepositoryFactory(JdbcRepositoryFactory delegate, ApplicationContext context, RelationalMappingContext mappingContext) { 24 | this.delegate = delegate; 25 | this.context = context; 26 | this.mappingContext = mappingContext; 27 | } 28 | 29 | @Override 30 | public EntityInformation getEntityInformation(Class domainClass) { 31 | return delegate.getEntityInformation(domainClass); 32 | } 33 | 34 | @Override 35 | protected Object getTargetRepository(RepositoryInformation metadata) { 36 | return delegate.getTargetRepository(metadata); 37 | } 38 | 39 | @Override 40 | protected Class getRepositoryBaseClass(RepositoryMetadata metadata) { 41 | return delegate.getRepositoryBaseClass(metadata); 42 | } 43 | 44 | @Override 45 | protected Optional getQueryLookupStrategy(@Nullable QueryLookupStrategy.Key key, 46 | QueryMethodEvaluationContextProvider evaluationContextProvider) { 47 | Optional optional = delegate.getQueryLookupStrategy(key, evaluationContextProvider); 48 | 49 | return Optional.of(new DelegatingJdbcQueryLookupStrategy(optional.get(), context, mappingContext)); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /huitool-core/src/main/java/com/huitool/util/LambdaUtils.java: -------------------------------------------------------------------------------- 1 | package com.huitool.util; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.util.ClassUtils; 6 | import org.springframework.util.ReflectionUtils; 7 | 8 | import java.io.Serializable; 9 | import java.lang.invoke.SerializedLambda; 10 | import java.lang.reflect.Method; 11 | import java.util.Map; 12 | import java.util.concurrent.ConcurrentHashMap; 13 | import java.util.regex.Matcher; 14 | import java.util.regex.Pattern; 15 | 16 | /** 17 | * <描述信息> 18 | */ 19 | public class LambdaUtils { 20 | private static final Logger logger = LoggerFactory.getLogger(LambdaUtils.class); 21 | private static final Pattern INSTANTIATED_METHOD_TYPE = Pattern.compile("\\(L(?[\\S&&[^;)]]+);\\)L[\\S]+;"); 22 | private static final Map, SerializedLambda> CLASS_LAMBDA_CACHE = new ConcurrentHashMap<>(); 23 | private static final Map, Class> LAMBDA_IMPLCLASS_CACHE = new ConcurrentHashMap<>(); 24 | 25 | public static String getImplMethodName(Serializable fn){ 26 | return resolve(fn).getImplMethodName(); 27 | } 28 | 29 | public static Class getImplClass(Serializable fn){ 30 | Class klass = LAMBDA_IMPLCLASS_CACHE.get(fn.getClass()); 31 | 32 | if(klass == null){ 33 | SerializedLambda lambda = resolve(fn); 34 | logger.debug("{}", lambda); 35 | Matcher matcher = INSTANTIATED_METHOD_TYPE.matcher(lambda.getInstantiatedMethodType()); 36 | if(matcher.find()){ 37 | logger.debug("instantiatedMethodType: {}", matcher.group("instantiatedMethodType").replaceAll("/", ".")); 38 | klass = ClassUtils.resolveClassName(matcher.group("instantiatedMethodType").replaceAll("/", "."), null); 39 | LAMBDA_IMPLCLASS_CACHE.put(fn.getClass(), klass); 40 | } 41 | } 42 | 43 | return klass; 44 | } 45 | 46 | private static SerializedLambda resolve(Serializable fn){ 47 | SerializedLambda lambda = CLASS_LAMBDA_CACHE.get(fn.getClass()); 48 | 49 | if(lambda == null){ 50 | Method method = ReflectionUtils.findMethod(fn.getClass(), "writeReplace"); 51 | method.setAccessible(true); 52 | lambda = (SerializedLambda) ReflectionUtils.invokeMethod(method, fn); 53 | CLASS_LAMBDA_CACHE.put(fn.getClass(), lambda); 54 | } 55 | 56 | return lambda; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /huitool-core/src/main/java/org/springframework/data/jdbc/mybatis/support/MybatisRepositoryQuery.java: -------------------------------------------------------------------------------- 1 | package org.springframework.data.jdbc.mybatis.support; 2 | 3 | import org.springframework.context.ApplicationEventPublisher; 4 | import org.springframework.data.relational.core.mapping.RelationalMappingContext; 5 | import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; 6 | import org.springframework.data.relational.core.mapping.event.AfterLoadEvent; 7 | import org.springframework.data.relational.core.mapping.event.Identifier; 8 | import org.springframework.data.repository.query.QueryMethod; 9 | import org.springframework.data.repository.query.RepositoryQuery; 10 | import org.springframework.lang.Nullable; 11 | 12 | import java.lang.reflect.InvocationTargetException; 13 | 14 | /** 15 | * <描述信息> 16 | */ 17 | public class MybatisRepositoryQuery implements RepositoryQuery { 18 | private final ApplicationEventPublisher publisher; 19 | private final RelationalMappingContext mappingContext; 20 | private final MybatisQueryMethod queryMethod; 21 | 22 | public MybatisRepositoryQuery(ApplicationEventPublisher publisher, RelationalMappingContext mappingContext, MybatisQueryMethod queryMethod) { 23 | this.publisher = publisher; 24 | this.mappingContext = mappingContext; 25 | this.queryMethod = queryMethod; 26 | } 27 | 28 | @Override 29 | public Object execute(Object[] parameters) { 30 | try { 31 | Object retVal = queryMethod.invoke(parameters); 32 | 33 | if(!queryMethod.isModifyingQuery()){ 34 | publishAfterLoad(retVal); 35 | } 36 | 37 | return retVal; 38 | } catch (InvocationTargetException e) { 39 | throw new RuntimeException(e.getTargetException()); 40 | } catch (IllegalAccessException e) { 41 | throw new RuntimeException("invoke from mybatis mapper failed.", e); 42 | } 43 | } 44 | 45 | @Override 46 | public QueryMethod getQueryMethod() { 47 | return this.queryMethod; 48 | } 49 | 50 | private void publishAfterLoad(@Nullable T entity) { 51 | 52 | if (entity != null && mappingContext.hasPersistentEntityFor(entity.getClass())) { 53 | 54 | RelationalPersistentEntity e = mappingContext.getRequiredPersistentEntity(entity.getClass()); 55 | Object identifier = e.getIdentifierAccessor(entity) 56 | .getIdentifier(); 57 | 58 | if (identifier != null) { 59 | publisher.publishEvent(new AfterLoadEvent(Identifier.of(identifier), entity)); 60 | } 61 | } 62 | 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | org.springframework.boot 9 | spring-boot-starter-parent 10 | 2.1.7.RELEASE 11 | 12 | 13 | 14 | com.huitool 15 | huitool-spring-boot 16 | 0.1-SNAPSHOT 17 | pom 18 | 19 | 20 | huitool-core 21 | huitool-spring-boot-autoconfigure 22 | huitool-spring-boot-starter 23 | 24 | 25 | 26 | 0.1-SNAPSHOT 27 | 2.1.0 28 | 29 | 30 | 31 | 32 | 33 | org.springframework.data 34 | spring-data-releasetrain 35 | Lovelace-SR10 36 | import 37 | pom 38 | 39 | 40 | 41 | org.mybatis.spring.boot 42 | mybatis-spring-boot 43 | ${mybatis-spring-boot.version} 44 | import 45 | pom 46 | 47 | 48 | 49 | com.huitool 50 | huitool-core 51 | ${huitool.version} 52 | 53 | 54 | 55 | com.huitool 56 | huitool-spring-boot-autoconfigure 57 | ${huitool.version} 58 | 59 | 60 | 61 | com.huitool 62 | huitool-spring-boot-starter 63 | ${huitool.version} 64 | 65 | 66 | 67 | 68 | 69 | 70 | aliyun 71 | http://maven.aliyun.com/nexus/content/groups/public/ 72 | 73 | 74 | 75 | central 76 | libs-release 77 | https://repo.spring.io/libs-release 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /huitool-spring-boot-autoconfigure/src/main/java/com/huitool/autoconfigure/JdbcRepositoryAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.huitool.autoconfigure; 2 | 3 | import org.springframework.boot.autoconfigure.AutoConfigureAfter; 4 | import org.springframework.boot.autoconfigure.AutoConfigureBefore; 5 | import org.springframework.boot.autoconfigure.AutoConfigureOrder; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 8 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 9 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 10 | import org.springframework.boot.autoconfigure.data.AbstractRepositoryConfigurationSourceSupport; 11 | import org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration; 12 | import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration; 13 | import org.springframework.context.annotation.Bean; 14 | import org.springframework.context.annotation.Configuration; 15 | import org.springframework.context.annotation.Import; 16 | import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories; 17 | import org.springframework.data.jdbc.repository.config.JdbcConfiguration; 18 | import org.springframework.data.jdbc.repository.config.JdbcRepositoryConfigExtension; 19 | import org.springframework.data.jdbc.repository.support.MybatisJdbcRepositoryFactoryBean; 20 | import org.springframework.data.repository.config.RepositoryConfigurationExtension; 21 | import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; 22 | 23 | import java.lang.annotation.Annotation; 24 | 25 | /** 26 | * <描述信息> 27 | */ 28 | @Configuration 29 | @ConditionalOnBean(NamedParameterJdbcOperations.class) 30 | @ConditionalOnClass({ NamedParameterJdbcOperations.class, JdbcConfiguration.class }) 31 | @AutoConfigureAfter(JdbcTemplateAutoConfiguration.class) 32 | @AutoConfigureBefore(JdbcRepositoriesAutoConfiguration.class) 33 | public class JdbcRepositoryAutoConfiguration { 34 | @Bean 35 | public JdbcRepositoryConfigExtension jdbcRepositoryConfigExtension(){ 36 | return new JdbcRepositoryConfigExtension(); 37 | } 38 | 39 | 40 | @Configuration 41 | // @ConditionalOnMissingBean(JdbcRepositoryConfigExtension.class) 42 | @Import(JdbcRepositoriesAutoConfigureRegistrar.class) 43 | static class JdbcRepositoriesConfiguration { 44 | 45 | } 46 | 47 | static class JdbcRepositoriesAutoConfigureRegistrar extends AbstractRepositoryConfigurationSourceSupport { 48 | @Override 49 | protected Class getAnnotation() { 50 | return EnableJdbcRepositories.class; 51 | } 52 | 53 | @Override 54 | protected Class getConfiguration() { 55 | return EnableJdbcRepositoriesConfiguration.class; 56 | } 57 | 58 | @Override 59 | protected RepositoryConfigurationExtension getRepositoryConfigurationExtension() { 60 | return new JdbcRepositoryConfigExtension(); 61 | } 62 | } 63 | 64 | @EnableJdbcRepositories(repositoryFactoryBeanClass = MybatisJdbcRepositoryFactoryBean.class) 65 | static class EnableJdbcRepositoriesConfiguration{ 66 | 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /huitool-core/src/main/java/com/huitool/data/mybatis/plugins/pagination/support/AbstractSqlDialect.java: -------------------------------------------------------------------------------- 1 | package com.huitool.data.mybatis.plugins.pagination.support; 2 | 3 | import com.huitool.data.mybatis.plugins.pagination.SqlDialect; 4 | import org.apache.ibatis.mapping.BoundSql; 5 | import org.apache.ibatis.mapping.MappedStatement; 6 | import org.apache.ibatis.scripting.defaults.DefaultParameterHandler; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.sql.Connection; 11 | import java.sql.PreparedStatement; 12 | import java.sql.ResultSet; 13 | import java.sql.SQLException; 14 | import java.util.regex.Matcher; 15 | import java.util.regex.Pattern; 16 | 17 | /** 18 | * <描述信息> 19 | */ 20 | public abstract class AbstractSqlDialect implements SqlDialect { 21 | private static final Logger logger = LoggerFactory.getLogger(AbstractSqlDialect.class); 22 | private String ORDER_BY_KEYWORD = "order\\s+by"; 23 | private String ORDER_BY_ELEMENT_REGEX = "order\\s+by[\\w|\\W|\\s|\\S]*"; 24 | private String OVER_ORDER_BY_REGEX = "over\\s+\\([\\w|\\W|\\s|\\S]*order\\s+by[\\w|\\W|\\s|\\S]*\\)"; 25 | 26 | public int getCount(MappedStatement ms, Connection connection, Object parameterObject) throws SQLException { 27 | BoundSql boundSql = ms.getBoundSql(parameterObject); 28 | 29 | String sql = boundSql.getSql().trim(); 30 | String countSql = getCountString(sql); 31 | 32 | logger.debug("Count SQL [{}]", countSql); 33 | logger.debug("Parameters: {} ", parameterObject); 34 | 35 | try(PreparedStatement stmt = connection.prepareStatement(countSql)){ 36 | DefaultParameterHandler handler = new DefaultParameterHandler(ms, parameterObject, boundSql); 37 | handler.setParameters(stmt); 38 | ResultSet rs = stmt.executeQuery(); 39 | 40 | int count = 0; 41 | if (rs.next()) { 42 | count = rs.getInt(1); 43 | } 44 | return count; 45 | } 46 | } 47 | 48 | protected String getCountString(String sql) { 49 | String newsql; 50 | if (containOrder(sql) && !containOver(sql)) { 51 | newsql = removeOrders(sql); 52 | } else { 53 | newsql = sql; 54 | } 55 | 56 | return "select count(1) from (" + newsql.trim() + ") tmp_count"; 57 | } 58 | 59 | protected String getLineSql(String sql) { 60 | return sql.replaceAll("[\r\n]", " ").replaceAll("\\s{2,}", " "); 61 | } 62 | 63 | private boolean containOrder(String sql) { 64 | return containRegex(sql, ORDER_BY_KEYWORD); 65 | } 66 | 67 | private boolean containOver(String sql) { 68 | return containRegex(sql, OVER_ORDER_BY_REGEX); 69 | } 70 | 71 | private boolean containRegex(String sql, String regex) { 72 | Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); 73 | Matcher matcher = pattern.matcher(sql); 74 | return matcher.find(); 75 | } 76 | 77 | private String removeOrders(String sql) { 78 | Pattern p = Pattern.compile(ORDER_BY_ELEMENT_REGEX, Pattern.CASE_INSENSITIVE); 79 | Matcher m = p.matcher(sql); 80 | StringBuffer sb = new StringBuffer(); 81 | while (m.find()) { 82 | m.appendReplacement(sb, ""); 83 | } 84 | m.appendTail(sb); 85 | return sb.toString(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /huitool-core/src/main/java/com/huitool/data/mybatis/plugins/jdbc/expression/Expressions.java: -------------------------------------------------------------------------------- 1 | package com.huitool.data.mybatis.plugins.jdbc.expression; 2 | 3 | import com.huitool.data.mybatis.plugins.jdbc.SqlColumn; 4 | 5 | import java.util.Collection; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.stream.IntStream; 10 | 11 | public final class Expressions { 12 | public static SimpleExpression eq(SqlColumn column, Object value) { 13 | return new SimpleExpression(column, value, "="); 14 | } 15 | 16 | public static SimpleExpression ne(SqlColumn column, Object value) { 17 | return new SimpleExpression(column, value, "<>"); 18 | } 19 | 20 | public static SimpleExpression like(SqlColumn column, Object value) { 21 | return new SimpleExpression(column, value, " like "); 22 | } 23 | 24 | public static SimpleExpression notLike(SqlColumn column, Object value) { 25 | return new SimpleExpression(column, value, " not like "); 26 | } 27 | 28 | public static SimpleExpression gt(SqlColumn column, Object value) { 29 | return new SimpleExpression(column, value, ">"); 30 | } 31 | 32 | public static SimpleExpression lt(SqlColumn column, Object value) { 33 | return new SimpleExpression(column, value, "<"); 34 | } 35 | 36 | public static SimpleExpression le(SqlColumn column, Object value) { 37 | return new SimpleExpression(column, value, "<="); 38 | } 39 | 40 | public static SimpleExpression ge(SqlColumn column, Object value) { 41 | return new SimpleExpression(column, value, ">="); 42 | } 43 | 44 | public static BetweenExpression between(SqlColumn column, Object low, Object high) { 45 | return new BetweenExpression(column, low, high); 46 | } 47 | 48 | public static InExpression in(SqlColumn column, Object... values) { 49 | return new InExpression(column, values); 50 | } 51 | 52 | public static InExpression in(SqlColumn column, Collection values) { 53 | return in(column, values.toArray()); 54 | } 55 | 56 | public static NotInExpression notIn(SqlColumn column, Object... values) { 57 | return new NotInExpression(column, values); 58 | } 59 | 60 | public static NotInExpression notIn(SqlColumn column, Collection values) { 61 | return notIn(column, values.toArray()); 62 | } 63 | 64 | public static NullExpression isNull(SqlColumn column) { 65 | return new NullExpression(column); 66 | } 67 | 68 | public static NotNullExpression isNotNull(SqlColumn column) { 69 | return new NotNullExpression(column); 70 | } 71 | 72 | public static Map fetchQueryParameter(List list) { 73 | Map parameter = new HashMap<>(); 74 | IntStream.range(0, list.size()) 75 | .forEach(idx -> { 76 | Expression expression = list.get(idx); 77 | if (expression instanceof SimpleExpression) { 78 | parameter.put("p" + idx, expression.getValue()); 79 | } 80 | 81 | if (expression instanceof BetweenExpression) { 82 | parameter.put("p" + idx + "-low", ((BetweenExpression) expression).getLow()); 83 | parameter.put("p" + idx + "-high", ((BetweenExpression) expression).getHigh()); 84 | } 85 | 86 | if (expression instanceof InExpression) { 87 | parameter.put("p" + idx, expression.getValues()); 88 | } 89 | }); 90 | return parameter; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /huitool-core/src/main/java/com/huitool/web/ServletExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.huitool.web; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.dao.DataAccessException; 6 | import org.springframework.http.HttpHeaders; 7 | import org.springframework.http.HttpStatus; 8 | import org.springframework.http.MediaType; 9 | import org.springframework.http.ResponseEntity; 10 | import org.springframework.http.converter.HttpMessageNotReadableException; 11 | import org.springframework.http.converter.HttpMessageNotWritableException; 12 | import org.springframework.util.StringUtils; 13 | import org.springframework.validation.BindException; 14 | import org.springframework.web.bind.MethodArgumentNotValidException; 15 | import org.springframework.web.bind.annotation.ControllerAdvice; 16 | import org.springframework.web.bind.annotation.ExceptionHandler; 17 | import org.springframework.web.context.request.WebRequest; 18 | import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; 19 | 20 | import java.sql.SQLException; 21 | import java.util.List; 22 | import java.util.stream.Collectors; 23 | 24 | /** 25 | * <描述信息> 26 | */ 27 | @ControllerAdvice 28 | public class ServletExceptionHandler extends ResponseEntityExceptionHandler { 29 | private static final Logger logger = LoggerFactory.getLogger(ServletExceptionHandler.class); 30 | 31 | @Override 32 | protected ResponseEntity handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { 33 | return handleExceptionInternal(ex, "数据解析失败", headers, status, request); 34 | } 35 | 36 | @Override 37 | protected ResponseEntity handleHttpMessageNotWritable(HttpMessageNotWritableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { 38 | return handleExceptionInternal(ex, "服务不可用", headers, status, request); 39 | } 40 | 41 | @Override 42 | protected ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { 43 | return handleExceptionInternal(ex, "数据验证失败", headers, status, request); 44 | } 45 | 46 | @Override 47 | protected ResponseEntity handleBindException(BindException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { 48 | Object body = null; 49 | if (ex.getBindingResult().hasErrors()) { 50 | List messages = ex.getBindingResult().getFieldErrors().stream() 51 | .map(fieldError -> new FormErrorMessage(fieldError.getField(), fieldError.getDefaultMessage())) 52 | .collect(Collectors.toList()); 53 | 54 | if (StringUtils.hasText(request.getHeader(HttpHeaders.ACCEPT)) 55 | && request.getHeader(HttpHeaders.ACCEPT).equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE)) { 56 | body = messages; 57 | } else { 58 | List list = messages.stream() 59 | .map(message -> message.getField() + ':' + message.getMessage()) 60 | .collect(Collectors.toList()); 61 | body = StringUtils.collectionToCommaDelimitedString(list); 62 | } 63 | } 64 | 65 | return handleExceptionInternal(ex, body, headers, status, request); 66 | } 67 | 68 | @Override 69 | protected ResponseEntity handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) { 70 | logger.error("{}", status, ex); 71 | return super.handleExceptionInternal(ex, body, headers, status, request); 72 | } 73 | 74 | @ExceptionHandler({SQLException.class, DataAccessException.class}) 75 | public ResponseEntity handleDataAccess(Exception ex, WebRequest request){ 76 | return handleExceptionInternal(ex, "数据操作异常", new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR, request); 77 | } 78 | 79 | static class FormErrorMessage{ 80 | private String field; 81 | private String message; 82 | 83 | public FormErrorMessage(String field, String message) { 84 | this.field = field; 85 | this.message = message; 86 | } 87 | 88 | public String getField() { 89 | return field; 90 | } 91 | 92 | public String getMessage() { 93 | return message; 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /huitool-core/src/main/java/com/huitool/data/mybatis/plugins/jdbc/SqlColumn.java: -------------------------------------------------------------------------------- 1 | package com.huitool.data.mybatis.plugins.jdbc; 2 | 3 | import com.huitool.data.mybatis.plugins.jdbc.expression.BetweenExpression; 4 | import com.huitool.data.mybatis.plugins.jdbc.expression.Expressions; 5 | import com.huitool.data.mybatis.plugins.jdbc.expression.InExpression; 6 | import com.huitool.data.mybatis.plugins.jdbc.expression.NotInExpression; 7 | import com.huitool.data.mybatis.plugins.jdbc.expression.NotNullExpression; 8 | import com.huitool.data.mybatis.plugins.jdbc.expression.NullExpression; 9 | import com.huitool.data.mybatis.plugins.jdbc.expression.SimpleExpression; 10 | import com.huitool.util.Functions; 11 | import com.huitool.util.LambdaUtils; 12 | import lombok.Data; 13 | import lombok.experimental.Accessors; 14 | import org.springframework.data.annotation.Transient; 15 | import org.springframework.util.ReflectionUtils; 16 | import org.springframework.util.StringUtils; 17 | 18 | import java.beans.Introspector; 19 | import java.lang.reflect.Field; 20 | import java.sql.JDBCType; 21 | import java.util.Collection; 22 | 23 | /** 24 | * <描述信息> 25 | */ 26 | @Data 27 | @Accessors(fluent = true) 28 | public class SqlColumn { 29 | private String name; 30 | private Class domainType; 31 | private Field property; 32 | private JDBCType jdbcType; 33 | private String alias; 34 | private Boolean descending; 35 | 36 | public SqlColumn(Class domainType, Field property) { 37 | this.domainType = domainType; 38 | this.property = property; 39 | 40 | if(property.isAnnotationPresent(Column.class)){ 41 | this.name = property.getAnnotation(Column.class).value(); 42 | }else{ 43 | this.name = property.getName(); 44 | } 45 | } 46 | 47 | public static SqlColumn of(Functions.SerializableFunction fn){ 48 | return new SqlColumn(LambdaUtils.getImplClass(fn), getProperty(fn)); 49 | } 50 | 51 | public boolean isPersistable(){ 52 | return !(property.isAnnotationPresent(Transient.class) || property.isAnnotationPresent(java.beans.Transient.class)); 53 | } 54 | 55 | @Override 56 | public String toString() { 57 | return String.format("%s as %s", this.name, StringUtils.hasText(this.alias) ? this.alias : this.name); 58 | // return StringUtils.hasText(this.alias) ? String.format("%s as %s", this.name, this.alias) : this.name; 59 | } 60 | 61 | public SimpleExpression eq(Object value){ 62 | return Expressions.eq(this, value); 63 | } 64 | 65 | public SimpleExpression ne(Object value){ 66 | return Expressions.ne(this, value); 67 | } 68 | 69 | public SimpleExpression like(Object value){ 70 | return Expressions.like(this, value); 71 | } 72 | 73 | public SimpleExpression notLike(Object value){ 74 | return Expressions.notLike(this, value); 75 | } 76 | 77 | public SimpleExpression gt(Object value){ 78 | return Expressions.gt(this, value); 79 | } 80 | 81 | public SimpleExpression lt(Object value){ 82 | return Expressions.lt(this, value); 83 | } 84 | 85 | public SimpleExpression le(Object value){ 86 | return Expressions.le(this, value); 87 | } 88 | 89 | public SimpleExpression ge(Object value){ 90 | return Expressions.ge(this, value); 91 | } 92 | 93 | public BetweenExpression between(Object low, Object high){ 94 | return Expressions.between(this, low, high); 95 | } 96 | 97 | public InExpression in(Object... values){ 98 | return Expressions.in(this, values); 99 | } 100 | 101 | public InExpression in(Collection values){ 102 | return Expressions.in(this, values); 103 | } 104 | 105 | public NotInExpression notIn(Object... values){ 106 | return Expressions.notIn(this, values); 107 | } 108 | 109 | public NotInExpression notIn(Collection values){ 110 | return Expressions.notIn(this, values); 111 | } 112 | 113 | public NullExpression isNull(){ 114 | return Expressions.isNull(this); 115 | } 116 | 117 | public NotNullExpression isNotNull(){ 118 | return Expressions.isNotNull(this); 119 | } 120 | 121 | private static Field getProperty(Functions.SerializableFunction fn){ 122 | String methodName = LambdaUtils.getImplMethodName(fn); 123 | String prefix = null; 124 | 125 | if(methodName.startsWith("get")){ 126 | prefix = "get"; 127 | }else if(methodName.startsWith("is")){ 128 | prefix = "is"; 129 | } 130 | 131 | if(prefix == null){ 132 | throw new RuntimeException("无效的getter方法: "+methodName); 133 | } 134 | 135 | String fieldName = methodName.substring(prefix.length()); 136 | return ReflectionUtils.findField(LambdaUtils.getImplClass(fn), Introspector.decapitalize(fieldName)); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /huitool-core/src/main/java/com/huitool/data/mybatis/plugins/pagination/PaginationInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.huitool.data.mybatis.plugins.pagination; 2 | 3 | import com.huitool.data.mybatis.plugins.pagination.support.HSQLDBDialect; 4 | import com.huitool.data.mybatis.plugins.pagination.support.MySQLDialect; 5 | import com.huitool.data.mybatis.plugins.pagination.support.OracleDialect; 6 | import com.huitool.data.mybatis.plugins.pagination.support.PostgreSQLDialect; 7 | import org.apache.ibatis.executor.statement.StatementHandler; 8 | import org.apache.ibatis.mapping.BoundSql; 9 | import org.apache.ibatis.mapping.MappedStatement; 10 | import org.apache.ibatis.mapping.SqlCommandType; 11 | import org.apache.ibatis.plugin.Interceptor; 12 | import org.apache.ibatis.plugin.Intercepts; 13 | import org.apache.ibatis.plugin.Invocation; 14 | import org.apache.ibatis.plugin.Plugin; 15 | import org.apache.ibatis.plugin.Signature; 16 | import org.apache.ibatis.reflection.MetaObject; 17 | import org.apache.ibatis.reflection.SystemMetaObject; 18 | import org.apache.ibatis.session.RowBounds; 19 | import org.slf4j.Logger; 20 | import org.slf4j.LoggerFactory; 21 | 22 | import java.sql.Connection; 23 | import java.util.Arrays; 24 | import java.util.List; 25 | import java.util.Locale; 26 | import java.util.Properties; 27 | 28 | /** 29 | * <描述信息> 30 | */ 31 | @Intercepts({ 32 | @Signature( 33 | type = StatementHandler.class, 34 | method = "prepare", 35 | args = {Connection.class, Integer.class} 36 | ) 37 | }) 38 | public class PaginationInterceptor implements Interceptor { 39 | private static final Logger logger = LoggerFactory.getLogger(PaginationInterceptor.class); 40 | private static final ThreadLocal ELEMENTS_TOTAL = new ThreadLocal() { 41 | @Override 42 | protected Integer initialValue() { 43 | return 0; 44 | } 45 | }; 46 | 47 | private final List sqlDialectList; 48 | 49 | public PaginationInterceptor(SqlDialect... sqlDialects) { 50 | this.sqlDialectList = defaultSqlDialects(); 51 | 52 | if(sqlDialects.length > 0){ 53 | this.sqlDialectList.addAll(Arrays.asList(sqlDialects)); 54 | } 55 | } 56 | 57 | public static long getTotalElements() { 58 | long total = ELEMENTS_TOTAL.get(); 59 | ELEMENTS_TOTAL.remove(); 60 | return total; 61 | } 62 | 63 | @Override 64 | public Object intercept(Invocation invocation) throws Throwable { 65 | Connection connection = (Connection) invocation.getArgs()[0]; 66 | StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); 67 | MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler); 68 | RowBounds rowBounds = (RowBounds) metaStatementHandler.getValue("delegate.rowBounds"); 69 | MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement"); 70 | 71 | if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) { 72 | return invocation.proceed(); 73 | } 74 | 75 | if (rowBounds == null || rowBounds == RowBounds.DEFAULT) { 76 | return invocation.proceed(); 77 | } 78 | 79 | int offset = rowBounds.getOffset(); 80 | int limit = rowBounds.getLimit(); 81 | 82 | if(offset != RowBounds.NO_ROW_OFFSET || limit != RowBounds.NO_ROW_LIMIT){ 83 | BoundSql boundSql = statementHandler.getBoundSql(); 84 | Object parameterObject = boundSql.getParameterObject(); 85 | 86 | logger.debug("intercept sql: {}", boundSql.getSql()); 87 | logger.debug("intercept sql parameter: {}", parameterObject); 88 | 89 | SqlDialect dialect = getSqlDialect(connection.getMetaData().getURL()); 90 | int count = dialect.getCount(mappedStatement, connection, parameterObject); 91 | ELEMENTS_TOTAL.set(count); 92 | 93 | String originalSql = (String) metaStatementHandler.getValue("delegate.boundSql.sql"); 94 | metaStatementHandler.setValue("delegate.boundSql.sql", dialect.getLimitString(originalSql, offset, limit)); 95 | metaStatementHandler.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET); 96 | metaStatementHandler.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT); 97 | 98 | logger.debug("limit sql: {}", boundSql.getSql()); 99 | } 100 | 101 | return invocation.proceed(); 102 | } 103 | 104 | @Override 105 | public Object plugin(Object target) { 106 | return Plugin.wrap(target, this); 107 | } 108 | 109 | @Override 110 | public void setProperties(Properties properties) { 111 | 112 | } 113 | 114 | private SqlDialect getSqlDialect(String url){ 115 | String urlWithoutPrefix = url.substring("jdbc".length()).toLowerCase(Locale.ENGLISH); 116 | return sqlDialectList.stream() 117 | .filter(sqlDialect -> urlWithoutPrefix.startsWith(":" + sqlDialect.getName())) 118 | .findFirst() 119 | .orElseThrow(()->new RuntimeException("未找到有效的 SqlDialect")); 120 | } 121 | 122 | private List defaultSqlDialects(){ 123 | return Arrays.asList(new SqlDialect[]{new HSQLDBDialect(), new MySQLDialect(), new OracleDialect(), new PostgreSQLDialect()}); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /huitool-core/src/main/java/com/huitool/data/mybatis/plugins/jdbc/dml/SQLSelectClause.java: -------------------------------------------------------------------------------- 1 | package com.huitool.data.mybatis.plugins.jdbc.dml; 2 | 3 | import com.huitool.data.mybatis.plugins.jdbc.SqlColumn; 4 | import com.huitool.data.mybatis.plugins.jdbc.expression.Expression; 5 | import com.huitool.data.mybatis.plugins.jdbc.expression.Expressions; 6 | import com.huitool.util.Functions; 7 | import org.apache.ibatis.jdbc.SQL; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.data.annotation.Transient; 11 | import org.springframework.data.relational.core.mapping.Table; 12 | import org.springframework.data.util.ParsingUtils; 13 | import org.springframework.util.Assert; 14 | 15 | import java.util.ArrayList; 16 | import java.util.Arrays; 17 | import java.util.HashMap; 18 | import java.util.List; 19 | import java.util.Map; 20 | import java.util.stream.IntStream; 21 | 22 | public class SQLSelectClause { 23 | private static final Logger logger = LoggerFactory.getLogger(SQLSelectClause.class); 24 | 25 | private Map, String> tableAliases = new HashMap<>(); 26 | private List columnList = new ArrayList<>(); 27 | private List joinList = new ArrayList<>(); 28 | private List leftJoinList = new ArrayList<>(); 29 | private List rightJoinList = new ArrayList<>(); 30 | private List groupByList = new ArrayList<>(); 31 | private List orderByList = new ArrayList<>(); 32 | private List whereList = new ArrayList<>(); 33 | 34 | private boolean distinct; 35 | private Class domainType; 36 | 37 | public SQLSelectClause select(SqlColumn... sqlColumns){ 38 | this.columnList.addAll(Arrays.asList(sqlColumns)); 39 | return this; 40 | } 41 | 42 | public SQLSelectClause select(Functions.SerializableFunction... fns){ 43 | Arrays.stream(fns) 44 | .forEach(fn -> { 45 | SqlColumn sqlColumn = SqlColumn.of(fn); 46 | if(sqlColumn.isPersistable()){ 47 | this.columnList.add(sqlColumn); 48 | } 49 | }); 50 | return this; 51 | } 52 | 53 | public SQLSelectClause distinct(){ 54 | this.distinct = true; 55 | return this; 56 | } 57 | 58 | public SQLSelectClause from(Class domainType){ 59 | this.tableAliases.put(domainType, domainType.getSimpleName()); 60 | this.domainType = domainType; 61 | return this; 62 | } 63 | 64 | public SQLSelectJoinClause join(Class domainType){ 65 | String tableAlias = domainType.getSimpleName(); 66 | String tableName = getTableName(domainType); 67 | this.tableAliases.put(domainType, tableAlias); 68 | 69 | SQLSelectJoinClause selectJoinClause = new SQLSelectJoinClause(this, tableName, tableAlias); 70 | this.joinList.add(selectJoinClause); 71 | return selectJoinClause; 72 | } 73 | 74 | public SQLSelectJoinClause leftJoin(Class domainType){ 75 | String tableAlias = domainType.getSimpleName(); 76 | String tableName = getTableName(domainType); 77 | this.tableAliases.put(domainType, tableAlias); 78 | 79 | SQLSelectJoinClause selectJoinClause = new SQLSelectJoinClause(this, tableName, tableAlias); 80 | this.leftJoinList.add(selectJoinClause); 81 | return selectJoinClause; 82 | } 83 | 84 | public SQLSelectJoinClause rightJoin(Class domainType){ 85 | String tableAlias = domainType.getSimpleName(); 86 | String tableName = getTableName(domainType); 87 | this.tableAliases.put(domainType, tableAlias); 88 | 89 | SQLSelectJoinClause selectJoinClause = new SQLSelectJoinClause(this, tableName, tableAlias); 90 | this.rightJoinList.add(selectJoinClause); 91 | return selectJoinClause; 92 | } 93 | 94 | public SQLSelectClause where(Expression... expressions){ 95 | this.whereList.addAll(Arrays.asList(expressions)); 96 | return this; 97 | } 98 | 99 | public SQLSelectClause groupBy(SqlColumn... columns){ 100 | this.groupByList.addAll(Arrays.asList(columns)); 101 | return this; 102 | } 103 | 104 | public SQLSelectClause orderBy(SqlColumn... columns){ 105 | this.orderByList.addAll(Arrays.asList(columns)); 106 | return this; 107 | } 108 | 109 | public String getSQL(){ 110 | Assert.notNull(this.domainType, "请使用 '.from()' 方法指定查询实体类型."); 111 | 112 | if (this.columnList.isEmpty()) { 113 | Arrays.stream(this.domainType.getDeclaredFields()) 114 | .filter(field -> !field.isAnnotationPresent(Transient.class) && !field.isAnnotationPresent(java.beans.Transient.class)) 115 | .forEach(field -> { 116 | field.setAccessible(true); 117 | columnList.add(new SqlColumn(this.domainType, field)); 118 | }); 119 | } 120 | 121 | return new SQL() {{ 122 | columnList.forEach(column -> { 123 | SELECT(String.format("%s.%s", distinct ? "distinct " + getTableAliases(column.domainType()): getTableAliases(column.domainType()), column)); 124 | distinct = false; 125 | }); 126 | 127 | FROM(String.format("%s %s", getTableName(domainType), getTableAliases(domainType))); 128 | 129 | joinList.forEach(selectJoin -> JOIN(selectJoin.render())); 130 | 131 | leftJoinList.forEach(selectJoin -> LEFT_OUTER_JOIN(selectJoin.render())); 132 | 133 | rightJoinList.forEach(selectJoin -> RIGHT_OUTER_JOIN(selectJoin.render())); 134 | 135 | //where 136 | IntStream.range(0, whereList.size()) 137 | .forEach(idx -> { 138 | Expression expression = whereList.get(idx); 139 | SqlColumn column = expression.getColumn(); 140 | WHERE(expression.render(getTableAliases(column.domainType()), idx)); 141 | }); 142 | 143 | groupByList.stream() 144 | .forEach(column -> GROUP_BY(String.format("%s.%s", getTableAliases(column.domainType()), column))); 145 | 146 | orderByList.stream() 147 | .forEach(column -> ORDER_BY(String.format("%s.%s%s", getTableAliases(column.domainType()), column, column.descending() ? " desc" : ""))); 148 | }}.toString(); 149 | } 150 | 151 | public Object getParameter(){ 152 | return Expressions.fetchQueryParameter(whereList); 153 | } 154 | 155 | private String getTableName(Class domainType) { 156 | if(domainType.isAnnotationPresent(Table.class)){ 157 | return domainType.getAnnotation(Table.class).value(); 158 | }else{ 159 | return ParsingUtils.reconcatenateCamelCase(domainType.getSimpleName(), "_"); 160 | } 161 | } 162 | 163 | String getTableAliases(Class sqlTable) { 164 | return this.tableAliases.get(sqlTable); 165 | } 166 | 167 | private String generatTableAlias(){ 168 | return "t" + tableAliases.size(); 169 | } 170 | 171 | public static class SQLSelectJoinClause{ 172 | private String tableAlias; 173 | private String tableName; 174 | private SqlColumn leftColumn; 175 | private SqlColumn rightColumn; 176 | private SQLSelectClause sqlSelectClause; 177 | 178 | public SQLSelectClause on(SqlColumn leftColumn, SqlColumn rightColumn){ 179 | this.leftColumn = leftColumn; 180 | this.rightColumn = rightColumn; 181 | return this.sqlSelectClause; 182 | } 183 | 184 | SQLSelectJoinClause(SQLSelectClause sqlSelectClause, String tableName, String tableAlias) { 185 | this.tableAlias = tableAlias; 186 | this.tableName = tableName; 187 | this.sqlSelectClause = sqlSelectClause; 188 | } 189 | 190 | String render() { 191 | return String.format("%s %s on %s.%s=%s.%s", 192 | this.tableName, tableAlias, 193 | getTableAlias(leftColumn.domainType()), leftColumn.name(), 194 | getTableAlias(rightColumn.domainType()), rightColumn.name()); 195 | } 196 | 197 | private String getTableAlias(Class table) { 198 | return sqlSelectClause.getTableAliases(table); 199 | } 200 | } 201 | } 202 | --------------------------------------------------------------------------------