Spring-data-jpa storing blob

后端 未结 6 918
梦如初夏
梦如初夏 2021-02-04 14:17

What is \"best\" or canonical way to store entity with blob using spring-data-jpa?

@Entity
public class Entity {
  @Id
  private Long id;
  @Lob()
  private Blob         


        
相关标签:
6条回答
  • 2021-02-04 14:51

    You can do it with a single statement (4 below) using Hibernate.getLobCreator and passing the session that EntityManager can unwrap to you:

    // 1. Get entity manager and repository
    EntityManager em = .... // get/inject someway the EntityManager
    EntityRepository repository = ...// get/inject your Entity repository
    
    // 2. Instantiate your Entity
    Entity entity = new Entity();
    
    // 3. Get an input stream (you shall also know its length)
    File inFile = new File("/somepath/somefile");
    InputStream inStream = new FileInputStream(inFile);
    
    // 4. Now copy to the BLOB
    Blob blob =
      Hibernate.getLobCreator(em.unwrap(Session.class))
               .createBlob(inStream, inFile.length());
    
    // 5. And finally save the BLOB
    entity.setBlob(blob);
    entityRepository.save(f);
    
    0 讨论(0)
  • 2021-02-04 14:53

    TL; DR

    You can see sample project on my github. The project shows how you stream data to/from database.

    Problem

    All advices about mapping the @Lob as byte[] defeats (IMO) the main advantage of blobs - streaming. With byte[] everything gets loaded in memory. May be ok but if you go with LargeObject you likely want to stream.

    Solution

    Mapping

    @Entity
    public class MyEntity {
    
        @Lob
        private Blob data;
    
        ...
    
    }
    

    Configuration

    Expose hibernate SessionFactory and CurrentSession so you can get hold of the LobCreator. In application.properties:

    spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext
    

    Expose session factory as bean:

    @Bean // Need to expose SessionFactory to be able to work with BLOBs
    public SessionFactory sessionFactory(HibernateEntityManagerFactory hemf) {
        return hemf.getSessionFactory();
    }
    

    Create blob

    @Service
    public class LobHelper {
    
        private final SessionFactory sessionFactory;
    
        @Autowired
        public LobHelper(SessionFactory sessionFactory) {
            this.sessionFactory = sessionFactory;
        }
    
        public Blob createBlob(InputStream content, long size) {
            return sessionFactory.getCurrentSession().getLobHelper().createBlob(content, size);
        }
    
        public Clob createClob(InputStream content, long size, Charset charset) {
            return sessionFactory.getCurrentSession().getLobHelper().createClob(new InputStreamReader(content, charset), size);
        }
    }
    

    Also - as pointed out in comments - as long as you work with the @Blob including the stream you get you need to be within transaction. Just mark the working part @Transactional.

    0 讨论(0)
  • 2021-02-04 15:00

    Autowire your repository interface and call the save method passing your entity object.

    I have a similar setup which works pretty well:

    @Autowired
    Repository repository;
    
    repository.save(entity);
    
    @Entity
    @Table(name = "something")
    public class Message {
    
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Long id;
    
        @Lob
        @Column
        private byte[] data;
    
    0 讨论(0)
  • 2021-02-04 15:06

    You can also create Blob right from DataSource:

    @Component
    public class LobHelper {
    
        private final DataSource ds;
    
        public LobHelper(@Autowired DataSource ds){
             this.ds = ds;
        }
    
        public Blob createBlob(byte[] content) {
            try (Connection conn = ds.getConnection()) {
                Blob b = conn.createBlob();
                try (OutputStream os = b.setBinaryStream(1);
                     InputStream is = new ByteArrayInputStream(content)) {
                    byte[] buffer = new byte[500000];
                    int len;
                    while ((len = is.read(buffer)) > 0) {
                        os.write(buffer, 0, len);
                    }
                    return b;
                }
            } catch (Exception e) {
                log.error("Error while creating blob.", e);
            }
            return null;
        }
    
    }
    
    0 讨论(0)
  • 2021-02-04 15:08

    Spring Data does not handle BLOBs but Spring Content does. Specifically, Spring Content JPA stores content as BLOBs in the database and associates that content with an Entity through annotations.

    pom.xml

       <!-- Java API -->
       <dependency>
          <groupId>com.github.paulcwarren</groupId>
          <artifactId>spring-content-jpa-boot-starter</artifactId>
          <version>0.0.11</version>
       </dependency>
       <!-- REST API -->
       <dependency>
          <groupId>com.github.paulcwarren</groupId>
          <artifactId>spring-content-rest-boot-starter</artifactId>
          <version>0.0.11</version>
       </dependency>
    

    Entity.java

    @Entity
    public class Entity {
       @Id
       @GeneratedValue
       private long id;
    
       @ContentId
       private String contentId;
    
       @ContentLength
       private long contentLength = 0L;
    
       // if you have rest endpoints
       @MimeType
       private String mimeType = "text/plain";
    

    DataContentStore.java

    @StoreRestResource(path="data")
    public interface DataContentStore extends ContentStore<Data, String> {
    }
    

    The advantage of this approach over the accepted answer is that the Developer doesn't need to worry about any of the boilerplate code (the "service" in the accepted answer). The BLOB is also exposed as Spring Resource giving a natural programming interface. Or can be automatically exported via a REST interface. But none of this requires any coding, on behalf of the Developer, besides java config and the store interface.

    0 讨论(0)
  • 2021-02-04 15:15

    I had a little problems to get current session factory like in answers above (got for example errors like: Could not obtain transaction-synchronized Session for current thread) . Finally (in Spring Boot application, currently 2.3.1.RELEASE version, Hibernate 5.4.1) i'm using approach like below and my problems has solved.

    @Component
    public class SomeService {
    
        /**
         * inject entity manager
         */
        @PersistenceContext 
        private EntityManager entityManager;
    
        @Transactional
        public void storeMethod(File file) {
           // ...
           FileInputStream in = new FileInputStream(file);
    
           Session session = entityManager.unwrap(Session.class);
           Blob blob = session.getLobHelper().createBlob(in, file.length());
           // ...
           entity.setData(blob);
           repo.save(entity);
        }
    }
    
    0 讨论(0)
提交回复
热议问题