使用spring-data-jpa存储带有blob的实体的“最佳”或标准方法是什么?
@Entity
public class Entity {
@Id
private Long id;
@Lob()
private Blob blob;
}
public interface Repository extends CrudRepository<Entity, Long> {
}
使用spring-data-jpa存储带有blob的实体的“最佳”或标准方法是什么?
@Entity
public class Entity {
@Id
private Long id;
@Lob()
private Blob blob;
}
public interface Repository extends CrudRepository<Entity, Long> {
}
你可以在我的 GitHub 上查看示例项目。该项目展示了如何将数据流传输到/从数据库。
所有有关将@Lob
映射为byte[]
的建议都不利于(在我看来) blobs 的主要优势 – 流式处理。使用byte[]
会将所有内容加载到内存中。虽然这可能没问题,但如果您使用大型对象,则可能需要流式处理。
@Entity
public class MyEntity {
@Lob
private Blob data;
...
}
暴露 Hibernate 的 SessionFactory 和 CurrentSession,以便您可以获取 LobCreator。在 application.properties 中:
spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext
将会话工厂公开为Bean:
@Bean // Need to expose SessionFactory to be able to work with BLOBs
public SessionFactory sessionFactory(HibernateEntityManagerFactory hemf) {
return hemf.getSessionFactory();
}
@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);
}
}
还有一点,正如评论中所指出的那样,只要你使用包含流的@Blob
,你需要在事务内工作。只需标记工作部分为@Transactional
即可。
自动装配您的仓库接口,并调用save方法传递您的实体对象。
我有一个类似的设置,它工作得非常好:
@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;
byte[]
会遇到 OOM(内存不足)问题,而使用 String
则不会? - MustafaSpring Data 不处理 BLOB ,但是 Spring Content 可以。具体来说,Spring Content JPA 将内容存储为 BLOB,并通过注释将该内容与实体关联到数据库中。
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> {
}
// 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);
您还可以直接从DataSource
创建Blob
:
@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;
}
}
我在获取当前会话工厂方面遇到了一些问题,就像上面的答案一样(例如出现错误:无法为当前线程获取事务同步会话
或没有进行中的事务
)。最终(在Spring Boot应用程序中,目前使用的是2.3.1.RELEASE版本,Hibernate 5.4.1),我采用了以下方法来解决我的问题。
@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);
}
}
LobHelper 可以是这样的:
@Service
public class LobHelper {
@PersistenceContext
private EntityManager entityManager;
public Blob createBlob(InputStream content, long size) {
return ((Session)entityManager).getLobHelper().createBlob(content, size);
}
// ...
}