1 /*
2 * Hibernate, Relational Persistence for Idiomatic Java
3 *
4 * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
5 * indicated by the @author tags or express copyright attribution
6 * statements applied by the authors. All third-party contributions are
7 * distributed under license by Red Hat Middleware LLC.
8 *
9 * This copyrighted material is made available to anyone wishing to use, modify,
10 * copy, or redistribute it subject to the terms and conditions of the GNU
11 * Lesser General Public License, as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
16 * for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this distribution; if not, write to:
20 * Free Software Foundation, Inc.
21 * 51 Franklin Street, Fifth Floor
22 * Boston, MA 02110-1301 USA
23 *
24 */
25 package org.hibernate.type;
26
27 import java.sql.PreparedStatement;
28 import java.sql.ResultSet;
29 import java.sql.SQLException;
30
31 import org.slf4j.LoggerFactory;
32 import org.slf4j.Logger;
33 import org.dom4j.Node;
34
35 import org.hibernate.EntityMode;
36 import org.hibernate.HibernateException;
37 import org.hibernate.engine.Mapping;
38 import org.hibernate.engine.SessionFactoryImplementor;
39 import org.hibernate.engine.SessionImplementor;
40 import org.hibernate.util.ArrayHelper;
41 import org.hibernate.util.EqualsHelper;
42 import org.hibernate.util.StringHelper;
43
44 /**
45 * Superclass of single-column nullable types.
46 *
47 * @author Gavin King
48 */
49 public abstract class NullableType extends AbstractType {
50
51 /**
52 * This is the old scheme where logging of parameter bindings and value extractions
53 * was controlled by the trace level enablement on the 'org.hibernate.type' package...
54 * <p/>
55 * Originally was cached such because of performance of looking up the logger each time
56 * in order to check the trace-enablement. Driving this via a central Log-specific class
57 * would alleviate that performance hit, and yet still allow more "normal" logging usage/config.
58 */
59 private static final boolean IS_VALUE_TRACING_ENABLED = LoggerFactory.getLogger( StringHelper.qualifier( Type.class.getName() ) ).isTraceEnabled();
60 private transient Logger log;
61
62 private Logger log() {
63 if ( log == null ) {
64 log = LoggerFactory.getLogger( getClass() );
65 }
66 return log;
67 }
68
69 /**
70 * Get a column value from a result set, without worrying about the
71 * possibility of null values. Called from {@link #nullSafeGet} after
72 * nullness checks have been performed.
73 *
74 * @param rs The result set from which to extract the value.
75 * @param name The name of the value to extract.
76 *
77 * @return The extracted value.
78 *
79 * @throws org.hibernate.HibernateException Generally some form of mismatch error.
80 * @throws java.sql.SQLException Indicates problem making the JDBC call(s).
81 */
82 public abstract Object get(ResultSet rs, String name) throws HibernateException, SQLException;
83
84 /**
85 * Set a parameter value without worrying about the possibility of null
86 * values. Called from {@link #nullSafeSet} after nullness checks have
87 * been performed.
88 *
89 * @param st The statement into which to bind the parameter value.
90 * @param value The parameter value to bind.
91 * @param index The position or index at which to bind the param value.
92 *
93 * @throws org.hibernate.HibernateException Generally some form of mismatch error.
94 * @throws java.sql.SQLException Indicates problem making the JDBC call(s).
95 */
96 public abstract void set(PreparedStatement st, Object value, int index) throws HibernateException, SQLException;
97
98 /**
99 * A convenience form of {@link #sqlTypes(org.hibernate.engine.Mapping)}, returning
100 * just a single type value since these are explicitly dealing with single column
101 * mappings.
102 *
103 * @return The {@link java.sql.Types} mapping value.
104 */
105 public abstract int sqlType();
106
107 /**
108 * A null-safe version of {@link #toString(Object)}. Specifically we are
109 * worried about null safeness in regards to the incoming value parameter,
110 * not the return.
111 *
112 * @param value The value to convert to a string representation; may be null.
113 * @return The string representation; may be null.
114 * @throws HibernateException Thrown by {@link #toString(Object)}, which this calls.
115 */
116 public String nullSafeToString(Object value) throws HibernateException {
117 return value == null ? null : toString( value );
118 }
119
120 public abstract String toString(Object value) throws HibernateException;
121
122 public abstract Object fromStringValue(String xml) throws HibernateException;
123
124 public final void nullSafeSet(
125 PreparedStatement st,
126 Object value,
127 int index,
128 boolean[] settable,
129 SessionImplementor session)
130 throws HibernateException, SQLException {
131 if ( settable[0] ) nullSafeSet(st, value, index);
132 }
133
134 public final void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session)
135 throws HibernateException, SQLException {
136 nullSafeSet(st, value, index);
137 }
138
139 public final void nullSafeSet(PreparedStatement st, Object value, int index)
140 throws HibernateException, SQLException {
141 try {
142 if ( value == null ) {
143 if ( IS_VALUE_TRACING_ENABLED ) {
144 log().trace( "binding null to parameter: " + index );
145 }
146
147 st.setNull( index, sqlType() );
148 }
149 else {
150 if ( IS_VALUE_TRACING_ENABLED ) {
151 log().trace( "binding '" + toString( value ) + "' to parameter: " + index );
152 }
153
154 set( st, value, index );
155 }
156 }
157 catch ( RuntimeException re ) {
158 log().info( "could not bind value '" + nullSafeToString( value ) + "' to parameter: " + index + "; " + re.getMessage() );
159 throw re;
160 }
161 catch ( SQLException se ) {
162 log().info( "could not bind value '" + nullSafeToString( value ) + "' to parameter: " + index + "; " + se.getMessage() );
163 throw se;
164 }
165 }
166
167 public final Object nullSafeGet(
168 ResultSet rs,
169 String[] names,
170 SessionImplementor session,
171 Object owner)
172 throws HibernateException, SQLException {
173 return nullSafeGet(rs, names[0]);
174 }
175
176 public final Object nullSafeGet(ResultSet rs, String[] names)
177 throws HibernateException, SQLException {
178 return nullSafeGet(rs, names[0]);
179 }
180
181 public final Object nullSafeGet(ResultSet rs, String name)
182 throws HibernateException, SQLException {
183 try {
184 Object value = get(rs, name);
185 if ( value == null || rs.wasNull() ) {
186 if ( IS_VALUE_TRACING_ENABLED ) {
187 log().trace( "returning null as column: " + name );
188 }
189 return null;
190 }
191 else {
192 if ( IS_VALUE_TRACING_ENABLED ) {
193 log().trace( "returning '" + toString( value ) + "' as column: " + name );
194 }
195 return value;
196 }
197 }
198 catch ( RuntimeException re ) {
199 log().info( "could not read column value from result set: " + name + "; " + re.getMessage() );
200 throw re;
201 }
202 catch ( SQLException se ) {
203 log().info( "could not read column value from result set: " + name + "; " + se.getMessage() );
204 throw se;
205 }
206 }
207
208 public final Object nullSafeGet(ResultSet rs, String name, SessionImplementor session, Object owner)
209 throws HibernateException, SQLException {
210 return nullSafeGet(rs, name);
211 }
212
213 public final String toXMLString(Object value, SessionFactoryImplementor pc)
214 throws HibernateException {
215 return toString(value);
216 }
217
218 public final Object fromXMLString(String xml, Mapping factory) throws HibernateException {
219 return xml==null || xml.length()==0 ? null : fromStringValue(xml);
220 }
221
222 public final int getColumnSpan(Mapping session) {
223 return 1;
224 }
225
226 public final int[] sqlTypes(Mapping session) {
227 return new int[] { sqlType() };
228 }
229
230 public final boolean isEqual(Object x, Object y, EntityMode entityMode) {
231 return isEqual(x, y);
232 }
233
234 public boolean isEqual(Object x, Object y) {
235 return EqualsHelper.equals(x, y);
236 }
237
238 public String toLoggableString(Object value, SessionFactoryImplementor factory) {
239 return value == null ? "null" : toString(value);
240 }
241
242 public Object fromXMLNode(Node xml, Mapping factory) throws HibernateException {
243 return fromXMLString( xml.getText(), factory );
244 }
245
246 public void setToXMLNode(Node xml, Object value, SessionFactoryImplementor factory)
247 throws HibernateException {
248 xml.setText( toXMLString(value, factory) );
249 }
250
251 public boolean[] toColumnNullness(Object value, Mapping mapping) {
252 return value==null ? ArrayHelper.FALSE : ArrayHelper.TRUE;
253 }
254
255 public boolean isDirty(Object old, Object current, boolean[] checkable, SessionImplementor session)
256 throws HibernateException {
257 return checkable[0] && isDirty(old, current, session);
258 }
259 }