項目使用jdbctemplate已經一段時間了,對於jdbcTemplate的使用有了一些小當心得,這裏總結後跟你們分享下。java
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>com.mysql.jdbc.Driver</value> </property> <property name="url"> <value>jdbc:mysql://localhost/shop?characterEncoding=utf-8&autoReconnect=true </value> </property> <property name="username"> <value>root</value> </property> <property name="password"> <value>root</value> </property> </bean> <bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate"> <constructor-arg ref="dataSource" /> </bean> <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> <property name="transactionManager"> <ref bean="transactionManager" /> </property> <property name="transactionAttributes"> <props> <prop key="find*">PROPAGATION_NEVER,readOnly</prop> <prop key="get*">PROPAGATION_NEVER,readOnly</prop> <prop key="load*">PROPAGATION_NEVER,readOnly</prop> <prop key="query*">PROPAGATION_NEVER,readOnly</prop> <prop key="is*">PROPAGATION_NEVER,readOnly</prop> <prop key="has*">PROPAGATION_NEVER,readOnly</prop> <prop key="exist*">PROPAGATION_NEVER,readOnly</prop> <prop key="check*">PROPAGATION_NEVER,readOnly</prop> <prop key="*">PROPAGATION_REQUIRED,-Exception</prop> </props> </property> </bean> <!-- 自動代理 --> <bean id="autoproxy" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <!-- 能夠是Service或DAO層(最好是針對業務層*Service) --> <property name="beanNames"> <list> <value>*Service</value> </list> </property> <property name="interceptorNames"> <list> <value>transactionInterceptor</value> </list> </property> </bean> </beans>
在項目使用中咱們能夠經過spring DBColumnMapper 獲取pojo全部字段,進而拼接insertSqlmysql
創建一個對象用來保存拼接的sql 和參數map spring
class InsertEntity{ Map<String, Object> paramMap; String sql; public Map<String, Object> getParamMap() { return paramMap; } public void setParamMap(Map<String, Object> paramMap) { this.paramMap = paramMap; } public String getSql() { return sql; } public void setSql(String sql) { this.sql = sql; } }
自定義columnMappersql
public class ColumnMapper { private String column; private String filed; private Object value; public Object getValue() { return value; } public void setValue(Object value) { this.value = value; } public String getColumn() { return column; } public void setColumn(String column) { this.column = column; } public String getFiled() { return filed; } public void setFiled(String filed) { this.filed = filed; } }
@Component public class DBColumnMapper { private static Map<String, List<ColumnMapper>> mapper = new HashMap<String, List<ColumnMapper>>(); private static final String UNDERLINE = "_"; public <T> List<ColumnMapper> getColumnMapper(T obj) throws IllegalArgumentException, IllegalAccessException { Class<?> clazz = obj.getClass(); List<ColumnMapper> columnMapperList = mapper.get(clazz.getName()); if(CollectionUtil.isEmpty(columnMapperList)) { columnMapperList = generateColumnMapper(obj); mapper.put(clazz.getName(), columnMapperList); } return columnMapperList; } private <T> List<ColumnMapper> generateColumnMapper(T obj) throws IllegalArgumentException, IllegalAccessException { List<ColumnMapper> columnMapperList = new ArrayList<ColumnMapper>(); Class<?> clazz = obj.getClass(); Field[] fields = clazz.getDeclaredFields(); for(Field field : fields) { field.setAccessible(true); String fieldName = field.getName(); DbIgnore dbIgnore = field.getAnnotation(DbIgnore.class); if(dbIgnore!=null) { logger.debug("the field[{}] has remarked as dbIgnore", fieldName); } if(!"serialVersionUID".equalsIgnoreCase(fieldName) && dbIgnore==null) { ColumnMapper columnMapper = new ColumnMapper(); columnMapper.setFiled(fieldName); columnMapper.setColumn(obtainColumn(fieldName)); columnMapperList.add(columnMapper); } field.setAccessible(false); } return columnMapperList; } private String obtainColumn(String fieldName) { StringBuffer builder = new StringBuffer(); char[] charArray = fieldName.toCharArray(); for(int index = 0; index < charArray.length; index++) { char ch = charArray[index]; if(Character.isUpperCase(ch)) { if(index == 0) { builder.append(Character.toLowerCase(ch)); } else { builder.append(UNDERLINE); builder.append(Character.toLowerCase(ch)); } } else { builder.append(ch); } } return builder.toString(); } }
注入DBColumnMapper數據庫
@Autowired private DBColumnMapper mapper;
泛型拼裝插入sqlapp
private <T> InsertEntity getInsertEntity(T obj, String tableName, String[] excludingNameArray) throws IllegalArgumentException, SecurityException, IllegalAccessException, NoSuchFieldException{ InsertEntity insertEntity = new InsertEntity(); Class<?> clazz = obj.getClass(); List<ColumnMapper> columnMapperList = mapper.getColumnMapper(obj); StringBuffer insertSQL = new StringBuffer(); insertSQL.append("INSERT INTO "); insertSQL.append(tableName); insertSQL.append("("); int count = 0; for(int index = 0; index < columnMapperList.size(); index++) { ColumnMapper mapper = columnMapperList.get(index); String filedName = mapper.getFiled(); if(isIgnoreField(filedName, excludingNameArray)) { //這個方法就不提供了,這裏忽略了不須要保存的字段 continue; } //若是字段值是空的,則不須要拼在腳本中,由於數據庫字段可能設置了非空的,或者設置了默認值的,因此插入null會有問題。 if (mapper.getValue()==null) { continue; } if(count!=0 && index != columnMapperList.size()) { insertSQL.append(","); } insertSQL.append(mapper.getColumn()); count++; } insertSQL.append(")"); insertSQL.append(" "); insertSQL.append("VALUES"); insertSQL.append("("); count = 0; for(int index = 0; index < columnMapperList.size(); index++) { ColumnMapper mapper = columnMapperList.get(index); String filedName = mapper.getFiled(); if(isIgnoreField(filedName, excludingNameArray)) { continue; } if (mapper.getValue()==null) { continue; } if(count!=0 && index != columnMapperList.size()) { insertSQL.append(","); } insertSQL.append(":"); insertSQL.append(mapper.getFiled()); count++; } insertSQL.append(")"); Map<String, Object> paramMap = new HashMap<String, Object>(); Field[] fields = clazz.getDeclaredFields(); for(Field field : fields) { String fieldName = field.getName(); if(!"serialVersionUID".equalsIgnoreCase(fieldName) && !isIgnoreField(fieldName, excludingNameArray)) { field.setAccessible(true); Object val = field.get(obj); Date now = new Date(); if (field.getName().equals("createBy") && val == null) { val = 0; } else if (field.getName().equals("lastModifiedBy") && val == null) { val = 0; } else if (field.getName().equals("createTime") && val == null) { val = now; } else if (field.getName().equals("lastModifiedTime") && val == null) { val = now; } paramMap.put(field.getName(), val); field.setAccessible(false); } } String sql = insertSQL.toString(); insertEntity.setParamMap(paramMap); //由於去掉了爲null的字段,因此拼裝sql的時候可能會出錯 logger.info("insert sql={}", sql); insertEntity.setSql(sql); return insertEntity; }
KeyHolder holder = new GeneratedKeyHolder(); int rowNum = jdbcTemplate.update(insertEntity.getSql(), paramSource, holder); if(rowNum > 0) { return holder.getKey().intValue(); } else { return null; }
spring 後來提供的namedParameterJdbcTemplate 涵蓋了jdbcTemplate的全部用法,並支持數據可字段和pojo的自動映射,更妙的是namedParameterJdbcTemplate 會自動將數據庫的下劃線命名的字段自動轉爲pojo的駝峯命名(可能描述的不太恰當),下面是經過rowmapper的一種使用方法。ide
List<Product> products = jdbcTemplate.query(sql,paramMap, new BeanPropertyRowMapper<Product>(Product.class));
這裏用反覆int舉了個例子,也能夠給自定義類添加ResultSetExtractor ui
int total = jdbcTemplate.query(pc.getCountSql(), paramMap, new ResultSetExtractor<Integer>() { @Override public Integer extractData(ResultSet rs) throws SQLException, DataAccessException { if (rs.next()) { return rs.getInt(1); } return 0; } });
喜歡jdbctemplate了this
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface DbIgnore { boolean value() default true; }