2

I'm trying to lazily fetch single byte[] content java property using Hibernate under Spring Boot, accessing PostgreSQL database. So I pulled together testing app for testing different solutions. One of them required me to use @Lob annotation on said property, so I did. Now reading entity from the database leads to very curious error, precisely:

Bad value for type long : \x454545454545445455

The value \x45... is value of bytea column not the bigint one, why is it trying to force it into the long even though it's wrong column? Why annotation on one column somehow affects another one? As for fix, removing @Lob seems to work (at least in my stack) but the problem remains unexplained to me and I would like to know what is going rather than just blindly moving on. Is it bug or I am misunderstanding something completely?

Entity:

@Entity
@Table(name = "blobentity")
public class BlobEntity {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @Lob //this annotation breaks code
  @Column(name = "content")
  @Basic(fetch = FetchType.LAZY)
  private byte[] content;

  @Column(name = "name")
  private String name;
  
  //getters/setters
}

Repository:

@Repository
public interface BlobRepo extends JpaRepository<BlobEntity, Long> {

}

Calling code:

  @Autowired
  BlobRepo blobrepo;

  @GetMapping("lazyBlob")
  public String blob () {

    var t = blobrepo.findAll().get(0);

    var name = t.getName();
    var dataAccessedIfLazy = t.getContent();

    return t.getName();
  }

Postgres DDL:

CREATE TABLE test.blobentity (
    id bigserial NOT NULL DEFAULT nextval('test.blobentity_id_seq'::regclass),
    "name" varchar NULL,
    "content" bytea NULL,
    CONSTRAINT blobentity_pk PRIMARY KEY (id)
);

Select result:

select result

Used version:

PostgreSQL 10.4; springframework.boot 2.4.2; hibernate version that comes with this spring boot version

Herioz
  • 81
  • 7

1 Answers1

2

The bytea type is inlined into the table whereas other types are chunked into a separate table which is called TOAST on PostgreSQL. To access these values, database have a concept often referred to as a LOB locator which essentially is just an id for doing the lookup. Some drivers/databases just work either way but others might need to match the actual physical representation. In your case, using @Lob is just wrong because AFAIK bytea is inlined up to a certain size and de-TOASTed i.e. materialized automatically behind the scenes if necessary. If you were using the varbinary/blob type or something like that, you would have to use @Lob as in that case, the main table only contains this LOB locator which is a long. The driver then knows when you ask for the value by using getBlob that it has to execute some select get_lob(?) query to retrieve the actual contents.

Christian Beikov
  • 15,141
  • 2
  • 32
  • 58