Spring Boot+Liquibase中SQL脚本读取Classpath文件失败问题咨询
解决PostgreSQL通过pg_read_binary_file读取Spring Boot classpath文件失败的问题
问题场景
使用Kotlin开发Spring Boot应用,通过Liquibase执行SQL迁移与数据填充。已创建maps表及对应SavedMapEntity实体类,在changelog脚本调用的SQL插入文件中,尝试用pg_read_binary_file读取classpath:liquibase/map/下的文件时,PostgreSQL报文件不存在错误,但该文件确实存在于resources/liquibase/map目录。
相关代码
创建表脚本
CREATE TABLE maps ( id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL PRIMARY KEY, camera_map OID NOT NULL );
Kotlin实体类
@Entity @Table( name = "maps", schema = "fleet", ) class SavedMapEntity : BaseEntity() { @Lob @Basic(fetch = FetchType.LAZY) @Column(name = "camera_map", nullable = false) var cameraMap: Blob? = null }
Liquibase Changelog脚本
<changeSet id="11-map-v_1.0.0_00006" author="fleet_management"> <sqlFile path="liquibase/sql/script/v1/11-map/v_1.0.0_000006_insert_map.sql" splitStatements="false"/> <comment> Add default sim floor, department, and map </comment> </changeSet>
插入数据SQL示例
DO $$ DECLARE temp_camera_map bytea; temp_camera_map_oid oid; BEGIN temp_camera_map := pg_read_binary_file('classpath:liquibase/camera_map')::bytea; temp_camera_map_oid := lo_import('/tmp/temp_camera_map.bin'); INSERT INTO maps (camera_map) VALUES (temp_image_oid); END $$;
错误信息
Caused by: org.postgresql.util.PSQLException: ERROR: could not open file "classpath:liquibase/map/sim_occupancy_map" for reading: No such file or directory
问题原因
pg_read_binary_file的局限性:该函数是PostgreSQL服务器端函数,仅能读取数据库服务器本地文件系统的文件,无法识别Spring Boot的classpath:路径——这个路径是应用层面的资源定位符,数据库服务器无法解析访问。- SQL变量错误:示例SQL中使用了未定义的
temp_image_oid变量,正确应为temp_camera_map_oid。
解决方案
方案1:利用Liquibase读取classpath资源并插入
Liquibase可直接访问classpath资源,无需依赖数据库服务器读取文件。通过Liquibase的函数将文件转成Base64,再在SQL中解码为bytea,最后创建大对象插入:
<changeSet id="insert-map-fixed" author="fleet_management"> <!-- 定义classpath下的文件路径 --> <property name="mapFilePath" value="classpath:liquibase/map/sim_occupancy_map" dbms="postgresql"/> <sql> DO $$ DECLARE temp_camera_map bytea := decode('${base64Encode(mapFilePath)}', 'base64'); temp_camera_map_oid oid; BEGIN -- 直接从bytea创建大对象,无需临时文件 temp_camera_map_oid := lo_from_bytea(0, temp_camera_map); INSERT INTO maps (camera_map) VALUES (temp_camera_map_oid); END $$; </sql> </changeSet>
方案2:将文件复制到数据库服务器可访问路径(不推荐)
若坚持使用pg_read_binary_file,需将resources/liquibase/map/下的文件复制到数据库服务器本地文件系统(如PostgreSQL数据目录/var/lib/postgresql/data/,需确保权限),然后修改SQL中的路径为服务器绝对路径:
temp_camera_map := pg_read_binary_file('/var/lib/postgresql/data/sim_occupancy_map')::bytea;
此方法灵活性差,换环境需重复操作,不适用于多环境部署场景。
方案3:修正SQL变量错误
无论采用哪种方案,需先将SQL中的temp_image_oid替换为temp_camera_map_oid,避免变量未定义错误。
内容的提问来源于stack exchange,提问作者Burak Dağlı
相关产品推荐
相关产品推荐

