一、Java 反射机制
二、利用反射实现数据库操作
三、数据对象与json的转换
一、Java 反射机制
二、利用反射实现数据库操作
数据库操作,反射的好处:
(1) dao 层只需要编写一次基础数据库类操作代码(比如,增删改查等等),就可以充分利用继承关系实现不同模型的数据库操作。
(2) 修改数据库的表字段基本上不用大的修改 dao 层的代码(比如 sql 语句等等)
本例工程的目录组织结构大致如下:

也可以将 LTBeanMapConverter.java 放到 dao.common 包中。
1、本实例使用 mysql 数据库。基础配置文件,请参考上一篇文章《MySQL + Tomcat 工程》 http://www.cnblogs.com/matchboxy/articles/4113834.html
2、编写 model 类,本例中为 LTNews.java 类。
1 package com.matchbox.model.news; 2 3 import java.io.Serializable; 4 5 import javax.persistence.Column; 6 import javax.persistence.GeneratedValue; 7 import javax.persistence.GenerationType; 8 import javax.persistence.Id; 9 import javax.persistence.Table; 10 11 12 /** 13 * @author MATCH 14 * @date 14-12-29 15 */ 16 17 18 @Table(name="news") 19 public class LTNews implements Serializable 20 { 21 private long id; 22 private long time; 23 private String url; 24 private String imgUrl; 25 private String type; 26 private String title; 27 private String author; 28 private String source; 29 private String outline; 30 private int language; 31 private int favourite; 32 private int comment; 33 private int click; 34 35 @Column(name="id") 36 @Id 37 @GeneratedValue(strategy = GenerationType.IDENTITY) 38 public long getId() { 39 return id; 40 } 41 42 public void setId(long id) { 43 this.id = id; 44 } 45 46 @Column(name="time") 47 public long getTime() { 48 return time; 49 } 50 51 public void setTime(long time) { 52 this.time = time; 53 } 54 55 @Column(name="url") 56 public String getUrl() { 57 return url; 58 } 59 60 public void setUrl(String url) { 61 this.url = url; 62 } 63 64 @Column(name="imgUrl") 65 public String getImgUrl() { 66 return imgUrl; 67 } 68 69 public void setImgUrl(String imgUrl) { 70 this.imgUrl = imgUrl; 71 } 72 73 @Column(name="type") 74 public String getType() { 75 return type; 76 } 77 78 public void setType(String type) { 79 this.type = type; 80 } 81 82 @Column(name="title") 83 public String getTitle() { 84 return title; 85 } 86 87 public void setTitle(String title) { 88 this.title = title; 89 } 90 91 @Column(name="author") 92 public String getAuthor() { 93 return author; 94 } 95 96 public void setAuthor(String author) { 97 this.author = author; 98 } 99 100 @Column(name="source") 101 public String getSource() { 102 return source; 103 } 104 105 public void setSource(String source) { 106 this.source = source; 107 } 108 109 @Column(name="outline") 110 public String getOutline() { 111 return outline; 112 } 113 114 public void setOutline(String outline) { 115 this.outline = outline; 116 } 117 118 @Column(name="language") 119 public int getLanguage() { 120 return language; 121 } 122 123 public void setLanguage(int language) { 124 this.language = language; 125 } 126 127 @Column(name="favourite") 128 public int getFavourite() { 129 return favourite; 130 } 131 132 public void setFavourite(int favourite) { 133 this.favourite = favourite; 134 } 135 136 @Column(name="comment") 137 public int getComment() { 138 return comment; 139 } 140 141 public void setComment(int comment) { 142 this.comment = comment; 143 } 144 145 @Column(name="click") 146 public int getClick() { 147 return click; 148 } 149 150 public void setClick(int click) { 151 this.click = click; 152 } 153 }
其中:@Table(name="news") 为 news 数据库表。
@Column(name="id")
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
为表中列名为 id 的列为主键,自增长类型。每一个 @Column(name="***") 写在 get 方法前面,对应数据表中的列名到相应的属性,一般来讲属性名最好与列名一致。
3、编写基础接口类 dao.common.LTGenericDao.java ,主要声明一些要实现的数据库操作。
1 package com.matchbox.dao.common; 2 3 import java.io.Serializable; 4 import java.util.List; 5 import java.util.Map; 6 7 8 /** 9 * @author MATCH 10 * @date 14-12-30 11 */ 12 13 14 public interface LTGenericDao <T, ID extends Serializable> 15 { 16 public String getTableName(); 17 public String getPk(); 18 public List<T> getAll(); 19 public long getCount(); 20 21 public ID insert(T object); 22 public List<ID> insertBatch(List<T> listObject); 23 24 public int delete(ID id); 25 public int deleteBatch(List<ID> listIds); 26 27 public int update(T object); 28 29 public T select(ID id); 30 public List<T> selectByIds(List<ID> ids); 31 public List<T> selectByMap(Map<String, Object> map); 32 }
这里需要运用模板的知识,其中: T 为模型类的 class , ID 为模型类对应的表的主键类型。
4、编写基础实现类 dao.common.impl.LTGenericDaoImpl.java ,提供接口类的基础实现。
1 package com.matchbox.dao.common.impl; 2 3 import java.beans.BeanInfo; 4 import java.beans.IntrospectionException; 5 import java.beans.Introspector; 6 import java.beans.PropertyDescriptor; 7 import java.io.Serializable; 8 import java.sql.Connection; 9 import java.sql.PreparedStatement; 10 import java.sql.SQLException; 11 import java.sql.Statement; 12 import java.sql.Types; 13 import java.util.ArrayList; 14 import java.util.Arrays; 15 import java.util.Date; 16 import java.util.HashMap; 17 import java.util.List; 18 import java.util.Map; 19 20 import javax.persistence.Column; 21 import javax.persistence.GeneratedValue; 22 import javax.persistence.GenerationType; 23 import javax.persistence.Id; 24 import javax.persistence.Table; 25 import javax.persistence.Transient; 26 27 import org.apache.commons.beanutils.BeanUtils; 28 import org.apache.commons.lang.StringUtils; 29 import org.springframework.core.annotation.AnnotationUtils; 30 import org.springframework.jdbc.core.JdbcTemplate; 31 import org.springframework.jdbc.core.PreparedStatementCreator; 32 import org.springframework.jdbc.core.support.JdbcDaoSupport; 33 import org.springframework.jdbc.support.GeneratedKeyHolder; 34 import org.springframework.jdbc.support.KeyHolder; 35 36 import com.matchbox.dao.common.LTGenericDao; 37 import com.matchbox.dao.common.LTModelPropertyRowMapper; 38 import com.matchbox.utility.LTBeanMapConverter; 39 40 41 /** 42 * @author MATCH 43 * @date 14-12-30 44 */ 45 46 47 public abstract class LTGenericDaoImpl<T, ID extends Serializable> 48 extends JdbcDaoSupport implements LTGenericDao<T, ID> 49 { 50 private Class<T> persistentClass; 51 private String tableName = ""; 52 private String pk = ""; 53 private GenerationType strategy; 54 private Map<String, String> property2ColumnMap = new HashMap<String, String>(); 55 private Map<String, String> column2PropertyMap = new HashMap<String, String>(); 56 protected List<String> transientPropertys = new ArrayList<String>(); 57 58 protected LTGenericDaoImpl(Class<T> tClass) 59 { 60 this.persistentClass = tClass; 61 62 Table table = AnnotationUtils.findAnnotation(tClass, Table.class); 63 if(null == table) 64 { 65 //throw new DaoException(persistentClass.getName() + "没有定义@table"); 66 } 67 68 this.tableName = (table==null) ? tClass.getName() : table.name(); 69 70 BeanInfo beanInfo = null; 71 try 72 { 73 beanInfo = Introspector.getBeanInfo(persistentClass); 74 } 75 catch (IntrospectionException e) 76 { 77 //throw new DaoException(e); 78 } 79 80 PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors(); 81 for(PropertyDescriptor pd : pds) 82 { 83 Id id = AnnotationUtils.findAnnotation(pd.getReadMethod(), Id.class); 84 if(null != id) 85 { 86 Column idColumn = AnnotationUtils.findAnnotation(pd.getReadMethod(), Column.class); 87 this.pk = (idColumn == null) ? pd.getName() : idColumn.name(); 88 89 GeneratedValue gv = AnnotationUtils.findAnnotation(pd.getReadMethod(), GeneratedValue.class); 90 this.strategy = (gv == null) ? GenerationType.IDENTITY : gv.strategy(); 91 } 92 93 Column column = AnnotationUtils.findAnnotation(pd.getReadMethod(), Column.class); 94 property2ColumnMap.put(pd.getName(), (column == null) ? pd.getName() : column.name()); 95 column2PropertyMap.put((column == null) ? pd.getName() : column.name(), pd.getName()); 96 97 Transient transient_ = AnnotationUtils.findAnnotation(pd.getReadMethod(), Transient.class); 98 if (null != transient_) 99 { 100 transientPropertys.add(pd.getName()); 101 } 102 } 103 104 if ("".equals(this.getPk())) 105 { 106 //throw new DaoException(persistentClass.getName() + "中没有在get方法上定义@Id"); 107 } 108 } 109 110 111 @Override 112 public String getTableName() 113 { 114 return this.tableName; 115 } 116 117 @Override 118 public String getPk() 119 { 120 return this.pk; 121 } 122 123 @Override 124 public List<T> getAll() 125 { 126 return null; 127 } 128 129 @Override 130 public long getCount() 131 { 132 StringBuilder sb = new StringBuilder("select count(*) from "); 133 sb.append(this.getTableName()); 134 return this.getJdbcTemplate().queryForObject(sb.toString(), Long.class); 135 } 136 137 /** 138 * 插入一条数据 139 */ 140 @Override 141 public ID insert(T object) 142 { 143 if (null == object) 144 { 145 //throw new DaoException("模型对象为空!保存失败"); 146 } 147 148 Map<String, Object> map = toModelMap(object); 149 for(String proterty : transientPropertys) 150 { 151 map.remove(proterty); 152 } 153 154 ID id = this.insertReturnIdByMap(map); 155 if(strategy.equals(GenerationType.IDENTITY)) 156 { 157 try 158 { 159 BeanUtils.setProperty(object, column2PropertyMap.get(pk), id); 160 } 161 catch(Exception e) 162 { 163 //log.error(persistentClass.getName() + "解析异常!", e); 164 } 165 } 166 return id; 167 } 168 169 /** 170 * 根据列名与对应的属性值,添加一条记录。 171 * 返回添加的主键id 172 */ 173 @SuppressWarnings("unchecked") 174 protected ID insertReturnIdByMap(Map<String, Object> map) 175 { 176 if (map == null || map.size() == 0) 177 return null; 178 179 if (this.strategy.equals(GenerationType.IDENTITY)) 180 { 181 map.remove(this.getPk()); 182 } 183 184 StringBuilder sb = new StringBuilder("insert into "); 185 sb.append(this.getTableName()); 186 187 List<String> columns = new ArrayList<String>(); 188 List<Object> values = new ArrayList<Object>(); 189 190 for (Map.Entry<String, Object> e : map.entrySet()) 191 { 192 columns.add(e.getKey()); 193 values.add(e.getValue()); 194 } 195 196 sb.append("(`"); 197 sb.append(StringUtils.join(columns, "`,`")); 198 sb.append("`) values("); 199 String[] paras = new String[values.size()]; 200 Arrays.fill(paras, "?"); 201 sb.append(StringUtils.join(paras, ',')); 202 sb.append(")"); 203 204 if(this.strategy.equals(GenerationType.IDENTITY)) //主键自增长 205 { 206 ID id = this.insertBySqlValuesIdentity(sb.toString(), values); 207 return (ID) id; 208 } 209 else if(this.strategy.equals(GenerationType.AUTO)) //主键由程序控制 210 { 211 int count = this.insertBySqlValuesAuto(sb.toString(), values); 212 if(count != 0) 213 { 214 return (ID) map.get(this.getPk()); 215 } 216 else 217 { 218 return null; 219 } 220 } 221 return null; 222 } 223 224 /** 225 * 根据sql和相关列值,插入一条记录。 226 * 主键自增长 227 * 返回主键值 228 */ 229 @SuppressWarnings("unchecked") 230 //@Override 231 public ID insertBySqlValuesIdentity(final String sql, final List<Object> values) 232 { 233 JdbcTemplate template = this.getJdbcTemplate(); 234 KeyHolder keyHolder = new GeneratedKeyHolder(); 235 236 template.update(new PreparedStatementCreator() 237 { 238 public PreparedStatement createPreparedStatement(Connection con) throws SQLException 239 { 240 int i = 0; 241 PreparedStatement ps = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); 242 for (i = 0; i < values.size(); i++) 243 { 244 Object value = values.get(i); 245 if (value != null) 246 { 247 if (value instanceof Integer) { 248 ps.setInt(i + 1, (Integer) value); 249 } else if (value instanceof Long) { 250 ps.setLong(i + 1, (Long) value); 251 } else if (value instanceof Date) { 252 ps.setDate(i + 1, new java.sql.Date(((Date) value).getTime())); 253 ps.setTimestamp(i + 1, new java.sql.Timestamp(((Date) value).getTime())); 254 } else if (value instanceof String) { 255 ps.setString(i + 1, value.toString()); 256 } else if (value instanceof Double) { 257 ps.setDouble(i + 1, (Double) value); 258 } else if (value instanceof Byte) { 259 ps.setByte(i + 1, (Byte) value); 260 } else if (value instanceof Character) { 261 ps.setString(i + 1, value.toString()); 262 } else if (value instanceof Float) { 263 ps.setFloat(i + 1, (Float) value); 264 } else if (value instanceof Boolean) { 265 ps.setBoolean(i + 1, (Boolean) value); 266 } else if (value instanceof Short) { 267 ps.setShort(i + 1, (Short) value); 268 } else { 269 ps.setObject(i + 1, value); 270 } 271 } 272 else 273 { 274 ps.setNull(i + 1, Types.NULL); 275 } 276 } 277 return ps; 278 } 279 }, keyHolder); 280 281 return (ID) (Long) keyHolder.getKey().longValue(); 282 } 283 284 /** 285 * 根据sql和相关列值,插入一条记录。 286 * 主键值由程序设定 287 * 返回受影响的行数 288 */ 289 protected int insertBySqlValuesAuto(final String sql, List<Object> values) 290 { 291 if (StringUtils.isEmpty(sql)) 292 return 0; 293 294 if (values == null) 295 values = new ArrayList<Object>(); 296 297 return this.getJdbcTemplate().update(sql, values.toArray()); 298 } 299 300 /** 301 * 插入一组数据 302 */ 303 @Override 304 public List<ID> insertBatch(List<T> listObject) 305 { 306 List<ID> listID = new ArrayList<ID>(); 307 ID id; 308 309 if(null == listObject) 310 return listID; 311 312 for(T object : listObject) 313 { 314 id = this.insert(object); 315 if(null != id) 316 listID.add(id); 317 } 318 319 return listID; 320 } 321 322 /** 323 * 根据 id 删除一条数据 324 */ 325 @Override 326 public int delete(ID id) 327 { 328 if(null == id) 329 return 0; 330 331 StringBuilder sb = new StringBuilder("delete from "); 332 sb.append(this.getTableName()); 333 sb.append(" where "); 334 sb.append(this.getPk()); 335 sb.append("=?"); 336 337 List<Object> values = new ArrayList<Object>(); 338 values.add(id); 339 340 return this.deleteBySqlValues(sb.toString(), values); 341 } 342 343 /** 344 * 删除一组数据 345 */ 346 @Override 347 public int deleteBatch(List<ID> listIds) 348 { 349 if((null == listIds) || listIds.isEmpty()) 350 return 0; 351 352 StringBuilder sb = new StringBuilder("delete from "); 353 sb.append(this.getTableName()); 354 sb.append(" where "); 355 sb.append(this.getPk()); 356 sb.append(" in ("); 357 Serializable[] ss = new Serializable[listIds.size()]; 358 Arrays.fill(ss, "?"); 359 sb.append(StringUtils.join(ss, ',')); 360 sb.append(")"); 361 List<Object> values = new ArrayList<Object>(); 362 values.addAll(listIds); 363 364 return this.deleteBySqlValues(sb.toString(), values); 365 } 366 367 /** 368 * 根据sql和相关列值,删除一条记录。 369 * 返回受影响的行数 370 */ 371 protected int deleteBySqlValues(final String sql, final List<Object> values) 372 { 373 return this.getJdbcTemplate().update(sql, values.toArray()); 374 } 375 376 /** 377 * 修改一条数据 378 */ 379 @Override 380 public int update(T object) 381 { 382 if (null == object) 383 { 384 //throw new DaoException("模型对象为空!"); 385 } 386 387 Map<String, Object> map = null; 388 try 389 { 390 map = toModelMap(object); 391 for(String proterty : transientPropertys) 392 { 393 map.remove(proterty); 394 } 395 } 396 catch(Exception e) 397 { 398 //throw new DaoException(persistentClass.getName() + "解析异常!", e); 399 } 400 401 int count = this.updateReturnIdByMap(map); 402 if(count == 0) 403 { 404 //throw new DaoException(persistentClass.getName() + "更新失败" + object); 405 } 406 return count; 407 } 408 409 /** 410 * 根据列名与对应的属性值,更新一条记录。 411 * 返回更新的主键id 412 */ 413 protected int updateReturnIdByMap(Map<String, Object> map) 414 { 415 if((null == map) || (map.size() == 0)) 416 return 0; 417 418 Serializable id = (Serializable) map.get(this.getPk()); 419 if ((null == id) || "".equals(id)) 420 return 0; 421 422 List<Object> values = new ArrayList<Object>(); 423 424 StringBuilder sb = new StringBuilder("update "); 425 sb.append(this.getTableName()); 426 sb.append(" set "); 427 428 map.remove(this.getPk()); 429 430 for (Map.Entry<String, Object> e : map.entrySet()) 431 { 432 sb.append('`').append(e.getKey()).append('`'); 433 sb.append("=?, "); 434 values.add(e.getValue()); 435 } 436 deleteLastStr(sb, ","); 437 sb.append(" where "); 438 sb.append(this.getPk()); 439 sb.append("=?"); 440 values.add(id); 441 map.put(this.getPk(), id); 442 443 return this.updateBySqlValues(sb.toString(), values); 444 } 445 446 /** 447 * 根据sql和相关列值,更新一条记录 448 * 返回受影响的行数 449 */ 450 protected int updateBySqlValues(final String sql, List<Object> values) 451 { 452 if (StringUtils.isEmpty(sql)) 453 return 0; 454 455 if (null == values) 456 values = new ArrayList<Object>(); 457 458 return this.getJdbcTemplate().update(sql, values.toArray()); 459 } 460 461 /** 462 * 根据id查找一条数据 463 * 返回这条数据 464 */ 465 @Override 466 public T select(ID id) 467 { 468 if(null == id) 469 return null; 470 471 StringBuilder sb = new StringBuilder("select * from "); 472 sb.append(this.getTableName()); 473 sb.append(" where "); 474 sb.append(this.getPk()); 475 sb.append("=?"); 476 477 List<Object> values = new ArrayList<Object>(); 478 values.add(id); 479 480 List<T> list = this.selectBySqlValues(sb.toString(), values); 481 if (list == null || list.size() == 0) 482 return null; 483 else 484 return list.get(0); 485 } 486 487 /** 488 * 根据一组id,查找相应一组数据。 489 * 返回找到的数据 490 */ 491 @Override 492 public List<T> selectByIds(List<ID> ids) 493 { 494 if((null == ids) || ids.isEmpty()) 495 return (new ArrayList<T>()); 496 497 StringBuilder sb = new StringBuilder("select * from "); 498 sb.append(this.getTableName()); 499 sb.append(" where "); 500 sb.append(this.getPk()); 501 sb.append(" in ("); 502 503 Serializable[] ss = new Serializable[ids.size()]; 504 Arrays.fill(ss, "?"); 505 sb.append(StringUtils.join(ss, ',')); 506 sb.append(")"); 507 508 List<Object> values = new ArrayList<Object>(); 509 values.addAll(ids); 510 511 return this.selectBySqlValues(sb.toString(), values); 512 } 513 514 /** 515 * 根据给定的属性与属性值对查找记录 516 * 返回找到的数据 517 */ 518 @Override 519 public List<T> selectByMap(Map<String, Object> map) 520 { 521 if (map == null || map.isEmpty()) 522 return null; 523 524 @SuppressWarnings("unchecked") 525 ID id = (ID) map.get(this.getPk()); 526 if (id != null) 527 { 528 map.remove(id); 529 } 530 531 List<String> removekeys = new ArrayList<String>(); 532 for (Map.Entry<String, Object> entry : map.entrySet()) 533 { 534 if (entry.getValue() == null) 535 { 536 removekeys.add(entry.getKey()); 537 } 538 } 539 540 for (String key : removekeys) 541 { 542 map.remove(key); 543 } 544 545 List<Object> values = new ArrayList<Object>(); 546 StringBuilder sb = new StringBuilder("select * from "); 547 sb.append(this.getTableName()); 548 if (map.size() != 0) 549 { 550 sb.append(" where "); 551 for (Map.Entry<String, Object> entry : map.entrySet()) 552 { 553 sb.append('`').append(entry.getKey()).append('`'); 554 sb.append("=? "); 555 values.add(entry.getValue()); 556 sb.append(" and "); 557 } 558 this.deleteLastStr(sb, "and"); 559 } 560 return this.selectBySqlValues(sb.toString(), values); 561 } 562 563 /** 564 * 根据sql和相关列值,选择数据 565 * 返回受影响的行数 566 */ 567 protected List<T> selectBySqlValues(final String sql, List<Object> values) 568 { 569 if (StringUtils.isEmpty(sql)) 570 return new ArrayList<T>(); 571 572 if (values == null) 573 values = new ArrayList<Object>(); 574 575 List<T> list = this.getJdbcTemplate().query(sql, values.toArray(), 576 new LTModelPropertyRowMapper<T>(this.persistentClass, property2ColumnMap)); 577 578 return (list == null) ? new ArrayList<T>() : list; 579 } 580 581 582 /** 583 * 删除给定字符之后的字符串 584 * 585 * @param sb 586 * @param str 587 */ 588 private void deleteLastStr(StringBuilder sb, String str) 589 { 590 int index = sb.lastIndexOf(str); 591 if (index != -1) 592 { 593 sb.delete(index, index + str.length()); 594 } 595 } 596 597 /** 598 * 用于将Model转变成Map,key为属性对应的列名,value为属性对应的值。 599 * 600 * @param object 601 * @return 602 */ 603 private Map<String, Object> toModelMap(T object) { 604 Map<String, Object> modelMap = null; 605 try 606 { 607 Map<String, Object> map = LTBeanMapConverter.convertMapFromBean(object); 608 modelMap = new HashMap<String, Object>(); 609 for (Map.Entry<String, Object> e : map.entrySet()) 610 { 611 modelMap.put(property2ColumnMap.get(e.getKey()), e.getValue()); 612 } 613 } 614 catch (Exception e) 615 { 616 //throw new DaoException(persistentClass.getName() + "解析异常!", e); 617 } 618 return modelMap; 619 } 620 }
public abstract class LTGenericDaoImpl<T, ID extends Serializable>
extends JdbcDaoSupport implements LTGenericDao<T, ID>
抽象类,继承 JdbcDaoSupport ,实现接口 LTGenericDao 。
这里只提供最基础的一些操作实现,其他复杂操作,可针对项目的需求,在接口里面添加方法,然后在这里实现。具体方法实现,可参考本类中其他方法。
5、步骤 4 中的实现类里面,需要有两个类的支持:
1 package com.matchbox.utility; 2 3 import java.beans.BeanInfo; 4 import java.beans.IntrospectionException; 5 import java.beans.Introspector; 6 import java.beans.PropertyDescriptor; 7 import java.lang.reflect.InvocationTargetException; 8 import java.lang.reflect.Method; 9 import java.util.HashMap; 10 import java.util.Map; 11 12 public class LTBeanMapConverter 13 { 14 /** 15 * 静态方法,将 model bean 转化为 map 16 */ 17 public static Map<String, Object> convertMapFromBean(Object bean) 18 throws IntrospectionException, IllegalAccessException, 19 IllegalArgumentException, InvocationTargetException 20 { 21 if(null == bean) 22 { 23 return (new HashMap<String, Object>()); 24 } 25 26 BeanInfo info = Introspector.getBeanInfo(bean.getClass()); 27 PropertyDescriptor[] descriptors = info.getPropertyDescriptors(); 28 29 Map<String, Object> description = new HashMap<String, Object>(); 30 Object value; 31 32 for (int i = 0; i < descriptors.length; i++) 33 { 34 String name = descriptors[i].getName(); 35 Method reader = descriptors[i].getReadMethod(); 36 if (reader != null) 37 { 38 // Object[] os = new Object[0]; 39 // value = reader.invoke(bean, os); 40 41 value = reader.invoke(bean); 42 description.put(name, value); 43 } 44 } 45 description.remove("class"); 46 return description; 47 } 48 }
1 package com.matchbox.dao.common; 2 3 import java.beans.PropertyDescriptor; 4 import java.sql.ResultSet; 5 import java.sql.ResultSetMetaData; 6 import java.sql.SQLException; 7 import java.util.HashMap; 8 import java.util.HashSet; 9 import java.util.Map; 10 import java.util.Set; 11 12 import org.springframework.beans.BeanUtils; 13 import org.springframework.beans.BeanWrapper; 14 import org.springframework.beans.NotWritablePropertyException; 15 import org.springframework.beans.PropertyAccessorFactory; 16 import org.springframework.beans.TypeMismatchException; 17 import org.springframework.jdbc.core.RowMapper; 18 import org.springframework.jdbc.support.JdbcUtils; 19 20 21 /** 22 * @author MATCH 23 * @date 14-12-31 24 */ 25 26 27 public class LTModelPropertyRowMapper<T> implements RowMapper<T> 28 { 29 private Class<T> mappedClass; 30 private Map<String, PropertyDescriptor> mappedFields; 31 private Set<String> mappedProperties; 32 33 public LTModelPropertyRowMapper(Class<T> mappedClass, Map<String, String> property2ColumnMap) 34 { 35 initialize(mappedClass, property2ColumnMap); 36 } 37 38 protected void initialize(Class<T> mappedClass, Map<String, String> property2ColumnMap) 39 { 40 this.mappedClass = mappedClass; 41 this.mappedFields = new HashMap<String, PropertyDescriptor>(); 42 this.mappedProperties = new HashSet<String>(); 43 PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(mappedClass); 44 45 for(PropertyDescriptor pd : pds) 46 { 47 if(pd.getWriteMethod() != null) 48 { 49 this.mappedFields.put(pd.getName().toLowerCase(), pd); 50 String underscoredName = underscoreName(pd.getName()); 51 if(!pd.getName().toLowerCase().equals(underscoredName)) 52 { 53 this.mappedFields.put(underscoredName, pd); 54 } 55 mappedFields.put(property2ColumnMap.get(pd.getName()), pd); 56 this.mappedProperties.add(pd.getName()); 57 } 58 } 59 } 60 61 /** 62 * 将实例的属性,转变为带有下标的形式。 63 * @param name 64 * @return 65 */ 66 private String underscoreName(String name) 67 { 68 StringBuilder result = new StringBuilder(); 69 if ((null != name) && (name.length() > 0)) 70 { 71 result.append(name.substring(0, 1).toLowerCase()); 72 for (int i=1; i<name.length(); i++) 73 { 74 String s = name.substring(i, i + 1); 75 if (s.equals(s.toUpperCase())) 76 { 77 result.append("_"); 78 result.append(s.toLowerCase()); 79 } 80 else 81 { 82 result.append(s); 83 } 84 } 85 } 86 return result.toString(); 87 } 88 89 /** 90 * 必须要重写的方法,指定查找的某行数据 91 */ 92 public T mapRow(ResultSet rs, int rowNumber) throws SQLException 93 { 94 T mappedObject = BeanUtils.instantiate(this.mappedClass); 95 BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(mappedObject); 96 97 ResultSetMetaData rsmd = rs.getMetaData(); 98 int columnCount = rsmd.getColumnCount(); 99 100 for (int index = 1; index <= columnCount; index++) { 101 String column = JdbcUtils.lookupColumnName(rsmd, index); 102 PropertyDescriptor pd = this.mappedFields.get(column.replaceAll(" ", "").toLowerCase()); 103 if(pd != null) 104 { 105 try 106 { 107 Object value = getColumnValue(rs, index, pd); 108 if(value == null) 109 { 110 continue; 111 } 112 try 113 { 114 bw.setPropertyValue(pd.getName(), value); 115 } 116 catch(TypeMismatchException e) 117 { 118 //throw new DaoException("模型类"+this.mappedClass.getName()+"的属性匹配异常", e); 119 } 120 } 121 catch (NotWritablePropertyException ex) 122 { 123 //throw new DaoException("模型类"+this.mappedClass.getName()+"中没有定义"+pd.getName()+"的set方法", ex); 124 } 125 } 126 } 127 return mappedObject; 128 } 129 130 protected Object getColumnValue(ResultSet rs, int index, PropertyDescriptor pd) 131 throws SQLException 132 { 133 return JdbcUtils.getResultSetValue(rs, index, pd.getPropertyType()); 134 } 135 }
其中: LTBeanMapConverter 类是 model bean 和 map 的转换。 LTModelPropertyRowMapper 类是 select 方法需要的,将一行数据转化为 model 。
6、编写 news 数据处理接口类 dao.news.LTNewsDao.java ,向外部提供操作 news 数据模型类的接口。
1 package com.matchbox.dao.news; 2 3 import java.util.List; 4 5 import com.matchbox.model.news.LTNews; 6 7 8 /** 9 * @author MATCH 10 * @date 14-12-30 11 */ 12 13 14 public interface LTNewsDao 15 { 16 public long insertNews(LTNews news); 17 public List<Long> insertNewsBatch(List<LTNews> listNews); 18 19 public int deleteNews(long id); 20 public int deleteNewsBatch(List<Long> listIds); 21 22 public int updateNews(LTNews news); 23 24 public LTNews selectNews(long id); 25 public List<LTNews> selectNewsBatch(List<Long> listIds); 26 }
7、编写 news 数据处理实现类 dao.news.impl.LTNewsDaoImpl.java ,提供 news 模型类操作的实现。
1 package com.matchbox.dao.news.impl; 2 3 import java.util.List; 4 5 import com.matchbox.dao.common.impl.LTGenericDaoImpl; 6 import com.matchbox.dao.news.LTNewsDao; 7 import com.matchbox.model.news.LTNews; 8 9 10 /** 11 * @author MATCH 12 * @date 14-12-30 13 */ 14 15 16 public class LTNewsDaoImpl extends LTGenericDaoImpl<LTNews, Long> implements LTNewsDao 17 { 18 protected LTNewsDaoImpl() 19 { 20 super(LTNews.class); 21 } 22 23 @Override 24 public long insertNews(LTNews news) 25 { 26 return this.insert(news); 27 } 28 29 @Override 30 public List<Long> insertNewsBatch(List<LTNews> listNews) 31 { 32 return this.insertBatch(listNews); 33 } 34 35 @Override 36 public int deleteNews(long id) 37 { 38 return this.delete(id); 39 } 40 41 @Override 42 public int deleteNewsBatch(List<Long> listIds) 43 { 44 return this.deleteBatch(listIds); 45 } 46 47 @Override 48 public int updateNews(LTNews news) 49 { 50 return this.update(news); 51 } 52 53 @Override 54 public LTNews selectNews(long id) 55 { 56 return this.select(id); 57 } 58 59 @Override 60 public List<LTNews> selectNewsBatch(List<Long> listIds) 61 { 62 return this.selectByIds(listIds); 63 } 64 }
其中: public class LTNewsDaoImpl extends LTGenericDaoImpl<LTNews, Long> implements LTNewsDao { ... }
需要指定扩展类的模板 <LTNews, Long> 表示数据模型为 LTNews 类,主键为 Long 类型的。
8、编写 api 转发类 LTNewsController.java
1 package com.matchbox.api; 2 3 import javax.servlet.http.HttpServletRequest; 4 import javax.servlet.http.HttpServletResponse; 5 6 import org.springframework.beans.factory.annotation.Autowired; 7 import org.springframework.stereotype.Controller; 8 import org.springframework.http.HttpStatus; 9 import org.springframework.web.bind.annotation.PathVariable; 10 import org.springframework.web.bind.annotation.RequestMapping; 11 import org.springframework.web.bind.annotation.RequestMethod; 12 import org.springframework.web.bind.annotation.ResponseBody; 13 import org.springframework.web.bind.annotation.ResponseStatus; 14 15 import com.matchbox.dao.news.LTNewsDao; 16 import com.matchbox.model.news.LTNews; 17 18 19 //rootPath = http://127.0.0.1:8080/api 20 @Controller 21 @RequestMapping("/news") //rootPath += "/news" 22 public class LTNewsController 23 { 24 @Autowired 25 private LTNewsDao newsDao; 26 27 //rootPath += "/insert" 28 @RequestMapping(value="/insert", method=RequestMethod.GET) 29 @ResponseStatus(HttpStatus.OK) 30 @ResponseBody 31 public String insert() 32 { 33 //time, url, imgUrl, type, title, author, source, outline, language, favourite, comment, click 34 35 LTNews news = new LTNews(); 36 news.setTime(1419927688); 37 news.setUrl("http://insertByNewsController/url_1"); 38 news.setImgUrl(""); 39 news.setType("1,2,3"); 40 news.setTitle("题目 insertByNewsController1"); 41 news.setAuthor(""); 42 news.setSource("来源"); 43 news.setOutline("<html><body>内容 insertByNewsController1</body></html>"); 44 news.setLanguage(1); 45 news.setFavourite(0); 46 news.setComment(0); 47 news.setClick(0); 48 49 return "insert test id: " + this.newsDao.insertNews(news); 50 } 51 52 //rootPath += "/delete" 53 @RequestMapping(value="/delete/{newsId}", method=RequestMethod.GET) 54 @ResponseStatus(HttpStatus.OK) 55 @ResponseBody 56 public String deleteById(@PathVariable String newsId) 57 { 58 this.newsDao.deleteNews(Long.valueOf(newsId)); 59 60 return "delete id: " + newsId; 61 } 62 63 //rootPath += "/update" 64 @RequestMapping(value="/update", method=RequestMethod.GET) 65 @ResponseStatus(HttpStatus.OK) 66 @ResponseBody 67 public String update() 68 { 69 //time, url, imgUrl, type, title, author, source, outline, language, favourite, comment, click 70 71 long id = 5; 72 73 LTNews news = new LTNews(); 74 news.setId(id); 75 news.setTime(1419927688); 76 news.setUrl("http://insertByNewsController/url_1修改"); 77 news.setImgUrl("修改"); 78 news.setType("1,2,3"); 79 news.setTitle("题目 修改222222"); 80 news.setAuthor("修改"); 81 news.setSource("来源修改"); 82 news.setOutline("<html><body>内容 insertByNewsController1</body></html>"); 83 news.setLanguage(1); 84 news.setFavourite(0); 85 news.setComment(0); 86 news.setClick(0); 87 88 this.newsDao.updateNews(news); 89 90 return "update by id: " + id; 91 } 92 93 //rootPath += "/select" 94 @RequestMapping(value="/select/{newsId}", method=RequestMethod.GET) 95 @ResponseStatus(HttpStatus.OK) 96 @ResponseBody 97 public LTNews select(@PathVariable String newsId) 98 { 99 LTNews news = this.newsDao.selectNews(Long.valueOf(newsId)); 100 101 return news; 102 } 103 }
@Autowired
private LTNewsDao newsDao;
表示要自动装配 LTNewsDao bean 类,需要在配置文件中进行配置,如下。
9、修改配置文件 spring-api.xml 、 spring-dao.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:mvc="http://www.springframework.org/schema/mvc" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> 7 8 <context:component-scan base-package="com.matchbox.api" /> 9 10 <!-- http.get 的时候,如果uri中带中文参数,需要设置get方法的header的Content-Type为utf-8 --> 11 <mvc:annotation-driven> 12 <mvc:message-converters> 13 <!-- StringHttpMessageConverter 默认采用的是 text/plain;charset=ISO-8859-1 --> 14 <!-- RESTClient 响应 Content-Type: text/plain;charset=UTF-8 --> 15 <bean class="org.springframework.http.converter.StringHttpMessageConverter"> 16 <property name="supportedMediaTypes"> 17 <list> 18 <value>text/plain;charset=UTF-8</value> 19 </list> 20 </property> 21 </bean> 22 23 <!-- json格式的自动转换--> 24 <!-- RESTClient 响应 Content-Type: application/json;charset=UTF-8 --> 25 <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/> 26 </mvc:message-converters> 27 </mvc:annotation-driven> 28 29 </beans>
<context:component-scan base-package="com.matchbox.api" /> 表示在 com.match.api 这个包下面寻找 servlet 的入口 uri 。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> 5 6 <bean id="genericDao" class="com.matchbox.dao.common.impl.LTGenericDaoImpl" abstract="true"> 7 <property name="dataSource" ref="dataSource" /> 8 </bean> 9 10 <bean id="newsDao" class="com.matchbox.dao.news.impl.LTNewsDaoImpl" parent="genericDao"></bean> 11 12 </beans>
要把 dao bean 类添加进去,这样才能正常运行。后面添加其他的 dao 类时,要同样添加进去。
10、修改数据库,建立 news 表
1 -- database 2 CREATE DATABASE 3 IF NOT EXISTS bishe_db DEFAULT CHARACTER 4 SET utf8; 5 6 USE bishe_db; 7 8 9 10 -- table news 11 DROP TABLE IF EXISTS news; 12 13 CREATE TABLE 14 IF NOT EXISTS news ( 15 id BIGINT NOT NULL auto_increment PRIMARY KEY, 16 time BIGINT, 17 url VARCHAR (200), 18 imgUrl VARCHAR (200), 19 type VARCHAR (100), 20 title TEXT, 21 author VARCHAR (100), 22 source VARCHAR (100), 23 outline TEXT, 24 language int, 25 favourite int, 26 comment int, 27 click int 28 ) auto_increment = 1;
11、启动 mysql 服务器和 tomcat 服务器,可以通过浏览器访问 uri 来操作数据库 news 类。
三、数据对象与json的转换
其实 @ResponseBody 这项工作就已经自动将模型对象与 json 文本数据进行转化了,但是前提是需要添加一些配置, spring-api.xml 中已经添加过了,步骤二中的第九步,已经将 json 数据转化 xml 配置过了,同时还需要添加一些包的支持, maven 的 pom.xml 中也已经添加过 json 的支持包。
浙公网安备 33010602011771号