001/* 002 * Copyright 2002-2018 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.jdbc.support; 018 019import javax.sql.DataSource; 020 021import org.apache.commons.logging.Log; 022import org.apache.commons.logging.LogFactory; 023 024import org.springframework.beans.factory.InitializingBean; 025import org.springframework.lang.Nullable; 026import org.springframework.util.Assert; 027 028/** 029 * Base class for {@link org.springframework.jdbc.core.JdbcTemplate} and 030 * other JDBC-accessing DAO helpers, defining common properties such as 031 * DataSource and exception translator. 032 * 033 * <p>Not intended to be used directly. 034 * See {@link org.springframework.jdbc.core.JdbcTemplate}. 035 * 036 * @author Juergen Hoeller 037 * @since 28.11.2003 038 * @see org.springframework.jdbc.core.JdbcTemplate 039 */ 040public abstract class JdbcAccessor implements InitializingBean { 041 042 /** Logger available to subclasses. */ 043 protected final Log logger = LogFactory.getLog(getClass()); 044 045 @Nullable 046 private DataSource dataSource; 047 048 @Nullable 049 private volatile SQLExceptionTranslator exceptionTranslator; 050 051 private boolean lazyInit = true; 052 053 054 /** 055 * Set the JDBC DataSource to obtain connections from. 056 */ 057 public void setDataSource(@Nullable DataSource dataSource) { 058 this.dataSource = dataSource; 059 } 060 061 /** 062 * Return the DataSource used by this template. 063 */ 064 @Nullable 065 public DataSource getDataSource() { 066 return this.dataSource; 067 } 068 069 /** 070 * Obtain the DataSource for actual use. 071 * @return the DataSource (never {@code null}) 072 * @throws IllegalStateException in case of no DataSource set 073 * @since 5.0 074 */ 075 protected DataSource obtainDataSource() { 076 DataSource dataSource = getDataSource(); 077 Assert.state(dataSource != null, "No DataSource set"); 078 return dataSource; 079 } 080 081 /** 082 * Specify the database product name for the DataSource that this accessor uses. 083 * This allows to initialize an SQLErrorCodeSQLExceptionTranslator without 084 * obtaining a Connection from the DataSource to get the meta-data. 085 * @param dbName the database product name that identifies the error codes entry 086 * @see SQLErrorCodeSQLExceptionTranslator#setDatabaseProductName 087 * @see java.sql.DatabaseMetaData#getDatabaseProductName() 088 */ 089 public void setDatabaseProductName(String dbName) { 090 this.exceptionTranslator = new SQLErrorCodeSQLExceptionTranslator(dbName); 091 } 092 093 /** 094 * Set the exception translator for this instance. 095 * <p>If no custom translator is provided, a default 096 * {@link SQLErrorCodeSQLExceptionTranslator} is used 097 * which examines the SQLException's vendor-specific error code. 098 * @see org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator 099 * @see org.springframework.jdbc.support.SQLStateSQLExceptionTranslator 100 */ 101 public void setExceptionTranslator(SQLExceptionTranslator exceptionTranslator) { 102 this.exceptionTranslator = exceptionTranslator; 103 } 104 105 /** 106 * Return the exception translator for this instance. 107 * <p>Creates a default {@link SQLErrorCodeSQLExceptionTranslator} 108 * for the specified DataSource if none set, or a 109 * {@link SQLStateSQLExceptionTranslator} in case of no DataSource. 110 * @see #getDataSource() 111 */ 112 public SQLExceptionTranslator getExceptionTranslator() { 113 SQLExceptionTranslator exceptionTranslator = this.exceptionTranslator; 114 if (exceptionTranslator != null) { 115 return exceptionTranslator; 116 } 117 synchronized (this) { 118 exceptionTranslator = this.exceptionTranslator; 119 if (exceptionTranslator == null) { 120 DataSource dataSource = getDataSource(); 121 if (dataSource != null) { 122 exceptionTranslator = new SQLErrorCodeSQLExceptionTranslator(dataSource); 123 } 124 else { 125 exceptionTranslator = new SQLStateSQLExceptionTranslator(); 126 } 127 this.exceptionTranslator = exceptionTranslator; 128 } 129 return exceptionTranslator; 130 } 131 } 132 133 /** 134 * Set whether to lazily initialize the SQLExceptionTranslator for this accessor, 135 * on first encounter of an SQLException. Default is "true"; can be switched to 136 * "false" for initialization on startup. 137 * <p>Early initialization just applies if {@code afterPropertiesSet()} is called. 138 * @see #getExceptionTranslator() 139 * @see #afterPropertiesSet() 140 */ 141 public void setLazyInit(boolean lazyInit) { 142 this.lazyInit = lazyInit; 143 } 144 145 /** 146 * Return whether to lazily initialize the SQLExceptionTranslator for this accessor. 147 * @see #getExceptionTranslator() 148 */ 149 public boolean isLazyInit() { 150 return this.lazyInit; 151 } 152 153 /** 154 * Eagerly initialize the exception translator, if demanded, 155 * creating a default one for the specified DataSource if none set. 156 */ 157 @Override 158 public void afterPropertiesSet() { 159 if (getDataSource() == null) { 160 throw new IllegalArgumentException("Property 'dataSource' is required"); 161 } 162 if (!isLazyInit()) { 163 getExceptionTranslator(); 164 } 165 } 166 167}