휴지 상태를 사용하여 Oracle에서 대용량 BLOB(100MB 이상)를 유지하는 방법
BLOB 열을 사용하여 Oracle 데이터베이스에 대용량 이미지(대부분 TIFF 형식)를 삽입하는 방법을 찾는 데 어려움을 겪고 있습니다.
웹을 통해 StackOverflow에서도 철저히 검색했지만 이 문제에 대한 해답을 찾지 못했습니다.
일단 문제는...그런 다음 관련 코드의 짧은 섹션(클래스/구성), 마지막으로 이미지 지속성을 테스트하기 위해 작성한 junit 테스트를 보여 주는 세 번째 섹션(junit 테스트 실행 중 오류 수신)
편집: 질문 끝에 JConsole을 사용한 테스트와 분석에 대해 설명하는 섹션을 추가했습니다.
문제
신신 an java.lang.OutOfMemoryError: Java heap space
휴지 상태를 사용하여 매우 큰 이미지/화면을 유지하려고 할 때 오류가 발생했습니다.
java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2786)
at java.io.ByteArrayOutputStream.toByteArray(ByteArrayOutputStream.java:133)
at org.hibernate.type.descriptor.java.DataHelper.extractBytes(DataHelper.java:190)
at org.hibernate.type.descriptor.java.BlobTypeDescriptor.unwrap(BlobTypeDescriptor.java:123)
at org.hibernate.type.descriptor.java.BlobTypeDescriptor.unwrap(BlobTypeDescriptor.java:47)
at org.hibernate.type.descriptor.sql.BlobTypeDescriptor$4$1.doBind(BlobTypeDescriptor.java:101)
at org.hibernate.type.descriptor.sql.BasicBinder.bind(BasicBinder.java:91)
at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:283)
at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:278)
at org.hibernate.type.AbstractSingleColumnStandardBasicType.nullSafeSet(AbstractSingleColumnStandardBasicType.java:89)
at org.hibernate.persister.entity.AbstractEntityPersister.dehydrate(AbstractEntityPersister.java:2184)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2430)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2874)
at org.hibernate.action.EntityInsertAction.execute(EntityInsertAction.java:79)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:273)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:265)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:184)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1216)
at it.paoloyx.blobcrud.manager.DocumentManagerTest.testInsertDocumentVersion(DocumentManagerTest.java:929)
코드(도메인 객체, 저장소 클래스, 구성)
다음은 제가 사용하고 있는 기술 스택입니다(DB에서 비즈니스 로직 계층까지).나는 JDK6를 쓴다.
- Oracle Database 10g Enterprise Edition 릴리즈 10.2.0.4.0 - Prod
- ojdbc6.jar (11.2.0.3 릴리즈용)
- 휴지 상태 4.0.1 파이널
- 봄 3.1.GA 릴리즈
1대 다로 매핑된 두 개의 도메인 클래스가 있습니다. a.DocumentVersion
DocumentData
에 다른 수 있습니다.DocumentVersion
.
★★★★★★★★★★★★★★★★★★★★의 관련 발췌DocumentVersion
링크:
@Entity
@Table(name = "DOCUMENT_VERSION")
public class DocumentVersion implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private Set<DocumentData> otherDocumentContents = new HashSet<DocumentData>(0);
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
@Column(name = "DOV_ID", nullable = false)
public Long getId() {
return id;
}
@OneToMany
@Cascade({ CascadeType.SAVE_UPDATE })
@JoinColumn(name = "DOD_DOCUMENT_VERSION")
public Set<DocumentData> getOtherDocumentContents() {
return otherDocumentContents;
}
★★★★★★★★★★★★★★★★★★★★의 관련 발췌DocumentData
링크:
@Entity
@Table(name = "DOCUMENT_DATA")
public class DocumentData {
private Long id;
/**
* The binary content (java.sql.Blob)
*/
private Blob binaryContent;
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
@Column(name = "DOD_ID", nullable = false)
public Long getId() {
return id;
}
@Lob
@Column(name = "DOD_CONTENT")
public Blob getBinaryContent() {
return binaryContent;
}
스프링 및 휴지 상태 설정의 주요 매개 변수는 다음과 같습니다.
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="it.paoloyx.blobcrud.model" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
<prop key="hibernate.jdbc.batch_size">0</prop>
<prop key="hibernate.jdbc.use_streams_for_binary">true</prop>
</props>
</property>
</bean>
<bean class="org.springframework.orm.hibernate4.HibernateTransactionManager"
id="transactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
데이터 소스 정의:
<bean class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close" id="dataSource">
<property name="driverClassName" value="${database.driverClassName}" />
<property name="url" value="${database.url}" />
<property name="username" value="${database.username}" />
<property name="password" value="${database.password}" />
<property name="testOnBorrow" value="true" />
<property name="testOnReturn" value="true" />
<property name="testWhileIdle" value="true" />
<property name="timeBetweenEvictionRunsMillis" value="1800000" />
<property name="numTestsPerEvictionRun" value="3" />
<property name="minEvictableIdleTimeMillis" value="1800000" />
<property name="validationQuery" value="${database.validationQuery}" />
</bean>
여기서 속성을 가져옵니다.
database.driverClassName=oracle.jdbc.OracleDriver
database.url=jdbc:oracle:thin:@localhost:1521:devdb
database.username=blobcrud
database.password=blobcrud
database.validationQuery=SELECT 1 from dual
저장소 클래스에 위임하는 서비스 클래스가 있습니다.
@Transactional
public class DocumentManagerImpl implements DocumentManager {
DocumentVersionDao documentVersionDao;
public void setDocumentVersionDao(DocumentVersionDao documentVersionDao) {
this.documentVersionDao = documentVersionDao;
}
이제 저장소 클래스에서 관련 추출을 수행합니다.
public class DocumentVersionDaoHibernate implements DocumentVersionDao {
@Autowired
private SessionFactory sessionFactory;
@Override
public DocumentVersion saveOrUpdate(DocumentVersion record) {
this.sessionFactory.getCurrentSession().saveOrUpdate(record);
return record;
}
에러의 원인이 되는 JUnit 테스트
단위 앞서 오류 음음 、 음 ( 、 음 if 、 기((((((((((((((((((((( the)가 나타납니다.java.lang.OutOfMemoryError: Java heap space
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath*:META-INF/spring/applicationContext*.xml" })
@Transactional
public class DocumentManagerTest {
@Autowired
protected DocumentVersionDao documentVersionDao;
@Autowired
protected SessionFactory sessionFactory;
@Test
public void testInsertDocumentVersion() throws SQLException {
// Original mock document content
DocumentData dod = new DocumentData();
// image.tiff is approx. 120MB
File veryBigFile = new File("/Users/paoloyx/Desktop/image.tiff");
try {
Session session = this.sessionFactory.getCurrentSession();
InputStream inStream = FileUtils.openInputStream(veryBigFile);
Blob blob = Hibernate.getLobCreator(session).createBlob(inStream, veryBigFile.length());
dod.setBinaryContent(blob);
} catch (IOException e) {
e.printStackTrace();
dod.setBinaryContent(null);
}
// Save a document version linked to previous document contents
DocumentVersion dov = new DocumentVersion();
dov.getOtherDocumentContents().add(dod);
documentVersionDao.saveOrUpdate(dov);
this.sessionFactory.getCurrentSession().flush();
// Clear session, then try retrieval
this.sessionFactory.getCurrentSession().clear();
DocumentVersion dbDov = documentVersionDao.findByPK(insertedId);
Assert.assertNotNull("Il document version ritornato per l'id " + insertedId + " è nullo", dbDov);
Assert.assertNotNull("Il document version recuperato non ha associato contenuti aggiuntivi", dbDov.getOtherDocumentContents());
Assert.assertEquals("Il numero di contenuti secondari non corrisponde con quello salvato", 1, dbDov.getOtherDocumentContents().size());
}
Postre에 대해서도 같은 코드가 동작합니다.SQL 9 설치이미지가 데이터베이스에 기록되고 있습니다.내 코드를 디버깅한 결과, 포스트그어가SQL jdbc 드라이버는 버퍼링된 출력 스트림을 사용하여 데이터베이스에 씁니다.Oracle OJDBC 드라이버는 이 모든 것을 한꺼번에 할당하려고 합니다.byte[]
이미지를 나타냅니다.
오류 스택:
java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2786)
at java.io.ByteArrayOutputStream.toByteArray(ByteArrayOutputStream.java:133)
이 동작에 의한 에러입니까?누가 이 문제에 대해 저에게 통찰력을 줄 수 있나요?
여러분 감사합니다.
JConsole을 사용한 메모리 테스트
질문에 대한 제안 덕분에 Postgre용 두 개의 다른 jdbc 드라이버를 사용하여 코드의 메모리 사용량을 보여주는 간단한 테스트를 시도했습니다.SQL과 Oracle용 SQL입니다.테스트 셋업:
- 이 테스트는 이전 섹션에서 설명한 JUnit 테스트를 사용하여 수행되었습니다.
- 매개 변수 -Xmx512를 사용하여 JVM 힙 크기가 512MB로 설정되었습니다.MB
- Oracle 데이터베이스에는 ojdbc6.jar 드라이버를 사용했습니다.
- Postgres 데이터베이스에서는 9.0-801.jdbc3 드라이버(Maven 경유)를 사용하고 있습니다.
약 150MB의 파일을 사용한 첫 번째 테스트
이 첫 번째 테스트에서는 Oracle과 Postgres가 모두 테스트에 합격했습니다(이것은 큰 뉴스입니다).파일 크기는 사용 가능한 JVM 힙 크기의 1/3입니다.다음은 JVM 메모리 소비량 그림입니다.
파일 Oracle 파파 、 512MB 、 150MB 파파
Testing 는 SQL 파일SQL, 512MB 파일SQL, 150MB 파일SQL, 512MB 파일SQL, 150MB 파일SQL 입니다.
두 번째 테스트(약 485MB 파일 포함
이 두 번째 테스트에서는 Postgres만 테스트를 통과했고 Oracle은 테스트를 통과하지 못했습니다.파일 크기는 사용 가능한 JVM 힙 공간의 크기에 매우 가깝습니다.다음은 JVM 메모리 소비량 그림입니다.
Oracle 파파 、 512MB 、 485MB 파파
Testing 는 SQL 파일SQL, 512MB 파일SQL, 485MB 파일SQL, 485MB 파일SQL, 512MB 파일SQL, 485MB 파일SQL을 하고 있습니다.
테스트 분석:
Postgre는 지금SQL 드라이버는 특정 임계값을 초과하지 않고 메모리를 처리하지만 Oracle 드라이버는 매우 다르게 동작합니다.
이유를 설명할 수 없습니다(동일한 Oracle jdbc 드라이버java.lang.OutOfMemoryError: Java heap space
사용 가능한 힙 영역 근처에서 파일 크기를 사용하는 경우).
저에게 더 많은 통찰력을 줄 수 있는 사람이 있나요?도와주셔서 감사합니다:)
저도 당신과 같은 문제로 "blob" 활자를 사용하여 지도를 작성하려고 했습니다.다음은 휴지 상태 사이트에 작성한 게시물 링크입니다.https://forum.hibernate.org/viewtopic.php?p=2452481#p2452481
3 상 33.6.9
11.2.0 Oracle 11.2.0
11.2. 데이터베이스 11.2.0.2.0
BLOB용 커스텀 UserType을 가진 코드를 사용하여 문제를 수정하기 위해 반환 유형을 java.sql로 설정했습니다.블럽.
이 UserType의 주요 방법 구현은 다음과 같습니다.
public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
Blob blob = rs.getBlob(names[0]);
if (blob == null)
return null;
return blob;
}
public void nullSafeSet(PreparedStatement st, Object value, int index)
throws HibernateException, SQLException {
if (value == null) {
st.setNull(index, sqlTypes()[0]);
}
else {
InputStream in = null;
OutputStream out = null;
// oracle.sql.BLOB
BLOB tempBlob = BLOB.createTemporary(st.getConnection(), true, BLOB.DURATION_SESSION);
tempBlob.open(BLOB.MODE_READWRITE);
out = tempBlob.getBinaryOutputStream();
Blob valueAsBlob = (Blob) value;
in = valueAsBlob.getBinaryStream();
StreamUtil.toOutput(in, out);
out.flush();
StreamUtil.close(out);
tempBlob.close();
st.setBlob(index, tempBlob);
StreamUtil.close(in);
}
}
개인적으로 Hibernate를 사용하여 Oracle BLOB 열에 최대 200MB의 파일을 저장하므로 확실히 작동합니다.그래서...
Oracle JDBC 드라이버의 최신 버전을 사용해 보십시오.스트림 대신 바이트 배열을 사용하는 이 동작은 시간이 지남에 따라 약간 변경된 것으로 보입니다.드라이버는 하위 호환성이 있습니다.그게 당신 문제를 해결할 수 있을지는 모르겠지만, 내겐 효과가 있어요.또한 org.hibernate.dialect로 전환해야 합니다.Oracle10gDialect - oracle.jdbc.driver 패키지의 사용을 oracle.jdbc를 위해 철회합니다.또한 도움이 될 수도 있습니다.
Oracle과 Hibernate에서 같은 문제가 발생했을 때 이 질문을 발견했습니다.휴지 상태의 블럽 처리로 문제가 발생.사용 중인 사투리에 따라 blob이 메모리에 복사되는 것 같습니다.일부 데이터베이스/드라이버에서 필요하기 때문에 그런 것 같습니다.단, Oracle에서는 이 동작이 필요하지 않은 것 같습니다.
수정은 매우 간단합니다.다음 코드를 포함하는 커스텀 OracleDialect를 작성하기만 하면 됩니다.
public class Oracle10DialectWithoutInputStreamToInsertBlob extends Oracle10gDialect {
public boolean useInputStreamToInsertBlob() {
return false;
}
}
다음으로 이 사투리를 사용하도록 세션팩토리를 설정해야 합니다.Oracle 11g용 ojdbc6-11.2.0.1.0 드라이버로 테스트한 결과 메모리 소비 문제가 해결되었습니다.
다른 Oracle 데이터베이스 및/또는 다른 Oracle 드라이버를 사용하여 이 기능을 시도하시는 분이 있다면, 이 기능을 사용할 수 있는지 알고 싶습니다.몇 가지 구성으로 동작하는 경우는, Hibernate 팀에 풀 요구를 송신합니다.
최적의 솔루션은 아니지만 -Xmx 파라미터를 사용하여 Java에서 더 많은 메모리를 사용할 수 있습니다.
편집: 문제를 좀 더 자세히 분석하여 JConsole을 사용해 보십시오.메모리 부하를 확인할 수 있습니다.
Postgres를 사용하더라도 힙 크기 제한이 필요하지만 로드된 드라이버가 사용하는 메모리가 조금 적기 때문에 힙 크기 제한을 넘지 않을 수 있습니다.
디폴트 설정에서는, 헴 사이즈 제한은 물리 메모리의 약 절반입니다.포스트그레에 얼마나 큰 방울을 저장할 수 있는지 시험해 보세요.
세션 팩토리에서 OracleLobHandler용 LobHandler 및 해당 버전을 정의해 본 적이 있습니까?
다음은 예를 제시하겠습니다.
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="oracleDocDataSource"/>
<property name="annotatedClasses">
<list>
...
</list>
</property>
<property name="lobHandler">
<bean class="org.springframework.jdbc.support.lob.OracleLobHandler">
<property name="nativeJdbcExtractor">
<bean class="org.springframework.jdbc.support.nativejdbc.WebSphereNativeJdbcExtractor"/>
</property>
</bean>
</property>
</bean>
갱신하다
그 연설이 겨울잠 4에 관한 것이라는 걸 방금 깨달았어.
언급URL : https://stackoverflow.com/questions/9253323/how-to-persist-large-blobs-100mb-in-oracle-using-hibernate
'programing' 카테고리의 다른 글
특정 이름의 메뉴가 존재하는지 확인하는 방법 (0) | 2023.03.23 |
---|---|
패키지를 설치하는 동안 npm ERR! git dep 준비에 실패했습니다.json (0) | 2023.03.23 |
jQuery에서 항목을 배열에 추가하려면 어떻게 해야 합니까? (0) | 2023.03.23 |
WebSockets를 사용할 수 있는데 AJAX를 사용하는 이유는 무엇입니까? (0) | 2023.03.23 |
AJAX & jQuery에서 django 폼을 게시하는 방법 (0) | 2023.03.23 |