我正在运行Oracle 11.2.0.3并且正在尝试创建一个可行的UserType来映射XMLType或SQLXML列。
Existing solutions found在线都存在两个问题:
XMLType值是LOB值,因此它们必须在Connection.close()之前是free(),否则它们将泄漏Java中的数据库资源和堆内存。
从这些列获取的XML值是连接对象;除非通过深层复制复制它们,否则它们会在连接关闭后消失。
因此,我在底部编写了这些类来存储XMLType对象 我的问题是 - 由于这些是LOB值,因此必须在事务提交之后释放,但在连接关闭之前 。有没有办法让Hibernate UserType执行此操作?暂时忽略这是一个SQLXML对象的事实 - 如果它是BLOB或CLOB,并且我有相同的要求(有人必须在提交后但在close()之前调用free()),我该怎么做它?
感谢您阅读所有这些......
package com.mycomp.types;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import oracle.xdb.XMLType;
import oracle.xml.binxml.BinXMLDecoder;
import oracle.xml.binxml.BinXMLException;
import oracle.xml.binxml.BinXMLStream;
import oracle.xml.jaxp.JXSAXTransformerFactory;
import oracle.xml.jaxp.JXTransformer;
import oracle.xml.parser.v2.XMLDOMImplementation;
import oracle.xml.parser.v2.XMLDocument;
import oracle.xml.scalable.InfosetReader;
import org.apache.commons.lang.ObjectUtils;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.usertype.UserType;
import org.w3c.dom.DOMException;
/**
* This class encapsulates the XMLDocument class into a database XMLType.
* It is used to allow Hibernate entities to use XMLDocument transparently
* for persistence as XMLTypes in an Oracle database.
*
* @author bmarke
*
*/
public class HibernateXMLType implements UserType
{
private static final String CAST_EXCEPTION_TEXT = " cannot be cast to a oracle.xml.parser.v2.XMLDocument.";
@Override
public int[] sqlTypes()
{
return new int[] { Types.SQLXML };
}
@Override
public Class<?> returnedClass()
{
return XMLDocument.class;
}
@Override
public boolean equals(Object x, Object y) throws HibernateException
{
if (x == y)
{
return true;
}
if (!(x instanceof XMLDocument && y instanceof XMLDocument))
{
throw new HibernateException(x.getClass().toString()
+ CAST_EXCEPTION_TEXT);
}
return ObjectUtils.equals(x, y);
}
@Override
public int hashCode(Object x) throws HibernateException
{
if (!(x instanceof XMLDocument))
{
throw new HibernateException(x.getClass().toString()
+ CAST_EXCEPTION_TEXT);
}
return x.hashCode();
}
@Override
public Object nullSafeGet(ResultSet rs, String[] names,
SessionImplementor session, Object owner)
throws HibernateException, SQLException
{
XMLType xmlData = (XMLType) rs.getSQLXML(names[0]);
XMLDocument doc = null;
XMLDocument toReturn = null;
BinXMLStream stream = null;
InfosetReader reader = null;
if (xmlData == null)
{
doc = null;
toReturn = null;
}
else
{
try
{
stream = xmlData.getBinXMLStream();
BinXMLDecoder decoder = stream.getDecoder();
reader = decoder.getReader();
XMLDOMImplementation domImpl = new XMLDOMImplementation();
domImpl.setAttribute(XMLDocument.SCALABLE_DOM, true);
domImpl.setAttribute(XMLDocument.ACCESS_MODE,
XMLDocument.UPDATEABLE);
doc = (XMLDocument) domImpl.createDocument(reader);
toReturn = (XMLDocument)deepCopy(doc);
}
catch (IllegalArgumentException e)
{
throw new HibernateException(e);
}
catch (DOMException e)
{
throw new HibernateException(e);
}
catch (BinXMLException e)
{
throw new HibernateException(e);
}
finally
{
if(doc != null)
{
doc.freeNode();
}
if(reader != null)
{
reader.close();
}
if(stream != null)
{
stream.close();
}
if(xmlData != null)
{
xmlData.close();
}
}
}
return toReturn;
}
@Override
public void nullSafeSet(PreparedStatement st, Object value, int index,
SessionImplementor session) throws HibernateException, SQLException
{
if( value == null )
{
st.setNull(index, Types.SQLXML);
}
else if( !(value instanceof XMLDocument) )
{
throw new HibernateException(value.getClass().toString()
+ CAST_EXCEPTION_TEXT);
}
else
{
XMLDocument xml = (XMLDocument) value;
XMLType xmlData = null;
try
{
xmlData = new XMLType(st.getConnection().getMetaData().getConnection(), xml);
st.setSQLXML(index, xmlData);
}
finally
{
if(xmlData != null)
{
xmlData.close();
}
}
}
}
@Override
public Object deepCopy(Object value) throws HibernateException
{
XMLDocument orig = (XMLDocument)value;
DOMResult result;
try
{
JXSAXTransformerFactory tfactory = new oracle.xml.jaxp.JXSAXTransformerFactory();
JXTransformer tx = (JXTransformer)tfactory.newTransformer();
DOMSource source = new DOMSource(orig);
result = new DOMResult();
tx.transform(source,result);
return (XMLDocument)result.getNode();
}
catch (Exception e)
{
throw new HibernateException(e);
}
}
@Override
public boolean isMutable()
{
return true;
}
@Override
public Serializable disassemble(Object value) throws HibernateException
{
XMLDocument doc = (XMLDocument) deepCopy(value);
return doc;
}
@Override
public Object assemble(Serializable cached, Object owner)
throws HibernateException
{
XMLDocument doc = (XMLDocument) deepCopy(cached);
return doc;
}
@Override
public Object replace(Object original, Object target, Object owner)
throws HibernateException
{
return deepCopy(original);
}
}
(是的,以上是特定于Oracle的...对于那些寻找与DBMS无关的类的人来说,它看起来像这样,但请注意警告,我还没有测试过它):
package com.mycomp.types;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLXML;
import java.sql.Types;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import org.apache.commons.lang.ObjectUtils;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.usertype.UserType;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
/**
* This class encapsulates the XMLDocument class into a database XMLType.
* It is used to allow Hibernate entities to use XMLDocument transparently
* for persistence as XMLTypes in an Oracle database.
*
* @author bmarke
*
*/
public class HibernateSQLXML implements UserType
{
private static final String CAST_EXCEPTION_TEXT = " cannot be cast to a oracle.xml.parser.v2.XMLDocument.";
@Override
public int[] sqlTypes()
{
return new int[] { Types.SQLXML };
}
@Override
public Class<?> returnedClass()
{
return SQLXML.class;
}
@Override
public boolean equals(Object x, Object y) throws HibernateException
{
if (x == y)
{
return true;
}
if (!(x instanceof SQLXML && y instanceof SQLXML))
{
throw new HibernateException(x.getClass().toString()
+ CAST_EXCEPTION_TEXT);
}
return ObjectUtils.equals(x, y);
}
@Override
public int hashCode(Object x) throws HibernateException
{
if (!(x instanceof SQLXML))
{
throw new HibernateException(x.getClass().toString()
+ CAST_EXCEPTION_TEXT);
}
return x.hashCode();
}
@Override
public Object nullSafeGet(ResultSet rs, String[] names,
SessionImplementor session, Object owner)
throws HibernateException, SQLException
{
SQLXML xmlData = rs.getSQLXML(names[0]);
Document toReturn = null;
if (xmlData == null)
{
toReturn = null;
}
else
{
try
{
DOMSource source = xmlData.getSource(DOMSource.class);
toReturn = (Document)deepCopy(source);
}
catch (IllegalArgumentException e)
{
throw new HibernateException(e);
}
catch (DOMException e)
{
throw new HibernateException(e);
}
finally
{
if(xmlData != null)
{
xmlData.free();
}
}
}
return toReturn;
}
@Override
public void nullSafeSet(PreparedStatement st, Object value, int index,
SessionImplementor session) throws HibernateException, SQLException
{
if( value == null )
{
st.setNull(index, Types.SQLXML);
}
else if( !(value instanceof Document) )
{
throw new HibernateException(value.getClass().toString()
+ CAST_EXCEPTION_TEXT);
}
else
{
Document xml = (Document) value;
SQLXML xmlData = null;
try
{
xmlData = st.getConnection().createSQLXML();
DOMResult res = xmlData.setResult(DOMResult.class);
res.setNode(xml);
st.setSQLXML(index, xmlData);
}
finally
{
if(xmlData != null)
{
xmlData.free();
}
}
}
}
public Object deepCopy(DOMSource orig) throws HibernateException
{
DOMResult result;
try
{
TransformerFactory tfactory = TransformerFactory.newInstance();
Transformer tx = tfactory.newTransformer();
result = new DOMResult();
tx.transform(orig,result);
return (Document)result.getNode();
}
catch (Exception e)
{
throw new HibernateException(e);
}
}
@Override
public Object deepCopy(Object value) throws HibernateException
{
Document orig = (Document)value;
DOMResult result;
try
{
TransformerFactory tfactory = TransformerFactory.newInstance();
Transformer tx = tfactory.newTransformer();
DOMSource source = new DOMSource(orig);
result = new DOMResult();
tx.transform(source,result);
return (Document)result.getNode();
}
catch (Exception e)
{
throw new HibernateException(e);
}
}
@Override
public boolean isMutable()
{
return true;
}
@Override
public Serializable disassemble(Object value) throws HibernateException
{
//NOTE: We're making a really ugly assumption here, that the particular parser
//impelementation creates a Document object that is Serializable. In the case
//of the Oracle XDK parser, it is, but it may not be for the default Xerces
//implementation - you have been warned.
Serializable doc = (Serializable) deepCopy(value);
return doc;
}
@Override
public Object assemble(Serializable cached, Object owner)
throws HibernateException
{
Document doc = (Document) deepCopy(cached);
return doc;
}
@Override
public Object replace(Object original, Object target, Object owner)
throws HibernateException
{
return deepCopy(original);
}
}
答案 0 :(得分:1)
我认为Hibernate可以通过某种方式添加一个ActionListener,它可以在提交完成后完成一些工作。来自freenode上#hibernate房间的人建议我们尝试使用AfterTransactionCompletionProcess来完成我们需要的工作。
所以下一个显而易见的问题是......我可以使用哪个例子?我打开了一个SOF问题并自己回答:How to use org.hibernate.action.spi.AfterTransactionCompletionProcess?
因此,使用此示例加上您提供的HibernateXMLType
类,我们现在可以注册AfterTransactionCompletionProcess进程,以便调用它以满足您的要求:“必须在事务提交后调用,但在连接关闭之前。“
以下是源代码。
请查看我遇到困难的评论。我不确切地知道从实体调用什么来手动清除内存。我想知道如何从free()
方法调用实体中java.sql.SQLXML
对象的doAfterTransactionCompletion
方法...从而消除内存泄漏。
我会在早上恢复原状,看看能不能搞清楚。也许这就是获得解决方案所需的全部内容?如果是这样,太好了!
<强> HibernateTest.java 强>
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.action.spi.AfterTransactionCompletionProcess;
import org.hibernate.cfg.Configuration;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventType;
import org.hibernate.event.spi.PostInsertEvent;
import org.hibernate.event.spi.PostInsertEventListener;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
public class HibernateTest {
public static void main(String [] args) {
PostInsertTransactionBoundaryListener listener = new PostInsertTransactionBoundaryListener();
Configuration configuration = new Configuration();
configuration.configure();
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry();
EventListenerRegistry registry = serviceRegistry.getService(EventListenerRegistry.class);
registry.getEventListenerGroup(EventType.POST_COMMIT_INSERT).appendListener(listener);
SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
Session session = sessionFactory.openSession();
session.getTransaction().begin();
TestEntity entity = new TestEntity();
session.save(entity);
session.getTransaction().commit();
session.close();
}
private static class PostInsertTransactionBoundaryListener implements PostInsertEventListener {
private static final long serialVersionUID = 1L;
public void onPostInsert(final PostInsertEvent event) {
event.getSession().getActionQueue().registerProcess(new AfterTransactionCompletionProcess() {
public void doAfterTransactionCompletion(boolean success, SessionImplementor session) {
TestEntity testEntity = (TestEntity)event.getEntity();
if (testEntity != null) {
// How can I free the memory here to avoid the memory leak???
}
}
});
}
}
}
<强> TestEntity.java 强>
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "TEST")
public class TestEntity {
@Id
@GeneratedValue
private Integer id;
private HibernateXMLType xml;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public HibernateXMLType getXml() {
return xml;
}
public void setXml(HibernateXMLType xml) {
this.xml = xml;
}
}