Inserting BLOB Using Spring JdbcTemplate Example
1. Introduction
BLOB (Binary Large Object) is a large object data type in the database system. BLOB could store documents, images, audio, or video files. For smaller BLOBs, setting a byte array directly in memory is ok. For large BLOBs, streaming is more memory efficient. In this example, I will demonstrate how to use JdbcTemplate to insert and update a BLOB field in a PostgreSQL database.
2. Setup
In this step, I will create a Spring Boot (4.0.1) Java (21) project via Spring Initializer with PostgreSQL, Lombok, and JPA dependencies.
2.1 Build.gradle
Here is the generated build.gradle file.
build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '4.0.1'
id 'io.spring.dependency-management' version '1.1.7'
}
group = 'org.jcg.zheng.demo'
version = '0.0.1-SNAPSHOT'
description = 'Demo project for BLOB'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'org.postgresql:postgresql'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-data-jpa-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
tasks.named('test') {
useJUnitPlatform()
}
2.2 InsertBlobApplication
Here is the generated InsertBlobApplication.java file.
InsertBlobApplication.java
package org.jcg.zheng.demo.insert_blob;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class InsertBlobApplication {
public static void main(String[] args) {
SpringApplication.run(InsertBlobApplication.class, args);
}
}
2.3 Application Properties
Here is the updated application.properties file.
application.properties
spring.application.name=insert-blob # DataSource configuration spring.datasource.url=jdbc:postgresql://localhost:5432/postgres?currentSchema=marydb spring.datasource.username=postgres spring.datasource.password= spring.datasource.driver-class-name=org.postgresql.Driver logging.level.org.springframework.jdbc.core.JdbcTemplate=trace logging.level.org.springframework.jdbc.core.StatementCreatorUtils=trace
- Line 9-10: enable the logging to show the raw SQL statement and binding parameters.
2.4 Start the Spring Boot Application
In this step, I will start the generated spring boot application and capture the server log.
Server Log
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v4.0.1) 2026-01-16T20:32:26.077-06:00 INFO 2864 --- [insert-blob] [ main] o.j.z.d.i.InsertBlobApplication : Starting InsertBlobApplication using Java 21.0.8 with PID 2864 (C:\MaryZheng\workspace\insert-blob-postgre\bin\main started by zzhen in C:\MaryZheng\workspace\insert-blob-postgre) 2026-01-16T20:32:26.081-06:00 INFO 2864 --- [insert-blob] [ main] o.j.z.d.i.InsertBlobApplication : No active profile set, falling back to 1 default profile: "default" 2026-01-16T20:32:26.440-06:00 INFO 2864 --- [insert-blob] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode. 2026-01-16T20:32:26.456-06:00 INFO 2864 --- [insert-blob] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 10 ms. Found 0 JPA repository interfaces. 2026-01-16T20:32:26.617-06:00 INFO 2864 --- [insert-blob] [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting... 2026-01-16T20:32:26.844-06:00 INFO 2864 --- [insert-blob] [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@64f16277 2026-01-16T20:32:26.846-06:00 INFO 2864 --- [insert-blob] [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed. 2026-01-16T20:32:26.942-06:00 INFO 2864 --- [insert-blob] [ main] org.hibernate.orm.jpa : HHH008540: Processing PersistenceUnitInfo [name: default] 2026-01-16T20:32:27.012-06:00 INFO 2864 --- [insert-blob] [ main] org.hibernate.orm.core : HHH000001: Hibernate ORM core version 7.2.0.Final 2026-01-16T20:32:27.535-06:00 INFO 2864 --- [insert-blob] [ main] o.s.o.j.p.SpringPersistenceUnitInfo : No LoadTimeWeaver setup: ignoring JPA class transformer 2026-01-16T20:32:27.592-06:00 INFO 2864 --- [insert-blob] [ main] org.hibernate.orm.connections.pooling : HHH10001005: Database info: Database JDBC URL [jdbc:postgresql://localhost:5432/postgres?currentSchema=marydb] Database driver: PostgreSQL JDBC Driver Database dialect: PostgreSQLDialect Database version: 18.0 Default catalog/schema: postgres/marydb Autocommit mode: undefined/unknown Isolation level: READ_COMMITTED [default READ_COMMITTED] JDBC fetch size: none Pool: DataSourceConnectionProvider Minimum pool size: undefined/unknown Maximum pool size: undefined/unknown 2026-01-16T20:32:28.238-06:00 INFO 2864 --- [insert-blob] [ main] org.hibernate.orm.core : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration) 2026-01-16T20:32:28.246-06:00 INFO 2864 --- [insert-blob] [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default' 2026-01-16T20:32:28.436-06:00 INFO 2864 --- [insert-blob] [ main] o.j.z.d.i.InsertBlobApplication : Started InsertBlobApplication in 2.647 seconds (process running for 2.928) 2026-01-16T20:32:28.446-06:00 INFO 2864 --- [insert-blob] [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default' 2026-01-16T20:32:28.449-06:00 INFO 2864 --- [insert-blob] [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated... 2026-01-16T20:32:28.452-06:00 INFO 2864 --- [insert-blob] [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
- Line 21: shows the Spring boot application using the PostgreSQL database as the configuration file specified.
3. PostgreSQL Database Table with a BLOB Column
In this step, I will create a table with a BLOB column in a PostgreSQL database:
Create Table SQL
create table demo_blob_table (
id integer not null,
content_type varchar(255),
blob_col bytea,
primary key (id)
);- Line 4: The PostgreSQL bytea data type is used to map to a BLOB object.
- Note: use different storage systems when a
byteafield exceeds 1 GB.
3.1 Create a Java Entity
In this example, I will create the BlobMeta.java that maps to a database table for non-BLOB fields.
BlobMeta.java
package org.jcg.zheng.demo.insert_blob.entity;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
@Entity
@Table(name="demo_blob_table")
@Data
public class BlobMeta {
@Id
private Integer id;
private String contentType;
}
- Line 9: the
BlobMetaentity is mapped to tabledemo_blob_tablewithout BLOB fields to reduce memory usage as JPA caches objects in memory.
4. Spring JdbcTemplate Insert BLOB
In this step, I will create an InsertBlobService.java that inserts or updates a BLOB column with the following methods:
insert_with_ByteArray: insert a BLOB asbyte[]directly using Spring JDBC.insert_with_bytes: insert a BLOB with the setBytes() method that sets the designated parameter to the given Java array of bytes when BLOB data is less than 1 MB.insert_with_BinaryStream: insert a BLOB and use the setBinaryStream method to set the designated parameter to the given input stream. It’s good when BLOB data is greater than 1 MB and less than 1 GB.insert_with_SQLVarbinary: insert a BLOB using Spring JDBC, explicitly treating the bytes array as SqlParameterValue(Types.VARBINARY) so the database stores it correctly.readBlob_asObject: read a BLOB via theJdbcTemplate.queryForObjectmethod. The return data is in memory. it’s ok for smaller BLOB objects.readBlob_asStream: read a BLOB via theJdbcTemplate.queryForStreammethod. It streams into theInputStreamand then writes to a file. It’s more memory efficient.update_with_bytes: update a BLOC via thesetBytesmethod.
InsertBlobService.java
package org.jcg.zheng.demo.insert_blob.service;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.sql.PreparedStatement;
import java.sql.Types;
import java.util.stream.Stream;
import org.jcg.zheng.demo.insert_blob.entity.BlobMeta;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.SqlParameterValue;
import org.springframework.stereotype.Service;
import jakarta.transaction.Transactional;
@Service
@Transactional
public class InsertBlobService {
private static final String SELECT_BLOB_SQL = "SELECT blob_col from demo_blob_table WHERE id=?";
private static final String UPDATE_BLOB_SQL = "UPDATE demo_blob_table set blob_col=? WHERE id=?";
private static final String INSERT_BLOB_SQL = "INSERT INTO demo_blob_table(id, content_type, blob_col) VALUES (?,?,?)";
private final JdbcTemplate jdbcTemplate;
public InsertBlobService(JdbcTemplate jdbcTemplate) {
super();
this.jdbcTemplate = jdbcTemplate;
}
public void insert_with_ByteArray(BlobMeta blobMeta, byte[] data) {
jdbcTemplate.update(INSERT_BLOB_SQL, blobMeta.getId(), blobMeta.getContentType(), data);
}
public void insert_with_SQLVarbinary(BlobMeta blobMeta, byte[] data) {
jdbcTemplate.update(INSERT_BLOB_SQL, blobMeta.getId(), blobMeta.getContentType(),
new SqlParameterValue(Types.VARBINARY, data));
}
public void insert_with_BinaryStream(BlobMeta blobMeta, byte[] data) {
jdbcTemplate.update(con -> {
PreparedStatement ps = con.prepareStatement(INSERT_BLOB_SQL);
ps.setInt(1, blobMeta.getId());
ps.setString(2, blobMeta.getContentType());
ps.setBinaryStream(3, new ByteArrayInputStream(data));
return ps;
});
}
public void insert_with_bytes(BlobMeta blobMeta, byte[] data) {
jdbcTemplate.update(con -> {
PreparedStatement ps = con.prepareStatement(INSERT_BLOB_SQL);
ps.setInt(1, blobMeta.getId());
ps.setString(2, blobMeta.getContentType());
ps.setBytes(3, data);
return ps;
});
}
public byte[] readBlob_asObject(int id) {
return jdbcTemplate.queryForObject(SELECT_BLOB_SQL, byte[].class, id);
}
public void readBlob_asStream(int id) {
Path target = Paths.get("output.bin");
try (Stream<InputStream> stream = jdbcTemplate.queryForStream(SELECT_BLOB_SQL,
(rs, rowNum) -> rs.getBinaryStream("blob_col"), id)) {
stream.findFirst().ifPresent(inputStream -> {
try (InputStream in = inputStream) {
Files.copy(in, target, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
e.printStackTrace();
}
});
}
}
public void update_with_bytes(int id, byte[] data) {
jdbcTemplate.update(UPDATE_BLOB_SQL, ps -> {
ps.setBytes(1, data);
ps.setInt(2, id);
});
}
}
- Line 25-27: the raw SQL statements to insert, update, and select BLOB data.
- Line 37: map
byte[]directly in theinsertstatement. - Line 42: use
SqlParameterValue(Types.VARBINARY)to map tobyte[]to ensure correct JDBC type and avoid JDBC driver-specific issues. It prevents binary data being treated as VARCHAR and works across databases ( MySQL, SQL Server, Oracle, PostgreSQL). - Line 50:
setBinaryStreammaps toInputStreamfor streaming into the database. - Line 60: use
setBytesmaps tobyte[]. - Line 66:
queryForObjectmaps tobyte[]in memory. - Line 71:
queryForStreammaps toInputStream. - Line 75:
try-with-resourcesclause closesInputStreamexplicitly. - Line 76:
Files.copywrites the BLOB into a file. - Line 88:
setBytesis used in an update statement.
5. Test InsertBlobService
In this step, I will create a InsertBlobServiceTest.java that tests the methods to insert, update, and select a BLOB column.
InsertBlobServiceTest.java
package org.jcg.zheng.demo.insert_blob.service;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.jcg.zheng.demo.insert_blob.entity.BlobMeta;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class InsertBlobServiceTest {
private static final String BLOB_STRING = "this is blob";
private static final String BLOB_STRING_2 = "this is blob 2";
@Autowired
private InsertBlobService testClass;
@Test
void test_insert_with_BinaryStream() {
int test_id = 2;
testClass.insert_with_BinaryStream(testBlobMeta(test_id), BLOB_STRING.getBytes());
byte[] ret = testClass.readBlob_asObject(test_id);
assertEquals(BLOB_STRING, new String(ret));
}
@Test
void test_insert_with_ByteArray() {
int test_id = 1;
testClass.insert_with_ByteArray(testBlobMeta(test_id), BLOB_STRING.getBytes());
byte[] ret = testClass.readBlob_asObject(test_id);
assertEquals(BLOB_STRING, new String(ret));
testClass.readBlob_asStream(1);
}
@Test
void test_insert_with_bytes() {
int test_id = 3;
testClass.insert_with_bytes(testBlobMeta(test_id), BLOB_STRING.getBytes());
byte[] ret = testClass.readBlob_asObject(test_id);
assertEquals(BLOB_STRING, new String(ret));
}
@Test
void test_insert_with_SQLVarbinary() {
int test_id = 4;
testClass.insert_with_SQLVarbinary(testBlobMeta(test_id), BLOB_STRING.getBytes());
byte[] ret = testClass.readBlob_asObject(test_id);
assertEquals(BLOB_STRING, new String(ret));
}
@Test
void test_update_with_bytes() {
int test_id = 1;
byte[] ret = testClass.readBlob_asObject(test_id);
if (ret == null) {
testClass.insert_with_ByteArray(testBlobMeta(test_id), BLOB_STRING.getBytes());
}
testClass.update_with_bytes(1, BLOB_STRING_2.getBytes());
assertEquals(BLOB_STRING_2, new String(testClass.readBlob_asObject(1)));
}
private BlobMeta testBlobMeta(int id) {
BlobMeta blobMeta = new BlobMeta();
blobMeta.setId(id);
blobMeta.setContentType("txt");
return blobMeta;
}
}
Run tests and capture output.
The console log shows the actual SQL statements and the binding parameters.
InsertBlobServiceTest Console Log
20:36:30.695 [main] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils -- Could not detect default configuration classes for test class [org.jcg.zheng.demo.insert_blob.service.InsertBlobServiceTest]: InsertBlobServiceTest does not declare any static, non-private, non-final, nested classes annotated with @Configuration. 20:36:30.773 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper -- Found @SpringBootConfiguration org.jcg.zheng.demo.insert_blob.InsertBlobApplication for test class org.jcg.zheng.demo.insert_blob.service.InsertBlobServiceTest . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v4.0.1) 2026-01-16T20:36:31.076-06:00 INFO 26912 --- [insert-blob] [ main] o.j.z.d.i.service.InsertBlobServiceTest : Starting InsertBlobServiceTest using Java 21.0.8 with PID 26912 (started by zzhen in C:\MaryZheng\workspace\insert-blob-postgre) 2026-01-16T20:36:31.076-06:00 INFO 26912 --- [insert-blob] [ main] o.j.z.d.i.service.InsertBlobServiceTest : No active profile set, falling back to 1 default profile: "default" 2026-01-16T20:36:31.322-06:00 INFO 26912 --- [insert-blob] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode. 2026-01-16T20:36:31.339-06:00 INFO 26912 --- [insert-blob] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 8 ms. Found 0 JPA repository interfaces. 2026-01-16T20:36:31.520-06:00 INFO 26912 --- [insert-blob] [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting... 2026-01-16T20:36:31.696-06:00 INFO 26912 --- [insert-blob] [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@31f295b6 2026-01-16T20:36:31.697-06:00 INFO 26912 --- [insert-blob] [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed. 2026-01-16T20:36:31.778-06:00 INFO 26912 --- [insert-blob] [ main] org.hibernate.orm.jpa : HHH008540: Processing PersistenceUnitInfo [name: default] 2026-01-16T20:36:31.842-06:00 INFO 26912 --- [insert-blob] [ main] org.hibernate.orm.core : HHH000001: Hibernate ORM core version 7.2.0.Final 2026-01-16T20:36:32.239-06:00 INFO 26912 --- [insert-blob] [ main] o.s.o.j.p.SpringPersistenceUnitInfo : No LoadTimeWeaver setup: ignoring JPA class transformer 2026-01-16T20:36:32.327-06:00 INFO 26912 --- [insert-blob] [ main] org.hibernate.orm.connections.pooling : HHH10001005: Database info: Database JDBC URL [jdbc:postgresql://localhost:5432/postgres?currentSchema=marydb] Database driver: PostgreSQL JDBC Driver Database dialect: PostgreSQLDialect Database version: 18.0 Default catalog/schema: postgres/marydb Autocommit mode: undefined/unknown Isolation level: READ_COMMITTED [default READ_COMMITTED] JDBC fetch size: none Pool: DataSourceConnectionProvider Minimum pool size: undefined/unknown Maximum pool size: undefined/unknown 2026-01-16T20:36:32.899-06:00 INFO 26912 --- [insert-blob] [ main] org.hibernate.orm.core : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration) 2026-01-16T20:36:32.905-06:00 INFO 26912 --- [insert-blob] [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default' 2026-01-16T20:36:33.074-06:00 INFO 26912 --- [insert-blob] [ main] o.j.z.d.i.service.InsertBlobServiceTest : Started InsertBlobServiceTest in 2.187 seconds (process running for 3.087) Mockito is currently self-attaching to enable the inline-mock-maker. This will no longer work in future releases of the JDK. Please add Mockito as an agent to your build as described in Mockito's documentation: https://javadoc.io/doc/org.mockito/mockito-core/latest/org.mockito/org/mockito/Mockito.html#0.3 WARNING: A Java agent has been loaded dynamically (C:\Users\zzhen\.gradle\caches\modules-2\files-2.1\net.bytebuddy\byte-buddy-agent\1.17.8\f09415827a71be7ed621c7bd02550678f28bc81c\byte-buddy-agent-1.17.8.jar) WARNING: If a serviceability tool is in use, please run with -XX:+EnableDynamicAgentLoading to hide this warning WARNING: If a serviceability tool is not in use, please run with -Djdk.instrument.traceUsage for more information WARNING: Dynamic loading of agents will be disallowed by default in a future release OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended 2026-01-16T20:36:33.443-06:00 DEBUG 26912 --- [insert-blob] [ main] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL update 2026-01-16T20:36:33.443-06:00 DEBUG 26912 --- [insert-blob] [ main] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL statement [INSERT INTO demo_blob_table(id, content_type, blob_col) VALUES (?,?,?)] 2026-01-16T20:36:33.450-06:00 TRACE 26912 --- [insert-blob] [ main] o.s.jdbc.core.StatementCreatorUtils : Setting SQL statement parameter value: column index 1, parameter value [4], value class [java.lang.Integer], SQL type unknown 2026-01-16T20:36:33.450-06:00 TRACE 26912 --- [insert-blob] [ main] o.s.jdbc.core.StatementCreatorUtils : Setting SQL statement parameter value: column index 2, parameter value [txt], value class [java.lang.String], SQL type unknown 2026-01-16T20:36:33.451-06:00 TRACE 26912 --- [insert-blob] [ main] o.s.jdbc.core.StatementCreatorUtils : Setting SQL statement parameter value: column index 3, parameter value [[B@2604940], value class [[B], SQL type -3 2026-01-16T20:36:33.453-06:00 TRACE 26912 --- [insert-blob] [ main] o.s.jdbc.core.JdbcTemplate : SQL update affected 1 rows 2026-01-16T20:36:33.461-06:00 DEBUG 26912 --- [insert-blob] [ main] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL query 2026-01-16T20:36:33.463-06:00 DEBUG 26912 --- [insert-blob] [ main] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL statement [SELECT blob_col from demo_blob_table WHERE id=?] 2026-01-16T20:36:33.463-06:00 TRACE 26912 --- [insert-blob] [ main] o.s.jdbc.core.StatementCreatorUtils : Setting SQL statement parameter value: column index 1, parameter value [4], value class [java.lang.Integer], SQL type unknown 2026-01-16T20:36:33.473-06:00 DEBUG 26912 --- [insert-blob] [ main] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL update 2026-01-16T20:36:33.473-06:00 DEBUG 26912 --- [insert-blob] [ main] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL statement 2026-01-16T20:36:33.473-06:00 TRACE 26912 --- [insert-blob] [ main] o.s.jdbc.core.JdbcTemplate : SQL update affected 1 rows 2026-01-16T20:36:33.477-06:00 DEBUG 26912 --- [insert-blob] [ main] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL query 2026-01-16T20:36:33.477-06:00 DEBUG 26912 --- [insert-blob] [ main] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL statement [SELECT blob_col from demo_blob_table WHERE id=?] 2026-01-16T20:36:33.477-06:00 TRACE 26912 --- [insert-blob] [ main] o.s.jdbc.core.StatementCreatorUtils : Setting SQL statement parameter value: column index 1, parameter value [3], value class [java.lang.Integer], SQL type unknown 2026-01-16T20:36:33.480-06:00 DEBUG 26912 --- [insert-blob] [ main] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL update 2026-01-16T20:36:33.480-06:00 DEBUG 26912 --- [insert-blob] [ main] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL statement [INSERT INTO demo_blob_table(id, content_type, blob_col) VALUES (?,?,?)] 2026-01-16T20:36:33.480-06:00 TRACE 26912 --- [insert-blob] [ main] o.s.jdbc.core.StatementCreatorUtils : Setting SQL statement parameter value: column index 1, parameter value [1], value class [java.lang.Integer], SQL type unknown 2026-01-16T20:36:33.480-06:00 TRACE 26912 --- [insert-blob] [ main] o.s.jdbc.core.StatementCreatorUtils : Setting SQL statement parameter value: column index 2, parameter value [txt], value class [java.lang.String], SQL type unknown 2026-01-16T20:36:33.480-06:00 TRACE 26912 --- [insert-blob] [ main] o.s.jdbc.core.StatementCreatorUtils : Setting SQL statement parameter value: column index 3, parameter value [[B@c8ed958], value class [[B], SQL type unknown 2026-01-16T20:36:33.481-06:00 TRACE 26912 --- [insert-blob] [ main] o.s.jdbc.core.JdbcTemplate : SQL update affected 1 rows 2026-01-16T20:36:33.482-06:00 DEBUG 26912 --- [insert-blob] [ main] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL query 2026-01-16T20:36:33.482-06:00 DEBUG 26912 --- [insert-blob] [ main] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL statement [SELECT blob_col from demo_blob_table WHERE id=?] 2026-01-16T20:36:33.482-06:00 TRACE 26912 --- [insert-blob] [ main] o.s.jdbc.core.StatementCreatorUtils : Setting SQL statement parameter value: column index 1, parameter value [1], value class [java.lang.Integer], SQL type unknown 2026-01-16T20:36:33.484-06:00 DEBUG 26912 --- [insert-blob] [ main] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL statement [SELECT blob_col from demo_blob_table WHERE id=?] 2026-01-16T20:36:33.484-06:00 TRACE 26912 --- [insert-blob] [ main] o.s.jdbc.core.StatementCreatorUtils : Setting SQL statement parameter value: column index 1, parameter value [1], value class [java.lang.Integer], SQL type unknown 2026-01-16T20:36:33.489-06:00 DEBUG 26912 --- [insert-blob] [ main] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL query 2026-01-16T20:36:33.489-06:00 DEBUG 26912 --- [insert-blob] [ main] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL statement [SELECT blob_col from demo_blob_table WHERE id=?] 2026-01-16T20:36:33.489-06:00 TRACE 26912 --- [insert-blob] [ main] o.s.jdbc.core.StatementCreatorUtils : Setting SQL statement parameter value: column index 1, parameter value [1], value class [java.lang.Integer], SQL type unknown 2026-01-16T20:36:33.490-06:00 DEBUG 26912 --- [insert-blob] [ main] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL update 2026-01-16T20:36:33.490-06:00 DEBUG 26912 --- [insert-blob] [ main] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL statement [UPDATE demo_blob_table set blob_col=? WHERE id=?] 2026-01-16T20:36:33.491-06:00 TRACE 26912 --- [insert-blob] [ main] o.s.jdbc.core.JdbcTemplate : SQL update affected 1 rows 2026-01-16T20:36:33.492-06:00 DEBUG 26912 --- [insert-blob] [ main] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL query 2026-01-16T20:36:33.492-06:00 DEBUG 26912 --- [insert-blob] [ main] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL statement [SELECT blob_col from demo_blob_table WHERE id=?] 2026-01-16T20:36:33.492-06:00 TRACE 26912 --- [insert-blob] [ main] o.s.jdbc.core.StatementCreatorUtils : Setting SQL statement parameter value: column index 1, parameter value [1], value class [java.lang.Integer], SQL type unknown 2026-01-16T20:36:33.495-06:00 DEBUG 26912 --- [insert-blob] [ main] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL update 2026-01-16T20:36:33.495-06:00 DEBUG 26912 --- [insert-blob] [ main] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL statement 2026-01-16T20:36:33.496-06:00 TRACE 26912 --- [insert-blob] [ main] o.s.jdbc.core.JdbcTemplate : SQL update affected 1 rows 2026-01-16T20:36:33.496-06:00 DEBUG 26912 --- [insert-blob] [ main] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL query 2026-01-16T20:36:33.496-06:00 DEBUG 26912 --- [insert-blob] [ main] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL statement [SELECT blob_col from demo_blob_table WHERE id=?] 2026-01-16T20:36:33.497-06:00 TRACE 26912 --- [insert-blob] [ main] o.s.jdbc.core.StatementCreatorUtils : Setting SQL statement parameter value: column index 1, parameter value [2], value class [java.lang.Integer], SQL type unknown 2026-01-16T20:36:33.505-06:00 INFO 26912 --- [insert-blob] [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default' 2026-01-16T20:36:33.507-06:00 INFO 26912 --- [insert-blob] [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated... 2026-01-16T20:36:33.512-06:00 INFO 26912 --- [insert-blob] [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
6. Conclusion
In this example, I demonstrated several ways to insert, update, and select a BLOB field in the PostgreSQL database.
- PreparedStatement.setBytes: sets the designated parameter to the given Java array of bytes. It’s good when BLOB data is less than 1 MB.
- PreparedStatement.setBinaryStream: sets the designated parameter to the given input stream. It’s good when BLOB data is greater than 1 MB and less than 1 GB.
- SqlParameterValue(Types.VARBINARY): sets an SQL parameter value for BLOB to avoid JDBC driver issues.
- JdbcTemplate.queryForStream: read result as Stream for BLOB for better memory management.
7. Download
This was an example of inserting BLOB fields via Spring JdbcTemplate.
You can download the full source code of this example here: Inserting BLOB Using Spring JdbcTemplate Example






