我正在尝试使以下代码正常工作。基本上我在 mysql 数据库中有一个名为 alert_settings 的表。下面是我如何配置它和类。
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd
">
<!-- DriverManagerDataSource : Spring simplest implementation of a DataSource (doesn�??t support database connection pooling)-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- The properties file containing the values for the JDBC datasource-->
<context:property-placeholder location="jdbc.properties"/>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="abstractBaseClass" abstract="true" class="org.jay.dao.impl.spring.commons.GenericDAO">
<property name="dataSource" value="dataSource"/>
</bean>
<bean id="alertSettingDAO" class="org.jay.dao.impl.spring.AlertSettingDAOImplSpring" parent="abstractBaseClass">
<!--Override the value of the abstract based class if necessary-->
<property name="dataSource" value="dataSource"/>
</bean>
下面是我的抽象通用 DAO、警报设置 DAO 接口和实际 DAO 类。
抽象通用 DAO
public abstract class GenericDAO<T> { /** * The DataSource providing the connections (injected by Spring) */ @Resource(name="dataSource") private DataSource dataSource; /** * The auto-incremented column name if any (null if none) */ private final String autoIncrColumnName ; /** * The columns types for each value to be inserted with an auto-incremented key (null if no auto-incremented column) */ private final int[] insertJdbcTypes ; /** * Constructor for a standard table (without auto-incremented column) */ protected GenericDAO() { super(); this.autoIncrColumnName = null ; this.insertJdbcTypes = null ; } /** * Constructor for a table with an auto-incremented key * @param autoIncrColumnName * @param insertJdbcTypes */ protected GenericDAO(String autoIncrColumnName, int[] insertJdbcTypes) { super(); this.autoIncrColumnName = autoIncrColumnName ; this.insertJdbcTypes = insertJdbcTypes ; } /** * Returns the SQL SELECT REQUEST to be used to retrieve the bean data from the database * @return */ protected abstract String getSqlSelect(); /** * Returns the SQL INSERT REQUEST to be used to insert the bean in the database * @return */ protected abstract String getSqlInsert(); /** * Returns the SQL UPDATE REQUEST to be used to update the bean in the database * @return */ protected abstract String getSqlUpdate(); /** * Returns the SQL DELETE REQUEST to be used to delete the bean from the database * @return */ protected abstract String getSqlDelete(); /** * Returns the SQL COUNT REQUEST to be used to check if the bean exists in the database * @return */ protected abstract String getSqlCount(); /** * Returns the SQL COUNT REQUEST to be used to count all the beans present in the database * @return */ protected abstract String getSqlCountAll(); /** * Returns the values to be used in the SQL INSERT PreparedStatement * @param bean * @return */ protected abstract Object[] getValuesForInsert(T bean) ; /** * Returns the values to be used in the SQL UPDATE PreparedStatement * @param bean * @return */ protected abstract Object[] getValuesForUpdate(T bean) ; /** * Returns the values to be used as Primary Key in a SQL WHERE clause in a PreparedStatement * @param bean * @return */ protected abstract Object[] getValuesForPrimaryKey(T bean); /** * Returns a RowMapper for the given bean * @param bean * @return */ protected abstract RowMapper<T> getRowMapper(T bean); /** * Returns a RowMapper for a new bean instance * @return */ protected abstract RowMapper<T> getRowMapper(); //----------------------------------------------------------------------------------------- private void log(String msg) { //System.out.println("[DAO LOG] : " + msg ); } //----------------------------------------------------------------------------------------- /** * Returns an instance of Spring 'JdbcTemplate' for the current DataSource * @return */ private JdbcTemplate getJdbcTemplate() { return new JdbcTemplate(dataSource); } //----------------------------------------------------------------------------------------- /** * Loads the given bean from the database using its current primary key (SQL SELECT)<br> * If found the given bean is populated * @param bean * @return true if found and loaded, false if not found */ protected boolean doSelect(T bean) { log("Select using a bean instance : " + bean ); Object[] primaryKey = getValuesForPrimaryKey(bean); log("Select using a bean instance : Primary Key = " + toString(primaryKey) ); RowMapper<T> rowMapper = getRowMapper(bean) ; JdbcTemplate jdbcTemplate = getJdbcTemplate(); //--- Try to find the record. NB : Spring throws "EmptyResultDataAccessException" if not found try { T beanFound = jdbcTemplate.queryForObject(getSqlSelect(), primaryKey, rowMapper); if ( beanFound != bean ) { throw new RuntimeException("Unexpected instance returned by JdbcTemplate"); } return true ; } catch (EmptyResultDataAccessException e) { // Nothing to do, just return "not found" return false; } } //----------------------------------------------------------------------------------------- /** * Loads a bean from the database using the given primary key (SQL SELECT)<br> * @param primaryKey * @return the bean found or null if not found */ protected T doSelect(Object[] primaryKey) { log("Select by Primary Key : " + toString(primaryKey) ); RowMapper<T> rowMapper = getRowMapper() ; JdbcTemplate jdbcTemplate = getJdbcTemplate(); //--- Try to find the record. NB : Spring throws "EmptyResultDataAccessException" if not found try { return jdbcTemplate.queryForObject(getSqlSelect(), primaryKey, rowMapper); } catch (EmptyResultDataAccessException e) { // Nothing to do, just return null return null; } } //----------------------------------------------------------------------------------------- /** * Inserts the given bean in the database (SQL INSERT) * @param bean */ protected void doInsert(T bean) { JdbcTemplate jdbcTemplate = getJdbcTemplate(); //--- Execute INSERT int result = jdbcTemplate.update(getSqlInsert(), getValuesForInsert(bean) ); if ( result != 1 ) { throw new RuntimeException("Unexpected return value after INSERT : " + result + " (1 expected) "); } } //----------------------------------------------------------------------------------------- /** * Returns a 'PreparedStatementCreator' usable with a 'KeyHolder' for an insert with an auto-incremented key * @param bean * @return */ private PreparedStatementCreator getPreparedStatementCreator(final T bean) { //log("getPreparedStatementCreator : auto incr col = " + autoIncrColumnName ); //log("getPreparedStatementCreator : sql insert = " + getSqlInsert() ); PreparedStatementCreatorFactory factory = new PreparedStatementCreatorFactory(getSqlInsert(), this.insertJdbcTypes) ; factory.setGeneratedKeysColumnNames(new String[]{autoIncrColumnName}); //log("before newPreparedStatementCreator : values for insert = " + toString(getValuesForInsert(bean))); PreparedStatementCreator psc = factory.newPreparedStatementCreator(getValuesForInsert(bean)); //log("after newPreparedStatementCreator "); return psc ; } //----------------------------------------------------------------------------------------- /** * Inserts the given bean in the database (SQL INSERT) with an auto-incremented key * @param bean * @return the value of the generated key */ protected long doInsertAutoIncr(final T bean) { JdbcTemplate jdbcTemplate = getJdbcTemplate(); // GeneratedKeyHolder : the default implementation of the KeyHolder interface, to be used for holding auto-generated keys KeyHolder keyHolder = new GeneratedKeyHolder(); int result = jdbcTemplate.update(getPreparedStatementCreator(bean), keyHolder ); if ( result != 1 ) { throw new RuntimeException("Unexpected return value after INSERT : " + result + " (1 expected) "); } //--- Retrieve the generated value Number key = keyHolder.getKey(); // Single numeric generated key if ( key != null ) { return key.longValue(); } else { throw new RuntimeException("Cannot retrive generated key after INSERT : KeyHolder returns null"); } } //----------------------------------------------------------------------------------------- /** * Inserts the given bean in the database (SQL INSERT) with an auto-incremented column (usually the primary key) * @param bean * @return the generated value for the auto-incremented column */ protected Long doInsertAutoIncrOLD(T bean ) { JdbcTemplate jdbcTemplate = getJdbcTemplate(); // GeneratedKeyHolder : the default implementation of the KeyHolder interface, to be used for holding auto-generated keys KeyHolder keyHolder = new GeneratedKeyHolder(); //--- FORM 2 with only the KeyHolder int result = jdbcTemplate.update(getSqlInsert(), getValuesForInsert(bean), keyHolder, new String[]{autoIncrColumnName} ); if ( result != 1 ) { throw new RuntimeException("Unexpected return value after INSERT : " + result + " (1 expected) "); } //--- Retrieve the generated value Number key = keyHolder.getKey(); // Single numeric generated key if ( key != null ) { return key.longValue(); } else { throw new RuntimeException("Cannot retrive generated key after INSERT : KeyHolder returns null"); } } //----------------------------------------------------------------------------------------- /** * Updates the given bean in the database (SQL UPDATE) * @param bean the bean to be updated * @return the JDBC return code (i.e. the row count affected by the UPDATE operation : 0 or 1 ) */ protected int doUpdate(T bean) { JdbcTemplate jdbcTemplate = getJdbcTemplate(); //--- Execute UPDATE int result = jdbcTemplate.update( getSqlUpdate(), getValuesForUpdate(bean) ); if ( result != 0 && result != 1 ) { throw new RuntimeException("Unexpected return value after UPDATE : " + result + " (0 or 1 expected) "); } return result ; } //----------------------------------------------------------------------------------------- /** * Deletes the given bean in the database (SQL DELETE) * @param bean the bean to be deleted (containing the Primary Key) * @return the JDBC return code (i.e. the row count affected by the DELETE operation : 0 or 1 ) */ protected int doDelete(T bean) { return doDelete( getValuesForPrimaryKey(bean) ) ; } protected int doDelete(Object[] primaryKey) { JdbcTemplate jdbcTemplate = getJdbcTemplate(); //--- Execute DELETE int result = jdbcTemplate.update(getSqlDelete(), primaryKey); if ( result != 0 && result != 1 ) { throw new RuntimeException("Unexpected return value after DELETE : " + result + " (0 or 1 expected) "); } return result ; } protected boolean doExists(T bean) { return doExists( getValuesForPrimaryKey(bean) ); } protected boolean doExists(Object[] primaryKey) { JdbcTemplate jdbcTemplate = getJdbcTemplate(); long count = jdbcTemplate.queryForObject(getSqlCount(), primaryKey, Long.class); return count > 0 ; } protected long doCountAll() { JdbcTemplate jdbcTemplate = getJdbcTemplate(); return jdbcTemplate.queryForObject(getSqlCountAll(), Long.class); } protected String toString(Object[] objects) { if ( objects != null ) { StringBuilder sb = new StringBuilder(); sb.append("["); int i = 0 ; for ( Object o : objects ) { if ( i > 0 ) { sb.append("|"); } sb.append(o.toString()); i++; } sb.append("]"); return sb.toString(); } else { return "null" ; } }
}
AlertSettingDAO 接口
public interface AlertSettingDAO { /** * Finds a bean by its primary key * @param userToken * @return the bean found or null if not found */ public AlertSetting find( String userToken ) ; //---------------------------------------------------------------------- /** * Loads the given bean, it is supposed to contains the primary key value(s) in its attribute(s)<br> * If found, the given instance is populated with the values retrieved from the database<br> * If not found, the given instance remains unchanged * @param alertSetting * @return true if found, false if not found */ public boolean load( AlertSetting alertSetting ) ; //---------------------------------------------------------------------- /** * Inserts the given bean in the database * @param alertSetting */ public void insert(AlertSetting alertSetting) ; //---------------------------------------------------------------------- /** * Updates the given bean in the database * @param alertSetting * @return */ public int update(AlertSetting alertSetting) ; //---------------------------------------------------------------------- /** * Deletes the record in the database using the given primary key value(s) * @param userToken * @return */ public int delete( String userToken ) ; //---------------------------------------------------------------------- /** * Deletes the given bean in the database * @param alertSetting * @return */ public int delete( AlertSetting alertSetting ) ; //---------------------------------------------------------------------- /** * Checks the existence of a record in the database using the given primary key value(s) * @param userToken * @return */ public boolean exists( String userToken ) ; public boolean exists( AlertSetting alertSetting ) ; public long count() ; }
和 3. AlertSettingDAO 实现类
@Repository 公共类 AlertSettingDAOImplSpring 扩展 GenericDAO 实现 AlertSettingDAO {
private final static String SQL_SELECT =
"select user_token, sms_enabled, sms_settle_enabled, sms_load_enabled, sms_unload_enabled, sms_auth_enabled, sms_decline_enabled, sms_chargeback_enabled, sms_promo_enabled, sms_flagged_for_failures, 2way_enabled, email_alert_enabled from alert_setting where user_token = ?";
private final static String SQL_INSERT =
"insert into alert_setting ( user_token, sms_enabled, sms_settle_enabled, sms_load_enabled, sms_unload_enabled, sms_auth_enabled, sms_decline_enabled, sms_chargeback_enabled, sms_promo_enabled, sms_flagged_for_failures, 2way_enabled, email_alert_enabled ) values ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )";
private final static String SQL_UPDATE =
"update alert_setting set sms_enabled = ?, sms_settle_enabled = ?, sms_load_enabled = ?, sms_unload_enabled = ?, sms_auth_enabled = ?, sms_decline_enabled = ?, sms_chargeback_enabled = ?, sms_promo_enabled = ?, sms_flagged_for_failures = ?, 2way_enabled = ?, email_alert_enabled = ? where user_token = ?";
private final static String SQL_DELETE =
"delete from alert_setting where user_token = ?";
private final static String SQL_COUNT_ALL =
"select count(*) from alert_setting";
private final static String SQL_COUNT =
"select count(*) from alert_setting where user_token = ?";
//----------------------------------------------------------------------
/**
* DAO constructor
*/
public AlertSettingDAOImplSpring() {
super();
}
//----------------------------------------------------------------------
/* (non-Javadoc)
* DAO interface implementation
*/
@Override
public AlertSetting find( String userToken ) {
Object[] primaryKey = new Object[] { userToken };
return super.doSelect(primaryKey);
}
//----------------------------------------------------------------------
/* (non-Javadoc)
* DAO interface implementation
*/
@Override
public boolean load( AlertSetting alertSetting ) {
return super.doSelect(alertSetting) ;
}
//----------------------------------------------------------------------
/* (non-Javadoc)
* DAO interface implementation
*/
@Override
public void insert(AlertSetting alertSetting) {
super.doInsert(alertSetting);
}
//----------------------------------------------------------------------
/* (non-Javadoc)
* DAO interface implementation
*/
@Override
public int update(AlertSetting alertSetting) {
return super.doUpdate(alertSetting);
}
//----------------------------------------------------------------------
/* (non-Javadoc)
* DAO interface implementation
*/
@Override
public int delete( String userToken ) {
Object[] primaryKey = new Object[] { userToken };
return super.doDelete(primaryKey);
}
//----------------------------------------------------------------------
/* (non-Javadoc)
* DAO interface implementation
*/
@Override
public int delete( AlertSetting alertSetting ) {
return super.doDelete(alertSetting);
}
//----------------------------------------------------------------------
/* (non-Javadoc)
* DAO interface implementation
*/
@Override
public boolean exists( String userToken ) {
Object[] primaryKey = new Object[] { userToken };
return super.doExists(primaryKey);
}
//----------------------------------------------------------------------
/* (non-Javadoc)
* DAO interface implementation
*/
@Override
public boolean exists( AlertSetting alertSetting ) {
return super.doExists(alertSetting);
}
//----------------------------------------------------------------------
/* (non-Javadoc)
* DAO interface implementation
*/
@Override
public long count() {
return super.doCountAll();
}
//----------------------------------------------------------------------
// Super class abstract methods implementation
//----------------------------------------------------------------------
@Override
protected String getSqlSelect() {
return SQL_SELECT ;
}
//----------------------------------------------------------------------
@Override
protected String getSqlInsert() {
return SQL_INSERT ;
}
//----------------------------------------------------------------------
@Override
protected String getSqlUpdate() {
return SQL_UPDATE ;
}
//----------------------------------------------------------------------
@Override
protected String getSqlDelete() {
return SQL_DELETE ;
}
//----------------------------------------------------------------------
@Override
protected String getSqlCount() {
return SQL_COUNT ;
}
//----------------------------------------------------------------------
@Override
protected String getSqlCountAll() {
return SQL_COUNT_ALL ;
}
//----------------------------------------------------------------------
@Override
protected Object[] getValuesForInsert(AlertSetting alertSetting) {
return new Object[] {
//--- Returns PRIMARY KEY and DATA ( for SQL "SET x=?, y=?, ..." )
alertSetting.getUserToken() , // "user_token" : java.lang.String
alertSetting.getSmsEnabled() , // "sms_enabled" : java.lang.Integer
alertSetting.getSmsSettleEnabled() , // "sms_settle_enabled" : java.lang.Integer
alertSetting.getSmsLoadEnabled() , // "sms_load_enabled" : java.lang.Integer
alertSetting.getSmsUnloadEnabled() , // "sms_unload_enabled" : java.lang.Integer
alertSetting.getSmsAuthEnabled() , // "sms_auth_enabled" : java.lang.Integer
alertSetting.getSmsDeclineEnabled() , // "sms_decline_enabled" : java.lang.Integer
alertSetting.getSmsChargebackEnabled() , // "sms_chargeback_enabled" : java.lang.Integer
alertSetting.getSmsPromoEnabled() , // "sms_promo_enabled" : java.lang.Integer
alertSetting.getSmsFlaggedForFailures() , // "sms_flagged_for_failures" : java.lang.Integer
alertSetting.getTwoWayEnabled() , // "2way_enabled" : java.lang.Integer
alertSetting.getEmailAlertEnabled() // "email_alert_enabled" : java.lang.Integer
};
}
//----------------------------------------------------------------------
@Override
protected Object[] getValuesForUpdate(AlertSetting alertSetting) {
return new Object[] {
//--- Returns DATA first ( for SQL "SET x=?, y=?, ..." )
alertSetting.getSmsEnabled(), // "sms_enabled" : java.lang.Integer
alertSetting.getSmsSettleEnabled(), // "sms_settle_enabled" : java.lang.Integer
alertSetting.getSmsLoadEnabled(), // "sms_load_enabled" : java.lang.Integer
alertSetting.getSmsUnloadEnabled(), // "sms_unload_enabled" : java.lang.Integer
alertSetting.getSmsAuthEnabled(), // "sms_auth_enabled" : java.lang.Integer
alertSetting.getSmsDeclineEnabled(), // "sms_decline_enabled" : java.lang.Integer
alertSetting.getSmsChargebackEnabled(), // "sms_chargeback_enabled" : java.lang.Integer
alertSetting.getSmsPromoEnabled(), // "sms_promo_enabled" : java.lang.Integer
alertSetting.getSmsFlaggedForFailures(), // "sms_flagged_for_failures" : java.lang.Integer
alertSetting.getTwoWayEnabled(), // "2way_enabled" : java.lang.Integer
alertSetting.getEmailAlertEnabled(), // "email_alert_enabled" : java.lang.Integer
//--- Returns PRIMARY KEY at the end ( for SQL "WHERE key=?, ..." )
alertSetting.getUserToken() // "user_token" : java.lang.String
};
}
//----------------------------------------------------------------------
@Override
protected Object[] getValuesForPrimaryKey(AlertSetting alertSetting) {
return new Object[] {
//--- Returns PRIMARY KEY values ( for SQL "WHERE key=?, ..." )
alertSetting.getUserToken() // "user_token" : java.lang.String
};
}
//----------------------------------------------------------------------
@Override
protected RowMapper<AlertSetting> getRowMapper(AlertSetting o) {
//--- RowMapper to populate the given bean instance
return new AlertSettingRowMapper(o) ;
}
//----------------------------------------------------------------------
@Override
protected RowMapper<AlertSetting> getRowMapper() {
//--- RowMapper to populate a new bean instance
return new AlertSettingRowMapper( new AlertSetting() ) ;
}
//----------------------------------------------------------------------
/**
* Populates the given bean with the data retrieved from the given ResultSet
* @param rs
* @param alertSetting
* @throws SQLException
*/
private void populateBean(ResultSet rs, AlertSetting alertSetting) throws SQLException {
//--- Set data from ResultSet to Bean attributes
alertSetting.setUserToken(rs.getString("user_token")); // java.lang.String
alertSetting.setSmsEnabled(rs.getInt("sms_enabled")); // java.lang.Integer
if ( rs.wasNull() ) { alertSetting.setSmsEnabled(null); }; // not primitive number => keep null value if any
alertSetting.setSmsSettleEnabled(rs.getInt("sms_settle_enabled")); // java.lang.Integer
if ( rs.wasNull() ) { alertSetting.setSmsSettleEnabled(null); }; // not primitive number => keep null value if any
alertSetting.setSmsLoadEnabled(rs.getInt("sms_load_enabled")); // java.lang.Integer
if ( rs.wasNull() ) { alertSetting.setSmsLoadEnabled(null); }; // not primitive number => keep null value if any
alertSetting.setSmsUnloadEnabled(rs.getInt("sms_unload_enabled")); // java.lang.Integer
if ( rs.wasNull() ) { alertSetting.setSmsUnloadEnabled(null); }; // not primitive number => keep null value if any
alertSetting.setSmsAuthEnabled(rs.getInt("sms_auth_enabled")); // java.lang.Integer
if ( rs.wasNull() ) { alertSetting.setSmsAuthEnabled(null); }; // not primitive number => keep null value if any
alertSetting.setSmsDeclineEnabled(rs.getInt("sms_decline_enabled")); // java.lang.Integer
if ( rs.wasNull() ) { alertSetting.setSmsDeclineEnabled(null); }; // not primitive number => keep null value if any
alertSetting.setSmsChargebackEnabled(rs.getInt("sms_chargeback_enabled")); // java.lang.Integer
if ( rs.wasNull() ) { alertSetting.setSmsChargebackEnabled(null); }; // not primitive number => keep null value if any
alertSetting.setSmsPromoEnabled(rs.getInt("sms_promo_enabled")); // java.lang.Integer
if ( rs.wasNull() ) { alertSetting.setSmsPromoEnabled(null); }; // not primitive number => keep null value if any
alertSetting.setSmsFlaggedForFailures(rs.getInt("sms_flagged_for_failures")); // java.lang.Integer
if ( rs.wasNull() ) { alertSetting.setSmsFlaggedForFailures(null); }; // not primitive number => keep null value if any
alertSetting.setTwoWayEnabled(rs.getInt("2way_enabled")); // java.lang.Integer
if ( rs.wasNull() ) { alertSetting.setTwoWayEnabled(null); }; // not primitive number => keep null value if any
alertSetting.setEmailAlertEnabled(rs.getInt("email_alert_enabled")); // java.lang.Integer
if ( rs.wasNull() ) { alertSetting.setEmailAlertEnabled(null); }; // not primitive number => keep null value if any
}
//----------------------------------------------------------------------
/**
* Specific inner class for 'RowMapper' implementation
*/
private class AlertSettingRowMapper implements RowMapper<AlertSetting> {
private final AlertSetting bean ;
AlertSettingRowMapper(AlertSetting bean) {
this.bean = bean ;
}
@Override
public AlertSetting mapRow(ResultSet rs, int rowNum) throws SQLException {
populateBean(rs, this.bean);
return this.bean;
}
}
}
这是我的主要课程
public class Test {
public static void main(String[] args) {
Resource resource=new ClassPathResource("spring-context.xml");
BeanFactory factory=new XmlBeanFactory(resource);
AlertSettingDAOImplSpring a = (AlertSettingDAOImplSpring) factory.getBean("alertSettingDAO");
System.out.println(a.find("Anil").toString());
}
}
Error creating bean with name 'alertSettingDAO' defined in class path resource [spring-context.xml]: Error setting property values; nested exception is org.springframework.beans.NotWritablePropertyException: Invalid property 'dataSource' of bean class [org.jay.dao.impl.spring.AlertSettingDAOImplSpring]: Bean property 'dataSource' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
我哪里错了?如何以正确的方式做到这一点?