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

RelationalDataAccessApplication传参及数据库操作相关技术问题

解决你的Spring组件交互与GeoJSON批量处理问题

哥们,你的核心问题是没搞清楚Spring组件之间的正确调用方式,直接去调用CommandLineRunner类的main方法是完全错误的操作,咱们一步步重构代码来解决你的三个问题:

核心重构思路

把原来写在RelationalDataAccessApplication里的数据库操作逻辑,抽成一个独立的GeoJsonDataService,这样其他Service(比如你的SchemeService)就可以通过Spring依赖注入的方式正常调用它,而不是去碰启动类的代码。


问题1:传递name和inputStream参数

原来的RelationalDataAccessApplication.main(name, inputStream)写法完全不成立(main方法只能接收String数组),重构后我们直接在新的GeoJsonDataService里写一个接收这两个参数的方法,SchemeService注入后直接调用即可。

问题2:获取生成的表名

让GeoJsonDataService的处理方法直接返回生成的表名,这样SchemeService可以直接拿到这个值并存入你的Scheme对象。

问题3:调用dropTable和readTable函数

把原来的私有方法改成GeoJsonDataService的公共方法,SchemeService注入后直接调用就行,权限问题直接解决。


重构后的代码示例

1. 创建独立的GeoJsonDataService

这个类专门处理GeoJSON的解析、数据库表创建/插入/删除/查询逻辑:

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.UUID;

@Service
public class GeoJsonDataService {
    private static final Logger log = LoggerFactory.getLogger(GeoJsonDataService.class);
    private final JdbcTemplate jdbcTemplate;

    // Spring推荐的构造方法注入
    public GeoJsonDataService(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    // 处理GeoJSON并返回生成的表名
    public String processGeoJson(String userName, InputStream inputStream) throws Exception {
        log.info("开始反序列化GEOJSON");
        DeserializeJSONFileToPropsAndGeom.initialize(inputStream);
        List<String> properties = DeserializeJSONFileToPropsAndGeom.getProperties();
        List<String> geometries = DeserializeJSONFileToPropsAndGeom.getGeometries();

        log.info("检查表是否存在");
        String checkedNameTable = generateTableName(userName);
        log.info("创建表: {}", checkedNameTable);
        jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS " + checkedNameTable + " ( table_id SERIAL, properties jsonb not null, geom geometry(GeometryZ,4326), primary key (table_id));");

        log.info("批量插入数据");
        // 修正批量插入的正确写法,原来的方式不是真正的批量操作
        String insertSql = "INSERT INTO " + checkedNameTable + " ( properties, geom ) VALUES ( ?, ST_Force3D(ST_SetSRID(ST_GeomFromGeoJSON(?), 4326)) );";
        jdbcTemplate.batchUpdate(insertSql, properties.size(), (ps, i) -> {
            ps.setString(1, properties.get(i).replace("'", "´"));
            ps.setString(2, geometries.get(i));
        });

        return checkedNameTable;
    }

    public void dropTable(String nameTable) throws Exception {
        log.info("删除表: {}", nameTable);
        if (nameTable != null && !nameTable.isBlank()) {
            String sql = "DROP TABLE IF EXISTS " + nameTable + ";";
            jdbcTemplate.execute(sql);
        }
    }

    public List<Map<String, Object>> readTable(String nameTable) throws Exception {
        log.info("读取表数据: {}", nameTable);
        if (nameTable != null && !nameTable.isBlank()) {
            String sql = "SELECT table_id, CAST(properties AS text), GeometryType(geom) FROM " + nameTable + " ORDER BY table_id ASC;";
            return jdbcTemplate.queryForList(sql);
        }
        return null;
    }

    private String generateTableName(String name) {
        UUID uuid = UUID.randomUUID();
        return isTableExists(name) ? name.toLowerCase() : name.toLowerCase() + "_" + uuid.toString().replace("-", "_");
    }

    private boolean isTableExists(String name) {
        // 修正表存在性检查的SQL,原来的逻辑不对
        String sql = "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public' AND table_name = ?";
        Integer count = jdbcTemplate.queryForObject(sql, new Object[]{name.toLowerCase()}, Integer.class);
        return count != null && count > 0;
    }
}

2. 改造启动类RelationalDataAccessApplication

现在它只是一个启动入口,不需要再承载业务逻辑,如果需要启动时测试,可以在这里注入GeoJsonDataService进行测试:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.CommandLineRunner;
import org.springframework.beans.factory.annotation.Autowired;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.InputStream;

@SpringBootApplication
public class RelationalDataAccessApplication implements CommandLineRunner {
    private static final Logger log = LoggerFactory.getLogger(RelationalDataAccessApplication.class);

    @Autowired
    private GeoJsonDataService geoJsonDataService;

    public static void main(String[] args) {
        SpringApplication.run(RelationalDataAccessApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        // 启动时测试的话可以打开下面的代码,替换成你的文件路径
        // InputStream inputStream = new FileInputStream("your/geo/json/path.geojson");
        // String tableName = geoJsonDataService.processGeoJson("test_table", inputStream);
        // log.info("测试创建的表名: {}", tableName);
    }
}

3. 改造SchemeService

现在通过注入GeoJsonDataService来完成所有数据库操作:

@Service
@Transactional
public class SchemeService extends DaoCrudService {
    @Autowired
    private SchemeDao schemeDao;
    @Autowired
    private GeoJsonDataService geoJsonDataService; // 注入新的服务

    public <T> T createScheme(final String creatorId, final String name, final String description, final InputStream inputStream, final String attachmentId, final String filePath, final Class<T> targetVOClass) throws Exception {
        Scheme scheme = createScheme(creatorId, name, description, inputStream, attachmentId, filePath);
        return getDozerService().map(scheme, targetVOClass);
    }

    protected Scheme createScheme(final String creatorId, final String name, final String description, final InputStream inputStream, final String attachmentId, final String filePath) throws Exception {
        // 调用GeoJson处理方法,直接拿到生成的表名
        String tableId = geoJsonDataService.processGeoJson(name, inputStream);

        Scheme scheme = new Scheme();
        scheme.setCreator(creatorId);
        scheme.setName(name);
        scheme.setDescription(description);
        scheme.setTableId(tableId);
        scheme.setAttachmentId(attachmentId);
        scheme.setFilePath(filePath);
        createScheme(scheme); // 假设父类方法负责保存Scheme到数据库
        return scheme;
    }

    public List<Map<String, Object>> readTable(final String name) throws Exception {
        // 直接调用读取表的方法
        return geoJsonDataService.readTable(name);
    }

    public void deleteScheme(final SchemeCriteria schemeCriteria) throws Exception {
        SchemeVO scheme = read(schemeDao, schemeCriteria, SchemeVO.class);
        String nameTable = scheme.getTableId();
        // 调用删除表的方法
        geoJsonDataService.dropTable(nameTable);
        delete(schemeDao, schemeCriteria);
    }
}

额外注意点

  1. 批量插入优化:原来的循环调用batchUpdate(SQL)不是真正的批量操作,现在改用带PreparedStatementSetter的重载方法,性能会好很多,适合大数据量场景。
  2. 表存在性检查修正:原来的SQL逻辑错误,现在改成精准查询指定表是否存在。
  3. 依赖注入规范:用构造方法注入代替字段注入,更符合Spring的最佳实践,也方便单元测试。
  4. 空值防护:在删除和读取表的方法里增加了表名的非空检查,避免SQL语法错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 09:05:45