first commit.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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));
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user