You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Data JDBC中类似#{#entityName}的实体名映射方案

解决方案

Spring Data JDBC 本身没有提供和 JPA 中 #{#entityName} 完全一致的内置 SpEL 表达式,但可以通过以下两种方式实现动态映射实体对应表名的需求:

方法一:自定义 SpEL 扩展

你可以通过实现 EvaluationContextExtension 来暴露当前仓库对应的实体表名,让其可以在 @Query 中引用:

  1. 创建自定义扩展类,获取当前仓库的实体类型并解析对应表名:
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();
    }
}
  1. 在仓库工厂中注入当前仓库信息到扩展类:
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;
    }
}
  1. 在仓库接口的 @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语句:

  1. 创建泛型基础仓库接口:
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);
}
  1. 实现基础仓库类,动态获取表名并执行删除:
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);
    }
}
  1. 让你的具体仓库接口继承这个基础仓库,无需再重复编写自定义查询:
public interface UserRepository extends BaseRepository<User, Integer> {
    // 其他自定义方法
}

这两种方法都能实现类似 JPA #{#entityName} 的动态表名映射效果,其中方法二更适合需要批量复用通用删除逻辑的场景,方法一则保留了 @Query 注解的灵活性。

内容的提问来源于stack exchange,提问作者user17051329

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 12:43:15