【问题标题】:StAX - reading base64 string from xml into dbStAX - 将base64字符串从xml读入db
【发布时间】:2012-03-19 17:41:42
【问题描述】:

我正在使用 StAX 读取我的文件,其中包含一些 Base64 数据,并使用 Hibernate 将其保存到数据库中。

XML:

<root>
  <base64>lololencoded12</base64>
  <base64>encodedlolos32</base64>
  ...............................
</root>

要读取和保存的代码:

xmlif = (XMLInputFactory2) XMLInputFactory2.newInstance();
xmlif.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, Boolean.FALSE);
xmlif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
xmlif.setProperty(XMLInputFactory.IS_COALESCING, Boolean.FALSE); 
xmlif.configureForLowMemUsage();

List<Entity> entities = new ArrayList();
FileInputStream fis = new FileInputStream(filename);
XMLStreamReader2 xmlr = (XMLStreamReader2) xmlif.createXMLStreamReader(filename, fis);
int eventType = xmlr.getEventType();
String curElement = "";
while (xmlr.hasNext()) {
     eventType = xmlr.next();
     switch (eventType) {
         case XMLEvent.START_ELEMENT:
             curElement=xmlr.getName().toString();
             if ("base64".equals(curElement)) {
                 Entity entity = new Entity();
                 entity.setBase64(xmlr.getElementText().getBytes());
                 session.save(entity);
                 session.flush();
             }
             break;
     }
 }
 iterator itr = entities.iterator();
 while (itr.hasNext()) {
     Entity e = (Entity)itr.next();                    
     session.saveOrUpdate(e);
 }

这种方法消耗的内存量是我的 xml 大小的 6-9 倍。我该如何改进呢?

编辑

如果我注释掉 entity.setBase64() 一切都很好。将 byte[] 保存到 db 时,内存使用会变得疯狂。为什么?

编辑 实体获取器和设置器:

    //for me
    public byte[] getBase64() {
        return base64;
    }

    public void setBase64(byte[] base64) {
        this.base64= base64;
    }


    //for hibernate
    public Blob getBase64Blob() {
        if (this.base64!=null) {
            LobCreator lobok =Hibernate.getLobCreator(MainFrame.sessionFactory.getCurrentSession());
            return lobok.createBlob(base64);
        } else {
            return null;
        }
    }

    public void setBase64Blob(Blob dataBlob) {
        if (dataBlob!=null) {
            this.base64= toByteArray(dataBlob);
        }
    }   

    //utilities methods from blob to byte array 
    private byte[] toByteArray(Blob fromBlob) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            return toByteArrayImpl(fromBlob, baos);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (baos != null) {
                try {
                    baos.close();
                } catch (IOException ex) {
                }
            }
        }
    }

    private byte[] toByteArrayImpl(Blob fromBlob, ByteArrayOutputStream baos)
            throws SQLException, IOException {
        byte[] buf = new byte[4000];
        InputStream is = fromBlob.getBinaryStream();
        try {
            for (;;) {
                int dataSize = is.read(buf);
                if (dataSize == -1)
                    break;
                baos.write(buf, 0, dataSize);
            }
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException ex) {
                }
            }
        }
        return baos.toByteArray();
    }

编辑 xmlr.getElementText().getBytes() 由于某种原因导致大量内存使用。

【问题讨论】:

  • 您是否分析了这段代码的内存消耗?记忆去哪儿了?
  • 我可以看到内存使用峰值是实际 xml 大小的 6 倍。
  • 我可以看到分配了很多 char[] 和 byte[]
  • session.saveOrUpdate(e);替换entities.add(entity);
  • 问:您需要将二进制 base64 编码的 blob 以什么表示形式存储在数据库中?只是编码文本留在 base64 中,还是对 setBase64 的调用实际上将 base64 文本解码回二进制数据,作为 blob 存储在您的数据库中?这个细节可能会有所帮助。

标签: java xml hibernate sax stax


【解决方案1】:

您多久刷新一次实体?

我认为您构建了太多实体并且没有足够频繁地刷新会话,这导致所有这些对象被创建以保留在当前会话中。

可能想检查一下。

编辑

下面的 cmets 表明提交者想要一种将二进制 blob 数据直接插入数据库的方法。

这可以通过 JDBC 来实现,而不是使用 hibernate 来实现。

java.sql.Connection conn = ...
java.sql.PreparedStatement pstmt= conn.prepareStatement("insert into ENTITY_TABLE (BASE64) VALUES (?)");
InputStream is= ... // byte data
pstmt.setBinaryStream(1, is);
int numUpdated= pstmt.executeUpdate();

现在请注意,这是真正的颠簸。这是假设 ENTITY_TABLE 使用数据库生成的行标识符,并且允许 BASE64 以外的列具有空值或具有合理的默认值。 executeUpdate 将运行插入语句,将 is 的值作为 blob 数据的值。

我希望这能让您更接近您的解决方案。

【讨论】:

  • 为什么关闭了合并?我想知道您是否正在为您的 base64 数据获取大量事件。不过,我在这里抓住了稻草
  • base64 字符串有多大?更重要的是,base64字符串有多大,解码后的二进制有多大?
  • 这就是我的想法,我希望其他 SO'ers 能够支持我。当您读取 XML 时,您会在内存中获得 11m,您请求字节,可能现在分配了另一个 11m 字节数组,这将是一个双倍,可能还有更多。尝试将 visualvm 运行到(我认为包含在 jdk 6 及更高版本中),看看你在哪里升温。
  • 我不记得具体在 visualvm 中指示内存分配的位置...我还会检查以确保此时休眠没有缓存实体...看起来您想要目前只是将这些东西放入数据库中,而不是其他任何东西。
  • 有...但是您必须通过身份或自动增量列来处理您的记录标识。查看答案正文进行编辑(即将推出)
猜你喜欢
  • 2016-05-14
  • 1970-01-01
  • 2011-06-06
  • 2016-03-04
  • 2016-03-10
  • 1970-01-01
  • 2016-12-03
  • 2020-08-04
  • 1970-01-01
相关资源
最近更新 更多