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); } }
额外注意点
- 批量插入优化:原来的循环调用
batchUpdate(SQL)不是真正的批量操作,现在改用带PreparedStatementSetter的重载方法,性能会好很多,适合大数据量场景。 - 表存在性检查修正:原来的SQL逻辑错误,现在改成精准查询指定表是否存在。
- 依赖注入规范:用构造方法注入代替字段注入,更符合Spring的最佳实践,也方便单元测试。
- 空值防护:在删除和读取表的方法里增加了表名的非空检查,避免SQL语法错误。
内容的提问来源于stack exchange,提问作者Javier
相关产品推荐
相关产品推荐

