001/* 002 * Copyright 2002-2017 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.core.support; 018 019import java.sql.ResultSet; 020import java.sql.SQLException; 021import java.util.Properties; 022import javax.sql.DataSource; 023 024import org.springframework.beans.factory.support.BeanDefinitionRegistry; 025import org.springframework.beans.factory.support.PropertiesBeanDefinitionReader; 026import org.springframework.jdbc.core.JdbcTemplate; 027import org.springframework.jdbc.core.RowCallbackHandler; 028import org.springframework.util.Assert; 029 030/** 031 * Bean definition reader that reads values from a database table, 032 * based on a given SQL statement. 033 * 034 * <p>Expects columns for bean name, property name and value as String. 035 * Formats for each are identical to the properties format recognized 036 * by PropertiesBeanDefinitionReader. 037 * 038 * <p><b>NOTE:</b> This is mainly intended as an example for a custom 039 * JDBC-based bean definition reader. It does not aim to offer 040 * comprehensive functionality. 041 * 042 * @author Rod Johnson 043 * @author Juergen Hoeller 044 * @see #loadBeanDefinitions 045 * @see org.springframework.beans.factory.support.PropertiesBeanDefinitionReader 046 */ 047public class JdbcBeanDefinitionReader { 048 049 private final PropertiesBeanDefinitionReader propReader; 050 051 private JdbcTemplate jdbcTemplate; 052 053 054 /** 055 * Create a new JdbcBeanDefinitionReader for the given bean factory, 056 * using a default PropertiesBeanDefinitionReader underneath. 057 * <p>DataSource or JdbcTemplate still need to be set. 058 * @see #setDataSource 059 * @see #setJdbcTemplate 060 */ 061 public JdbcBeanDefinitionReader(BeanDefinitionRegistry beanFactory) { 062 this.propReader = new PropertiesBeanDefinitionReader(beanFactory); 063 } 064 065 /** 066 * Create a new JdbcBeanDefinitionReader that delegates to the 067 * given PropertiesBeanDefinitionReader underneath. 068 * <p>DataSource or JdbcTemplate still need to be set. 069 * @see #setDataSource 070 * @see #setJdbcTemplate 071 */ 072 public JdbcBeanDefinitionReader(PropertiesBeanDefinitionReader beanDefinitionReader) { 073 Assert.notNull(beanDefinitionReader, "Bean definition reader must not be null"); 074 this.propReader = beanDefinitionReader; 075 } 076 077 078 /** 079 * Set the DataSource to use to obtain database connections. 080 * Will implicitly create a new JdbcTemplate with the given DataSource. 081 */ 082 public void setDataSource(DataSource dataSource) { 083 this.jdbcTemplate = new JdbcTemplate(dataSource); 084 } 085 086 /** 087 * Set the JdbcTemplate to be used by this bean factory. 088 * Contains settings for DataSource, SQLExceptionTranslator, etc. 089 */ 090 public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { 091 Assert.notNull(jdbcTemplate, "JdbcTemplate must not be null"); 092 this.jdbcTemplate = jdbcTemplate; 093 } 094 095 096 /** 097 * Load bean definitions from the database via the given SQL string. 098 * @param sql SQL query to use for loading bean definitions. 099 * The first three columns must be bean name, property name and value. 100 * Any join and any other columns are permitted: e.g. 101 * {@code SELECT BEAN_NAME, PROPERTY, VALUE FROM CONFIG WHERE CONFIG.APP_ID = 1} 102 * It's also possible to perform a join. Column names are not significant -- 103 * only the ordering of these first three columns. 104 */ 105 public void loadBeanDefinitions(String sql) { 106 Assert.notNull(this.jdbcTemplate, "Not fully configured - specify DataSource or JdbcTemplate"); 107 final Properties props = new Properties(); 108 this.jdbcTemplate.query(sql, new RowCallbackHandler() { 109 @Override 110 public void processRow(ResultSet rs) throws SQLException { 111 String beanName = rs.getString(1); 112 String property = rs.getString(2); 113 String value = rs.getString(3); 114 // Make a properties entry by combining bean name and property. 115 props.setProperty(beanName + '.' + property, value); 116 } 117 }); 118 this.propReader.registerBeanDefinitions(props); 119 } 120 121}