项目使用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; }