001/*
002 * Copyright 2002-2012 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.orm.hibernate3.support;
018
019import java.io.ByteArrayInputStream;
020import java.io.ByteArrayOutputStream;
021import java.io.IOException;
022import java.io.InputStream;
023import java.io.ObjectInputStream;
024import java.io.ObjectOutputStream;
025import java.io.Serializable;
026import java.sql.PreparedStatement;
027import java.sql.ResultSet;
028import java.sql.SQLException;
029import java.sql.Types;
030import javax.transaction.TransactionManager;
031
032import org.hibernate.HibernateException;
033
034import org.springframework.jdbc.support.lob.LobCreator;
035import org.springframework.jdbc.support.lob.LobHandler;
036
037/**
038 * Hibernate UserType implementation for arbitrary objects that get serialized to BLOBs.
039 * Retrieves the LobHandler to use from LocalSessionFactoryBean at config time.
040 *
041 * <p>Can also be defined in generic Hibernate mappings, as DefaultLobCreator will
042 * work with most JDBC-compliant database drivers. In this case, the field type
043 * does not have to be BLOB: For databases like MySQL and MS SQL Server, any
044 * large enough binary type will work.
045 *
046 * @author Juergen Hoeller
047 * @since 1.2
048 * @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#setLobHandler
049 * @deprecated as of Spring 4.3, in favor of Hibernate 4.x/5.x
050 */
051@Deprecated
052public class BlobSerializableType extends AbstractLobType {
053
054        /**
055         * Initial size for ByteArrayOutputStreams used for serialization output.
056         * <p>If a serialized object is larger than these 1024 bytes, the size of
057         * the byte array used by the output stream will be doubled each time the
058         * limit is reached.
059         */
060        private static final int OUTPUT_BYTE_ARRAY_INITIAL_SIZE = 1024;
061
062        /**
063         * Constructor used by Hibernate: fetches config-time LobHandler and
064         * config-time JTA TransactionManager from LocalSessionFactoryBean.
065         * @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#getConfigTimeLobHandler
066         * @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#getConfigTimeTransactionManager
067         */
068        public BlobSerializableType() {
069                super();
070        }
071
072        /**
073         * Constructor used for testing: takes an explicit LobHandler
074         * and an explicit JTA TransactionManager (can be {@code null}).
075         */
076        protected BlobSerializableType(LobHandler lobHandler, TransactionManager jtaTransactionManager) {
077                super(lobHandler, jtaTransactionManager);
078        }
079
080        @Override
081        public int[] sqlTypes() {
082                return new int[] {Types.BLOB};
083        }
084
085        @Override
086        public Class<?> returnedClass() {
087                return Serializable.class;
088        }
089
090        @Override
091        public boolean isMutable() {
092                return true;
093        }
094
095        @Override
096        public Object deepCopy(Object value) throws HibernateException {
097                try {
098                        // Write to new byte array to clone.
099                        ByteArrayOutputStream baos = new ByteArrayOutputStream(OUTPUT_BYTE_ARRAY_INITIAL_SIZE);
100                        ObjectOutputStream oos = new ObjectOutputStream(baos);
101                        try {
102                                oos.writeObject(value);
103                        }
104                        finally {
105                                oos.close();
106                        }
107
108                        // Read it back and return a true copy.
109                        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
110                        ObjectInputStream ois = new ObjectInputStream(bais);
111                        try {
112                                return ois.readObject();
113                        }
114                        finally {
115                                ois.close();
116                        }
117                }
118                catch (ClassNotFoundException ex) {
119                        throw new HibernateException("Couldn't clone BLOB contents", ex);
120                }
121                catch (IOException ex) {
122                        throw new HibernateException("Couldn't clone BLOB contents", ex);
123                }
124        }
125
126        @Override
127        protected Object nullSafeGetInternal(
128                        ResultSet rs, String[] names, Object owner, LobHandler lobHandler)
129                        throws SQLException, IOException, HibernateException {
130
131                InputStream is = lobHandler.getBlobAsBinaryStream(rs, names[0]);
132                if (is != null) {
133                        ObjectInputStream ois = new ObjectInputStream(is);
134                        try {
135                                return ois.readObject();
136                        }
137                        catch (ClassNotFoundException ex) {
138                                throw new HibernateException("Could not deserialize BLOB contents", ex);
139                        }
140                        finally {
141                                ois.close();
142                        }
143                }
144                else {
145                        return null;
146                }
147        }
148
149        @Override
150        protected void nullSafeSetInternal(
151                        PreparedStatement ps, int index, Object value, LobCreator lobCreator)
152                        throws SQLException, IOException {
153
154                if (value != null) {
155                        ByteArrayOutputStream baos = new ByteArrayOutputStream(OUTPUT_BYTE_ARRAY_INITIAL_SIZE);
156                        ObjectOutputStream oos = new ObjectOutputStream(baos);
157                        try {
158                                oos.writeObject(value);
159                                oos.flush();
160                                lobCreator.setBlobAsBytes(ps, index, baos.toByteArray());
161                        }
162                        finally {
163                                oos.close();
164                        }
165                }
166                else {
167                        lobCreator.setBlobAsBytes(ps, index, null);
168                }
169        }
170
171}