first commit.

This commit is contained in:
2022-12-07 18:14:38 +08:00
commit e916d067f3
183 changed files with 9649 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
package xyz.zhouxy.plusone.jdbc;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.lang.Nullable;
/**
* 扩展了 {@link BeanPropertySqlParameterSource},在将 POJO 转换为
* {@link org.springframework.jdbc.core.namedparam.SqlParameterSource} 时,
* 使用 {@link Enum#ordinal()} 将枚举转换成整数。
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*
* @see SqlParameterSource
* @see BeanPropertySqlParameterSource
* @see org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate
* @see Enum
*/
public class BeanPropertyParamSource extends BeanPropertySqlParameterSource {
public BeanPropertyParamSource(Object object) {
super(object);
}
@Override
@Nullable
public Object getValue(String paramName) throws IllegalArgumentException {
Object value = super.getValue(paramName);
if (value instanceof Enum) {
return ((Enum<?>) value).ordinal();
}
return value;
}
}

View File

@@ -0,0 +1,51 @@
package xyz.zhouxy.plusone.jdbc;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jdbc.core.convert.JdbcCustomConversions;
import org.springframework.data.jdbc.repository.QueryMappingConfiguration;
import org.springframework.data.jdbc.repository.config.AbstractJdbcConfiguration;
import org.springframework.data.jdbc.repository.config.DefaultQueryMappingConfiguration;
import xyz.zhouxy.plusone.jdbc.converter.EnumToOrdinalConverter;
/**
* JDBC 配置
*
* <p>
* 配置了类型转换器
* </p>
*
* <p>
* 这里声明了 {@link DefaultQueryMappingConfiguration} 的实例为 Spring bean。
* 在其它配置类中注入该 bean就可以向其中添加
* {@link org.springframework.jdbc.core.RowMapper}
* 供 Spring Data JDBC 查询时使用。如下所示:
*
* <pre>
* {@code @Configuration}
* class CustomConfig {
* public CustomConfig(DefaultQueryMappingConfiguration rowMappers) {
* rowMappers.registerRowMapper(Person.class, new PersonRowMapper());
* }
* }
* </pre>
* </p>
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
@Configuration
public class JdbcConfig extends AbstractJdbcConfiguration {
@Override
public JdbcCustomConversions jdbcCustomConversions() {
return new JdbcCustomConversions(List.of(EnumToOrdinalConverter.INSTANCE));
}
@Bean
QueryMappingConfiguration rowMappers() {
return new DefaultQueryMappingConfiguration();
}
}

View File

@@ -0,0 +1,68 @@
package xyz.zhouxy.plusone.jdbc;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.stream.Stream;
import javax.annotation.Nonnull;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import xyz.zhouxy.plusone.domain.Entity;
public abstract class JdbcEntityDaoSupport<T extends Entity<ID>, ID extends Serializable> {
protected final NamedParameterJdbcTemplate jdbc;
protected RowMapper<T> rowMapper;
protected ResultSetExtractor<T> resultSetExtractor;
protected JdbcEntityDaoSupport(@Nonnull NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
this.jdbc = namedParameterJdbcTemplate;
this.rowMapper = (ResultSet rs, int rowNum) -> mapRow(rs);
this.resultSetExtractor = (ResultSet rs) -> rs.next() ? mapRow(rs) : null;
}
protected final T queryForObject(String sql) {
return this.jdbc.query(sql, this.resultSetExtractor);
}
protected final T queryForObject(String sql, SqlParameterSource paramSource) {
return this.jdbc.query(sql, paramSource, this.resultSetExtractor);
}
protected final List<T> queryForList(String sql) {
return this.jdbc.query(sql, this.rowMapper);
}
protected final List<T> queryForList(String sql, SqlParameterSource parameterSource) {
return this.jdbc.query(sql, parameterSource, this.rowMapper);
}
protected final Stream<T> queryForStream(String sql, SqlParameterSource parameterSource) {
return this.jdbc.queryForStream(sql, parameterSource, this.rowMapper);
}
protected final <E> Stream<E> queryForStream(String sql, SqlParameterSource parameterSource, Class<E> elementType) {
return this.jdbc.queryForList(sql, parameterSource, elementType).stream();
}
protected final boolean queryExists(String sql, SqlParameterSource parameterSource) {
Boolean isExists = this.jdbc.query(sql, parameterSource, ResultSet::next);
return Boolean.TRUE.equals(isExists);
}
protected abstract T mapRow(ResultSet rs) throws SQLException;
protected void setRowMapper(@Nonnull RowMapper<T> rowMapper) {
this.rowMapper = rowMapper;
}
protected void setResultSetExtractor(@Nonnull ResultSetExtractor<T> resultSetExtractor) {
this.resultSetExtractor = resultSetExtractor;
}
}

View File

@@ -0,0 +1,30 @@
package xyz.zhouxy.plusone.jdbc;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import xyz.zhouxy.plusone.spring.SpringContextHolder;
/**
* 全局单例,由 Spring 装配好的对象。
* 可通过静态方法获取 Spring 容器中的 {@link JdbcTemplate} 和
* {@link NamedParameterJdbcTemplate} 对象。
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
* @see JdbcTemplate
* @see NamedParameterJdbcTemplate
*/
public final class JdbcFactory {
private JdbcFactory() {
throw new IllegalStateException("Utility class");
}
public static JdbcTemplate getJdbcTemplate() {
return SpringContextHolder.getContext().getBean(JdbcTemplate.class);
}
public static NamedParameterJdbcTemplate getNamedParameterJdbcTemplate() {
return SpringContextHolder.getContext().getBean(NamedParameterJdbcTemplate.class);
}
}

View File

@@ -0,0 +1,58 @@
package xyz.zhouxy.plusone.jdbc;
import java.io.Serializable;
import javax.annotation.Nonnull;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import xyz.zhouxy.plusone.domain.AggregateRoot;
import xyz.zhouxy.plusone.domain.IRepository;
public abstract class JdbcRepositorySupport<T extends AggregateRoot<ID>, ID extends Serializable>
extends JdbcEntityDaoSupport<T, ID>
implements IRepository<T, ID> {
protected JdbcRepositorySupport(@Nonnull NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
super(namedParameterJdbcTemplate);
}
protected abstract void doDelete(@Nonnull T entity);
protected abstract T doFindById(@Nonnull ID id);
protected abstract T doInsert(@Nonnull T entity);
protected abstract T doUpdate(@Nonnull T entity);
@Override
public final void delete(T entity) {
if (entity == null) {
throw new IllegalArgumentException("Cannot delete null.");
}
doDelete(entity);
}
@Override
public final T find(ID id) {
if (id == null) {
throw new IllegalArgumentException("Id cannot be null.");
}
return doFindById(id);
}
@Override
public final T save(T entity) {
if (entity == null) {
throw new IllegalArgumentException("Cannot save null.");
}
return entity.getId().isPresent() ? doUpdate(entity) : doInsert(entity);
}
protected abstract SqlParameterSource generateParamSource(ID id, @Nonnull T entity);
protected final SqlParameterSource generateParamSource(@Nonnull T entity) {
return generateParamSource(entity.getId().orElseThrow(), entity);
}
}

View File

@@ -0,0 +1,45 @@
package xyz.zhouxy.plusone.jdbc.common;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 查询结果处理
*
* <p>
* 通过在 {@link #map(ResultSet)} 中配置 {@link ResultSet} 到对象的映射,
* 可将 {@link #rowMapper(ResultSet, int)} 的方法应用,
* 直接当成 {@link org.springframework.jdbc.core.RowMapper} 对象传给
* {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate}
* 的查询方法,
* 或者在 Spring Data JDBC 的配置中使用。
* </p>
*
* <p>
* 在
* {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate}
* 的 query(String, java.util.Map, ResultSetExtractor)
* 和 query(String, SqlParameterSource, ResultSetExtractor)
* 两个方法执行时ResultSetExtractor 中需要执行一次 {@link ResultSet#next()} 判断是否查询到数据,
* 如果查询到数据再执行查询结果的实例化。
* {@link #resultSetExtractor(ResultSet)} 封装了这个过程,
* 和 {@link #rowMapper(ResultSet, int)}一样,
* 直接把方法引用当成 {@link org.springframework.jdbc.core.ResultSetExtractor} 的对象即可。
* </p>
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
* @see org.springframework.data.jdbc.repository.config.DefaultQueryMappingConfiguration
*/
@FunctionalInterface
public interface ResultMapper<T> {
T map(ResultSet resultSet) throws SQLException;
default T rowMapper(ResultSet resultSet, int rowNum) throws SQLException {
return map(resultSet);
}
default T resultSetExtractor(ResultSet resultSet) throws SQLException {
return resultSet.next() ? map(resultSet) : null;
}
}

View File

@@ -0,0 +1,30 @@
package xyz.zhouxy.plusone.jdbc.common;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.RowMapper;
public class SimpleResultMapper<T> implements ResultMapper<T> {
private final RowMapper<T> rowMapper;
public SimpleResultMapper(RowMapper<T> rowMapper) {
this.rowMapper = rowMapper;
}
@Override
public T map(ResultSet resultSet) throws SQLException {
return rowMapper.mapRow(resultSet, 1);
}
@Override
public T rowMapper(ResultSet resultSet, int rowNum) throws SQLException {
return this.rowMapper.mapRow(resultSet, rowNum);
}
public static <T> SimpleResultMapper<T> of(Class<T> clazz) {
return new SimpleResultMapper<>(new BeanPropertyRowMapper<>(clazz));
}
}

View File

@@ -0,0 +1,20 @@
package xyz.zhouxy.plusone.jdbc.converter;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.WritingConverter;
/**
* 枚举序号转换器
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
@WritingConverter
public enum EnumToOrdinalConverter implements Converter<Enum<?>, Integer> {
INSTANCE
;
@Override
public Integer convert(Enum<?> source) {
return source.ordinal();
}
}

View File

@@ -0,0 +1,44 @@
package xyz.zhouxy.plusone.jdbc.converter;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.ReadingConverter;
/**
* 序号枚举转换器
* <p>
* 向 {@link org.springframework.data.jdbc.core.convert.JdbcCustomConversions}
* 中添加类型转换器,
* 如:
*
* <pre>
* return new JdbcCustomConversions(List.of(new OrdinalToEnumConverter<SystemStatus>()));
* </pre>
* </p>
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*
* @see Converter
* @see org.springframework.data.jdbc.core.convert.JdbcCustomConversions
*/
@ReadingConverter
public class OrdinalToEnumConverter<E extends Enum<E>> implements Converter<Integer, Enum<E>> {
private final Class<E> type;
private final E[] constants;
public OrdinalToEnumConverter(Class<E> type) {
this.type = type;
this.constants = type.getEnumConstants();
}
@Override
public Enum<E> convert(Integer ordinal) {
try {
return constants[ordinal];
} catch (ArrayIndexOutOfBoundsException exception) {
throw new IllegalArgumentException(
String.format("Cannot convert %d to %s by ordinal value.", ordinal, type.getSimpleName()),
exception);
}
}
}