Spring Data JDBC中类似#{#entityName}的实体名映射方案
解决方案
Spring Data JDBC 本身没有提供和 JPA 中 #{#entityName} 完全一致的内置 SpEL 表达式,但可以通过以下两种方式实现动态映射实体对应表名的需求:
方法一:自定义 SpEL 扩展
你可以通过实现 EvaluationContextExtension 来暴露当前仓库对应的实体表名,让其可以在 @Query 中引用:
- 创建自定义扩展类,获取当前仓库的实体类型并解析对应表名:
import org.springframework.data.jdbc.core.convert.JdbcConverter; import org.springframework.data.jdbc.core.mapping.JdbcPersistentEntity; import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.data.spel.spi.EvaluationContextExtension; import org.springframework.stereotype.Component; @Component public class EntityTableNameExtension implements EvaluationContextExtension { private final JdbcConverter jdbcConverter; private RepositoryInformation repositoryInformation; public EntityTableNameExtension(JdbcConverter jdbcConverter) { this.jdbcConverter = jdbcConverter; } // 通过仓库工厂注入当前仓库的信息 public void setRepositoryInformation(RepositoryInformation repositoryInformation) { this.repositoryInformation = repositoryInformation; } @Override public String getExtensionId() { return "entityTableName"; } @Override public Object getRootObject() { return this; } // 提供获取表名的方法,供SpEL调用 public String getTableName() { Class<?> domainType = repositoryInformation.getDomainType(); JdbcPersistentEntity<?> entity = jdbcConverter.getMappingContext().getPersistentEntity(domainType); return entity.getTableName(); } }
- 在仓库工厂中注入当前仓库信息到扩展类:
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactoryBean; import org.springframework.data.repository.core.support.RepositoryFactorySupport; public class CustomJdbcRepositoryFactoryBean<T extends org.springframework.data.repository.Repository<S, ID>, S, ID> extends JdbcRepositoryFactoryBean<T, S, ID> { private final EntityTableNameExtension tableNameExtension; public CustomJdbcRepositoryFactoryBean(Class<? extends T> repositoryInterface, EntityTableNameExtension tableNameExtension) { super(repositoryInterface); this.tableNameExtension = tableNameExtension; } @Override protected RepositoryFactorySupport createRepositoryFactory() { RepositoryFactorySupport factory = super.createRepositoryFactory(); tableNameExtension.setRepositoryInformation(factory.getRepositoryInformation(getRepositoryInterface())); return factory; } }
- 在仓库接口的
@Query中使用自定义 SpEL 表达式:
@Modifying @Query("DELETE FROM #{#entityTableName.getTableName()} WHERE user=:user AND id=:id") void deleteByUserAndId(@Param("user") int user, @Param("id") Integer id);
方法二:抽象基础仓库类
通过泛型基础仓库封装通用的删除逻辑,动态构建SQL语句:
- 创建泛型基础仓库接口:
import org.springframework.data.repository.NoRepositoryBean; import org.springframework.data.repository.Repository; @NoRepositoryBean public interface BaseRepository<T, ID> extends Repository<T, ID> { void deleteByUserAndId(int user, Integer id); }
- 实现基础仓库类,动态获取表名并执行删除:
import org.springframework.data.jdbc.core.convert.JdbcConverter; import org.springframework.data.jdbc.core.mapping.JdbcPersistentEntity; import org.springframework.data.jdbc.repository.support.SimpleJdbcRepository; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.transaction.annotation.Transactional; import java.io.Serializable; public class BaseRepositoryImpl<T, ID extends Serializable> extends SimpleJdbcRepository<T, ID> implements BaseRepository<T, ID> { private final NamedParameterJdbcTemplate namedParameterJdbcTemplate; private final JdbcConverter jdbcConverter; private final JdbcPersistentEntity<T> persistentEntity; public BaseRepositoryImpl(JdbcPersistentEntity<T> persistentEntity, NamedParameterJdbcTemplate namedParameterJdbcTemplate, JdbcConverter jdbcConverter) { super(persistentEntity, namedParameterJdbcTemplate, jdbcConverter); this.namedParameterJdbcTemplate = namedParameterJdbcTemplate; this.jdbcConverter = jdbcConverter; this.persistentEntity = persistentEntity; } @Override @Transactional public void deleteByUserAndId(int user, Integer id) { String tableName = persistentEntity.getTableName(); String sql = String.format("DELETE FROM %s WHERE user=:user AND id=:id", tableName); MapSqlParameterSource params = new MapSqlParameterSource(); params.addValue("user", user); params.addValue("id", id); namedParameterJdbcTemplate.update(sql, params); } }
- 让你的具体仓库接口继承这个基础仓库,无需再重复编写自定义查询:
public interface UserRepository extends BaseRepository<User, Integer> { // 其他自定义方法 }
这两种方法都能实现类似 JPA #{#entityName} 的动态表名映射效果,其中方法二更适合需要批量复用通用删除逻辑的场景,方法一则保留了 @Query 注解的灵活性。
内容的提问来源于stack exchange,提问作者user17051329
相关产品推荐
相关产品推荐

