【问题标题】:IOException: The stream is closed Hibernate MS SQL JDBC4IOException:流已关闭 Hibernate MS SQL JDBC4
【发布时间】:2012-06-28 04:37:58
【问题描述】:

获取二进制流时收到以下异常。

    java.io.IOException: The stream is closed.
at com.microsoft.sqlserver.jdbc.BaseInputStream.checkClosed(SimpleInputStream.java:93)
at com.microsoft.sqlserver.jdbc.PLPInputStream.read(PLPInputStream.java:237)
at au.gov.vic.doi.tp.service.binary.dao.BinaryDAOImplTest.retrieveBinary(BinaryDAOImplTest.java:102)

我正在使用:Hibernate 4.1.1、MS SQL Server 2008 和最新的 MS SQL JDBC4 驱动程序。 我使用以下自定义映射类型“BlobToStreamUserType”从 SQL Blob (varchar(max)) 映射到 InputStream。 Hibernate 似乎没有提供将 SQL BLOB 映射到 Java InputStream 的类型,MaterializedBlobType 仅将 SQL BLOB 映射到 Java byte[]。

我正在使用“rs.getBinaryStream”返回一个真正的二进制流,而不是缓冲在内存中的流,原因是我们有大图像,如果多个用户同时访问会导致内存问题。

根据微软

Microsoft 驱动程序在 getBinaryStream 方面遵循 JDBC 规范 => 注意:在获取任何 >other 列的值之前,必须读取返回流中的所有数据。对 getter 方法的下一次调用会隐式关闭流。此外,当调用 InputStream.available 方法时,流可能会返回 0,无论是否有数据>可用。

所以 getBinaryStream 旨在返回真正的字节流。我们返回一个真正的>字节流,它没有在内存中缓冲,它是一个从服务器响应中直接读取的流。在为我们的实现获取所有字节之前,您无法关闭 contentDataResultSet。

我尝试将休眠属性 'hibernate.jdbc.use_streams_for_binary' 设置为 true 和 false,但这没有区别。

这是我的代码:

显示映射到自定义用户类型的图像实体类。

   import java.io.InputStream;
   import java.util.Date;
   import javax.persistence.Column;
   import javax.persistence.Entity;
   import javax.persistence.GeneratedValue;
   import javax.persistence.Id;
   import javax.persistence.Table;
   import org.hibernate.annotations.DynamicInsert;
   import org.hibernate.annotations.DynamicUpdate;
   import org.hibernate.annotations.GenericGenerator;
   import org.hibernate.annotations.Type;
   import com.google.common.base.Objects;

   @Entity
   @DynamicInsert(false)
   @DynamicUpdate(false)
   @Table(name = "Binary")
   public class Binary
   {
   .........
    @Lob
    @Column(name = "image")
    @Type(type = "my.package.BlobToStreamUserType")   
    public InputStream getImageStream()
    {
        return this.imageStream;
    }
    }

这是我的自定义映射类型:

public class BlobToStreamUserType implements UserType
{

public int[] sqlTypes()
{
    return new int[]
    { Types.BLOB };
}

public Class returnedClass()
{
    return InputStream.class;
}

public Object nullSafeGet(final ResultSet rs, final String[] names, final SessionImplementor session,
        final Object owner) throws HibernateException, SQLException
{
    return rs.getBinaryStream(names[0]);
}

public void nullSafeSet(final PreparedStatement st, final Object value, final int index,
        final SessionImplementor session) throws HibernateException, SQLException
{

    if (value != null)
    {
        if (!InputStream.class.isAssignableFrom(value.getClass()))
        {
            throw new HibernateException(value.getClass().toString() + " cannot be cast to a java.IO.InputStream");
        }
        InputStream inStream = (InputStream) value;
        st.setBinaryStream(index, inStream);
    }
    else
    {
        st.setBytes(index, null);
    }
}

public boolean equals(final Object x, final Object y) throws HibernateException
{
    return ObjectUtils.equals(x, y);
}

public int hashCode(final Object x) throws HibernateException
{
    assert (x != null);
    return x.hashCode();
}

public Object deepCopy(final Object value) throws HibernateException
{
    return value;
}

public boolean isMutable()
{
    return false;
}

public Object replace(final Object original, final Object target, final Object owner) throws HibernateException
{
    return original;
}

public Serializable disassemble(final Object value) throws HibernateException
{
    //disassemble() is only called when caching the data in the second-level cache
    // also safe for mutable objects
    throw new UnsupportedOperationException("Not supported yet.");
}

public Object assemble(final Serializable cached, final Object owner) throws HibernateException
{
    throw new UnsupportedOperationException("Not supported yet.");
}

}

这是我用来检索图像流的 DAO

public class BinaryDAOImpl extends AbstractSpringHibernateDAO implements BinaryDAO
{

public Binary getBinary(final String amsAssetId, final String assetMaintainer, final String variant,
        final Integer sequence) throws GetException
{

    try
    {
        Query query = super.getSession().createQuery(
                "from Binary bin where bin.amsAssetId = :amsAssetId and bin.assetMaintainer = :assetMaintainer "
                        + "and bin.variant = :variant and bin.sequence = :sequence");
        query.setParameter("amsAssetId", amsAssetId);
        query.setParameter("assetMaintainer", assetMaintainer);
        query.setParameter("variant", variant);
        query.setParameter("sequence", sequence);
        return query.uniqueResult();
    }

N.b 绕过休眠并直接从结果集中检索流的以下解决方法有效,并且允许我毫无例外地读取流。但是我宁愿使用休眠映射来检索真正的二进制流。

修改DAO方法

public Binary getBinary(final String amsAssetId, final String assetMaintainer, final String variant,
        final Integer sequence) throws GetException
{
    try
    {
        Query query = super.getSession().createQuery(
                "from Binary bin where bin.amsAssetId = :amsAssetId and bin.assetMaintainer = :assetMaintainer "
                        + "and bin.variant = :variant and bin.sequence = :sequence");
        query.setParameter("amsAssetId", amsAssetId);
        query.setParameter("assetMaintainer", assetMaintainer);
        query.setParameter("variant", variant);
        query.setParameter("sequence", sequence);
        Binary binary = (Binary) query.uniqueResult();

        final String id = binary.getId();

        // Necessary until Hibernate supports InputStream mapping
        binary.setImageStream(getSession().doReturningWork(new ReturningWork<InputStream>()
        {
                      public InputStream execute(final Connection connection) throws SQLException
                        {
                            PreparedStatement statement = connection.prepareStatement("select image from Binary where id=?",
                                    ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);

                            statement.setString(1, id);

                            ResultSet resultSet = statement.executeQuery();

                            return (resultSet.first() ? resultSet.getBinaryStream(1) : null);
                        }
                    }));

        return binary;

    }

【问题讨论】:

    标签: java sql-server-2008 jdbc stream hibernate-mapping


    【解决方案1】:

    您是否尝试将类型标记为 Lob?

    @Lob
    @Column(name = "image")
    @Type(type = "my.package.BlobToStreamUserType")   
    Blob blob;
    
    public Blob getBlob(){
       return blob;
    } 
    

    什么时候需要处理流?

                final InputStream is;
        try {
            is = blob.getBinaryStream();
                        //do some work
                }
    

    知道你可以简单地取回一个 Blob 会很有帮助 在定义自己的类型之前。

    【讨论】:

      【解决方案2】:

      对于所有遇到这种情况的人:

      将您的 JDBC 驱动程序更新到至少 7.0 或更高版本。

      这是一个 *icrosoft JDBC 驱动程序问题,在其 JDBC 驱动程序的 6.5.0(预览版)或 7.0.0(最终版)中是 fixed。 原始问题报告为hereherehere。 (对于任何有兴趣的人。) 有一个很好的support matrix,您可以在其中查看最适合您的版本。 (因为这有时是受支持的 Java 版本、JDBC 驱动程序版本和 JDBC API 版本的问题)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-09-10
        • 2018-02-01
        • 1970-01-01
        • 2020-06-08
        • 1970-01-01
        • 2017-11-02
        • 2012-03-10
        相关资源
        最近更新 更多