[SPRING] 최대 절전 모드를 사용하여 Oracle에서 LARGE BLOB (> 100MB)를 유지하는 방법
SPRING최대 절전 모드를 사용하여 Oracle에서 LARGE BLOB (> 100MB)를 유지하는 방법
BLOB 컬럼을 사용하여 오라클 데이터베이스에 큰 이미지 (> 100MB, 대부분 TIFF 포맷)를 삽입하는 방법을 찾기 위해 고심하고 있습니다.
나는이 문제에 대한 해답을 찾지 못한 채 웹이나 심지어 StackOverflow에서 철저히 조사했다. 우선, 문제는 ... 관련 코드 (Java 클래스 / 구성)에 대한 짧은 섹션, 마침내 제 3 절에서는 이미지 지속성을 테스트하기 위해 작성한 junit 테스트를 보여줍니다 (내 junit 동안 오류가 발생 함). 테스트 실행)
java.lang.OutOfMemoryError : 하이버 네이트를 사용하여 Java 힙 공간 오류가 발생하고 매우 큰 이미지 / 문서를 유지하려고합니다.
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을 사용한다.
두 가지 도메인 클래스가 일대 다 방식으로 매핑되었습니다. DocumentVersion에는 많은 DocumentData가 있으며, 각각은 동일한 DocumentVersion에 대해 다른 2 진 컨텐츠를 나타낼 수 있습니다.
관련 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;
}
Spring과 Hibernate 구성의 주요 매개 변수는 다음과 같습니다.
<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;
}
다음 단위 테스트를 실행하면 앞에서 설명한 오류가 발생했습니다 (java.lang.OutOfMemoryError : Java 힙 공간).
@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());
}
동일한 코드가 PostreSQL 9 설치에 적용됩니다. 이미지가 데이터베이스에 기록됩니다. 내 코드를 디버깅, 나는 PostgreSQL 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)
이 문제로 인한 오류입니까? 누구든지이 문제에 대한 통찰력을 줄 수 있습니까?
모두에게 감사드립니다.
덕분에 내 질문에 대한 제안을 받았다. 나는 PostgreSQL 용과 Oracle 용의 두 가지 다른 jdbc 드라이버를 사용하여 내 코드의 메모리 사용량을 보여주기 위해 간단한 테스트를 시도했다. 테스트 설정 :
이 첫 번째 테스트에서 Oracle과 Postgres 모두 테스트를 통과했습니다 (이것은 큰 뉴스입니다). 파일의 크기는 사용 가능한 JVM 힙 크기의 1/3입니다. 여기에 JVM 메모리 사용 그림이 있습니다.
테스팅 Oracle, 512MB 힙 크기, 150MB 파일
PostgreSQL 테스트, 512MB 힙 크기, 150MB 파일
이 두 번째 테스트에서는 Postgres 만 테스트를 통과했으며 Oracle은 테스트를 통과하지 못했습니다. 이 파일의 크기는 사용 가능한 JVM 힙 공간 크기와 매우 비슷합니다. 여기에 JVM 메모리 사용 그림이 있습니다.
Oracle 테스트, 512MB 힙 크기, 485MB 파일
PostgreSQL 테스트, 512MB 힙 크기, 485MB 파일
PostgreSQL 드라이버는 특정 임계 값을 초과하지 않고 메모리를 처리하는 반면 Oracle 드라이버는 매우 다르게 동작하는 것으로 보입니다.
오라클 jdbc 드라이버가 사용 가능한 힙 공간 근처의 크기로 파일을 사용할 때 오류 (동일한 java.lang.OutOfMemoryError : Java 힙 공간)로 안내하는 이유를 솔직히 설명 할 수 없습니다.
저에게 더 많은 통찰력을 줄 수있는 사람이 있습니까? 도와 줘서 고마워. :)
해결법
-
==============================
1."blob"형식을 사용하여 매핑하려고 시도 할 때와 같은 문제가 발생했습니다. 다음은 최대 절전 모드 사이트에서 만든 게시물 링크입니다 : https://forum.hibernate.org/viewtopic.php?p=2452481#p2452481
"blob"형식을 사용하여 매핑하려고 시도 할 때와 같은 문제가 발생했습니다. 다음은 최대 절전 모드 사이트에서 만든 게시물 링크입니다 : https://forum.hibernate.org/viewtopic.php?p=2452481#p2452481
최대 절전 모드 3.6.9 오라클 드라이버 11.2.0.2.0 Oracle Database 11.2.0.2.0
문제를 해결하기 위해 Blob 용 사용자 정의 UserType을 사용하는 코드를 사용하여 반환 유형을 java.sql.Blob로했습니다.
다음은이 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); } }
-
==============================
2.개인적으로 최대 절전 모드를 사용하여 Oracle BLOB 컬럼에 최대 200MB의 파일을 저장하므로 작동하는지 확인할 수 있습니다. 그래서...
개인적으로 최대 절전 모드를 사용하여 Oracle BLOB 컬럼에 최대 200MB의 파일을 저장하므로 작동하는지 확인할 수 있습니다. 그래서...
최신 버전의 Oracle JDBC 드라이버를 사용해보십시오. 스트림 대신 바이트 배열을 사용하는 이러한 동작은 시간이 지남에 따라 조금씩 변경되었습니다. 그리고 드라이버는 이전 버전과 호환됩니다. 그게 당신 문제를 해결할 지 모르겠지만, 저에게는 효과가 있습니다. 또한 org.hibernate.dialect.Oracle10gDialect로 전환해야합니다 - oracle.jdbc.ddb 패키지를 사용하는 것을 회수하고 oracle.jdbc를 사용합니다 - 도움이 될 수도 있습니다.
-
==============================
3.나는 오라클과 Hibernate에서 같은 문제가있을 때이 질문을 발견했다. 문제는 Hibernate blob 처리에 있습니다. 사용중인 방언에 따라 BLOB를 메모리에 복사하는 것 같습니다. 일부 데이터베이스 / 드라이버에서 필요하기 때문에 그렇게하는 것 같습니다. 그러나 오라클의 경우이 동작이 필요하지는 않습니다.
나는 오라클과 Hibernate에서 같은 문제가있을 때이 질문을 발견했다. 문제는 Hibernate blob 처리에 있습니다. 사용중인 방언에 따라 BLOB를 메모리에 복사하는 것 같습니다. 일부 데이터베이스 / 드라이버에서 필요하기 때문에 그렇게하는 것 같습니다. 그러나 오라클의 경우이 동작이 필요하지는 않습니다.
수정은 매우 간단합니다.이 코드가 포함 된 사용자 정의 OracleDialect를 작성하십시오.
public class Oracle10DialectWithoutInputStreamToInsertBlob extends Oracle10gDialect { public boolean useInputStreamToInsertBlob() { return false; } }
다음으로이 Dialect를 사용하도록 세션 팩토리를 구성해야합니다. 필자는 Oracle 11g에 대한 ojdbc6-11.2.0.1.0 드라이버를 사용하여 테스트 한 결과, 메모리 소비와 관련된 문제를 해결할 수 있음을 확인했습니다.
여러분 중 일부가 다른 Oracle 데이터베이스 및 / 또는 다른 Oracle 드라이버를 사용하여이 작업을 시도 할 경우 작동하는 경우 듣고 싶습니다. 몇가지 설정으로 작동한다면, 나는 Hibernate 팀에게 pull 요청을 보낼 것이다.
-
==============================
4.그것은 최선의 해결책은 아니지만 Java가 -Xmx parametr로 더 많은 메모리를 사용하도록 허용 할 수 있습니다
그것은 최선의 해결책은 아니지만 Java가 -Xmx parametr로 더 많은 메모리를 사용하도록 허용 할 수 있습니다
편집하다: 문제를 깊이있게 분석하고 JConsole을 사용해보십시오. 그것은 당신이 메모리 부하를 볼 수 있습니다.
Postgres를 사용해도로드 된 드라이버가 조금 더 적은 메모리를 사용하기 때문에 힙 크기 제한에 가깝지만 넘어갈 수는 없습니다.
기본 설정으로 heam size 제한은 실제 메모리의 약 절반입니다. 얼마나 큰 얼룩을 포스트 그레스에 저장할 수 있는지보십시오.
-
==============================
5.세션 팩토리에서 oracle OracleLobHandler에 대한 LobHandler 및 해당 버전을 정의하려고 시도 했습니까?
세션 팩토리에서 oracle 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임을 깨달았다.
from https://stackoverflow.com/questions/9253323/how-to-persist-large-blobs-100mb-in-oracle-using-hibernate by cc-by-sa and MIT license
'SPRING' 카테고리의 다른 글
[SPRING] 서비스 계층이 정말로 필요합니까? (0) | 2019.01.11 |
---|---|
[SPRING] 좋은 아이디어 로깅을 위해 Spring AOP를 사용하고 있습니까? (0) | 2019.01.11 |
[SPRING] Spring - 파일로서 응답을 다운로드한다. (0) | 2019.01.11 |
[SPRING] Tomcat 5.5에서 web-app를 배포 할 때 listenerStart 오류를 해결하는 방법은 무엇입니까? (0) | 2019.01.11 |
[SPRING] 메소드에서 Spring @ Transactional 어노테이션에 대한 설명 (0) | 2019.01.11 |