一、業務闡述
在開發中查詢的數據庫結果集,既要連接數據庫、執行數據庫操作、關閉數據庫,還要把結果集的記錄人為的設置到自己封裝的DAO中等一系列的重復代碼。
本文主要是想解決:用戶只需要得到數據庫連接,寫sql語句,自己封裝dao,其余的操作由封轉的小框架解決這些重復的工作,用戶得到的只是一個集合List。
List里面的元素有集合Map其中key是數據庫中的字段類型,value是字段類型對應的值這個函數
DBUtil.executeQuery(con, sql)
List還提供集合元素存放的是dao對象,一條數據庫記錄對應一個dao對象,此函數是
DBUtil.executeQuery(con, sql,Vehicle.class)
以下提供源碼的敘述
二、源碼解說
測試類
- package com.hewen.dao.manage;
- import java.sql.Connection;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import java.util.List;
- public class Main
- {
- public static void main(String[] args)
- {
- Connection con;
- try {
- con = DBTest.getCon();
- } catch (SQLException e) {
- e.printStackTrace();
- return;
- }
- PreparedStatement pst = null;
- ResultSet rs = null;
- String sql = "select * from t_vehicle t where t.vehicle_id<4";
- /**
- * 該方法用到的技術是通過結果集的列屬性的性質得到,沒有用到反射機制
- * 這個測試用例也是把查詢的結果集放到List集合
- * 里面的元素是集合Map,key是數據庫中的字段類型,value是
- * 字段類型對應的值,
- * 查詢的結果如:[{KIND_ID=1, DEF_FLAG=null, CHANNELNO=1, SN=陸震,(822)22911,13771000789,
- * BUYDATE=2010-02-26, DELETETIME=null, STAMP=2010-02-26, REGDATE=null, ISDELETED=0,
- * VEHICLE_ID=2, NUMBER_PLATE=蘇B10001, VEHICLESTATE=待命狀態(對應現場返回), USEDATE=2010-02-26,
- * INTERPHONENO=null, NUMBER_PLATE_TYPE_ID=4, TEL2=null, STYLE=null, COLOR=null,
- * INTERPHONEID=null, LASTMAINTAINTIME=null, INITDISTANCE=0, LAST_UPDATE_TIME=2010-02-26,
- * REMARK=null, TEL=null, SUPERVISER=null},
- * {KIND_ID=3, DEF_FLAG=null, CHANNELNO=1, SN=陸震,
- * (822)22911,13771000789, BUYDATE=2010-02-26, DELETETIME=null, STAMP=2010-02-26,
- * REGDATE=null, ISDELETED=0, VEHICLE_ID=3, NUMBER_PLATE=蘇B90003,
- * VEHICLESTATE=待命狀態(對應現場返回), USEDATE=2010-02-26, INTERPHONENO=null,
- * NUMBER_PLATE_TYPE_ID=4, TEL2=13151000793, STYLE=面包車, COLOR=白, INTERPHONEID=null,
- * LASTMAINTAINTIME=null, INITDISTANCE=0, LAST_UPDATE_TIME=2010-02-26, REMARK=null,
- * TEL=22916, SUPERVISER=楊興華}]
- */
- try {
- List list=DBUtil.executeQuery(con, sql);
- System.out.println(list);
- } catch (SQLException e) {
- e.printStackTrace();
- }
- /**
- * 這個測試用例只是把查詢的結果集中的某一條記錄映射到了dao對象中,
- * 查詢的結果如:
- * vehicle:vehicle_id: 2 numberPlate: 蘇B10001 deleteDate: null
- vehicle:vehicle_id: 3 numberPlate: 蘇B90003 deleteDate: null
- */
- /* try {
- pst = con.prepareStatement(sql);
- rs = pst.executeQuery();
- while(rs.next()){
- Vehicle r = (Vehicle) DBUtil.getFirstObjectFromRs(rs, Vehicle.class);
- System.out.println("vehicle:" + r);
- }
- } catch (SQLException e) {
- e.printStackTrace();
- }finally{
- DBUtil.closeRs(rs);
- DBUtil.closePst(pst);
- DBUtil.closeCon(con);
- }*/
- /**
- * 方法是用的反射機制
- * 這個測試用例是測試executeQuery函數,把查詢的結果集放到List集合
- * 並且集合元素存放的是dao對象,一條數據庫記錄對應一個dao對象,
- * 打印出來的結果如:
- * [vehicle_id: 2 numberPlate: 蘇B10001 deleteDate: null,
- * vehicle_id: 3 numberPlate: 蘇B90003 deleteDate: null]
- *
- */
- /* try {
- List list=DBUtil.executeQuery(con, sql,Vehicle.class);
- System.out.println(list);
- } catch (SQLException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }*/
- }
- }
封裝dao DBUtil類
- package com.hewen.dao.manage;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- import java.lang.reflect.Type;
- import java.sql.Connection;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import java.sql.ResultSetMetaData;
- import java.sql.SQLException;
- import java.sql.Statement;
- import java.sql.Types;
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- /***
- * 注意利用查詢出數據庫的一條記錄映射到相應的dao中,寫相應的dao一定要注意字段,一定
- * 要與數據庫的記錄字段相對應,大小寫可以忽略,但是字段不一致就返回錯誤的數據
- *
- * private static Object getValueFromRs(ResultSet rs, String fieldName, Type t) throws SQLException
- * 此接口有個小的問題就是如果,獲取的字段值是空值或者為null,而你自己的需求就是想要獲取的字段為一個
- * 默認的值,那就只需要客戶該寫這個方法,進行判斷就可以
- * @author Administrator
- *
- */
- public class DBUtil {
- /**
- * 對操作的數據庫回滾
- * @param con 對數據庫操作所得到的鏈接
- */
- public static void rollBack(Connection con){
- try {
- con.rollback();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- /***
- *
- * @param con 數據庫jdbc鏈接
- * @param sql 執行的sql語句
- * @return 返回查詢的記錄數,記錄存儲在集合List里面,
- * 里面的元素是集合Map,key是數據庫中的字段類型,value是
- * 字段類型對應的值
- * @throws SQLException
- */
- public static List<Map<String, Object>> executeQuery(Connection con, String sql) throws SQLException{
- PreparedStatement pst = null;
- ResultSet rs = null;
- try {
- pst = con.prepareStatement(sql);
- rs = pst.executeQuery();
- return getListFromRsLowerCase(rs);
- }finally{
- closeRs(rs);
- closePst(pst);
- }
- }
- /***
- * 執行sql語句,把結果集存放到List集合里,集合的元素是dao對象
- * @param con 數據庫得到的鏈接
- * @param sql 執行查詢的sql語句
- * @param c 把一條條記錄要映射的dao類中的對象中去
- * @return
- * @throws SQLException
- */
- public static List<Object> executeQuery(Connection con, String sql, Class<?> c) throws SQLException{
- PreparedStatement pst = null;
- ResultSet rs = null;
- try {
- pst = con.prepareStatement(sql);
- rs = pst.executeQuery();
- return getListFromRs(rs, c);
- }finally{
- closeRs(rs);
- closePst(pst);
- }
- }
- /**
- * 得到結果集存儲到list中
- * @param rs 查詢的結果集
- * @return
- * @throws SQLException
- */
- public static List<Map<String, Object>> getListFromRs(ResultSet rs) throws SQLException{
- ResultSetMetaData md = rs.getMetaData();//得到結果集列的屬性
- int columns = md.getColumnCount();//得到記錄有多少列
- int i;
- List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
- while(rs.next()){
- Map<String, Object> map = new HashMap<String, Object>();
- for(i = 0; i < columns; i++){
- map.put(md.getColumnName(i + 1), getValueByType(rs, md.getColumnType(i + 1), md.getColumnName(i + 1)));
- }
- list.add(map);
- }
- return list;
- }
- /**
- * 這個與getListFromRs(ResultSet rs)差不多,只是把數據庫的字段變成小寫
- *
- * @param rs
- * @return
- * @throws SQLException
- */
- public static List<Map<String, Object>> getListFromRsLowerCase(ResultSet rs) throws SQLException{
- ResultSetMetaData md = rs.getMetaData();
- int columns = md.getColumnCount();
- int i;
- List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
- while(rs.next()){
- Map<String, Object> map = new HashMap<String, Object>();
- for(i = 0; i < columns; i++){
- map.put(md.getColumnName(i + 1).toLowerCase(), getValueByType(rs, md.getColumnType(i + 1), md.getColumnName(i + 1)));
- }
- list.add(map);
- }
- return list;
- }
- /**
- * 這個與getListFromRs(ResultSet rs)功能一樣,只是把數據庫的字段變成大寫
- * @param rs
- * @return
- * @throws SQLException
- */
- public static List<Map<String, Object>> getListFromRsUpperCase(ResultSet rs) throws SQLException{
- ResultSetMetaData md = rs.getMetaData();
- int columns = md.getColumnCount();
- int i;
- List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
- while(rs.next()){
- Map<String, Object> map = new HashMap<String, Object>();
- for(i = 0; i < columns; i++){
- map.put(md.getColumnName(i + 1).toUpperCase(), getValueByType(rs, md.getColumnType(i + 1), md.getColumnName(i + 1)));
- }
- list.add(map);
- }
- return list;
- }
- /***
- *
- * @param rs 查詢的結果集
- * @param c 集合元素存放的dao對象
- * @return
- * @throws SQLException
- */
- public static List<Object> getListFromRs(ResultSet rs, Class<?> c) throws SQLException{
- List<Object> list = new ArrayList<Object>();
- try {
- while(rs.next()){
- Object o = initObjectFromRsIfExist(rs, c);
- list.add(o);
- }
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } catch (InstantiationException e) {
- e.printStackTrace();
- }
- return list;
- }
- /**
- *
- * @param rs 查詢的結果集
- * @param c 結果集一條記錄,而一條記錄所對應的dao類
- * @return
- * @throws SQLException
- */
- public static Object getFirstObjectFromRs(ResultSet rs, Class<?> c) throws SQLException{
- Object o = null;
- try {
- o = initObjectFromRsIfExist(rs, c);
- } catch (InstantiationException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- }
- return o;
- }
- /***
- *
- * @param rs 查詢出來的結果集
- * @param type SQL type from java.sql.Types
- * @param name 數據庫記錄所對應的字段名稱
- * @return 返回一條記錄的一個列值
- * @throws SQLException
- */
- private static Object getValueByType(ResultSet rs, int type, String name) throws SQLException{
- switch(type){
- case Types.NUMERIC:
- return rs.getLong(name);
- case Types.VARCHAR:
- //if(rs.getString(name)==null){
- //return "";
- //}
- return rs.getString(name);
- case Types.DATE:
- //if(rs.getDate(name)==null){
- //return System.currentTimeMillis();
- // }
- return rs.getDate(name);
- case Types.TIMESTAMP:
- return rs.getTimestamp(name).toString().substring(0,rs.getTimestamp(name).toString().length()-2);
- case Types.INTEGER:
- return rs.getInt(name);
- case Types.DOUBLE:
- return rs.getDouble(name);
- case Types.FLOAT:
- return rs.getFloat(name);
- case Types.BIGINT:
- return rs.getLong(name);
- default:
- return rs.getObject(name);
- }
- }
- /***
- * 查詢dao映射的字段是否在記錄在數據庫包含的字段
- * @param rs 查詢的記錄集
- * @param fieldName dao映射的字段
- * @return 如果包含在數據庫記錄集里面,返回true,否則false
- * @throws SQLException
- */
- private static boolean rsContainsFields(ResultSet rs, String fieldName) throws SQLException{
- ResultSetMetaData md = rs.getMetaData();
- for(int i = 0; i < md.getColumnCount(); i++){
- if(md.getColumnName(i + 1).equalsIgnoreCase(fieldName)){
- return true;
- }
- }
- return false;
- }
- /***
- * 這個函數與initObjectFromRsIfExist函數實現的功能是一樣,只是
- * 沒有判斷dao中的字段是否與數據庫記錄所定義的字段是一樣的,
- * 沒有判斷時如果自己設置的dao字段與數據庫的字段不一致就會報異常
- * @param rs
- * @param c
- * @return
- * @throws InstantiationException
- * @throws SQLException
- * @throws IllegalAccessException
- */
- private static Object initObjectFromRs(ResultSet rs, Class<?> c) throws InstantiationException, SQLException, IllegalAccessException{
- Object o = c.newInstance();
- Method[] methods = o.getClass().getMethods();
- for(Method m: methods){
- if(m.getName().startsWith("set")){
- try {
- m.invoke(o, getParamValueFromRs(rs, m));
- } catch (IllegalArgumentException e) {
- throw new RuntimeException("IllegalArgumentException:" + e + "\nMethods:" + m.getName());
- } catch (InvocationTargetException e) {
- throw new RuntimeException("InvocationTargetException:" + e + "\nMethods:" + m.getName());
- }
- }
- }
- return o;
- }
- /***
- *
- * 把數據庫的一條記錄映射到相應的dao對象中,
- * 如果dao中的字段與數據庫字段不一致,返回的就是dao數據類型定義的默認值
- * 如:dao的字段long vehicleID;而數據庫的字段是vehicle_id,那么返回的
- * 就定義的默認值0.
- * @param rs 查詢的結果集
- * @param c 結果集一條記錄,而一條記錄所對應的dao類
- * @return
- * @throws SQLException
- * @throws IllegalAccessException
- * @throws InstantiationException
- */
- private static Object initObjectFromRsIfExist(ResultSet rs, Class<?> c) throws SQLException, IllegalAccessException, InstantiationException{
- Object o = c.newInstance();//一條記錄的dao,新建對象
- Method[] methods = o.getClass().getMethods();//dao對象所有的方法
- String field;
- for(Method m: methods){
- //得到dao字段,如getRegdate,轉換成Regdate
- field = m.getName().substring(3);
- //查詢dao映射的字段是否在記錄在數據庫包含的字段,dao方法對set開頭的方法進行處理
- //因為要將結果集映射到dao里面
- if(m.getName().startsWith("set") && rsContainsFields(rs, field)){
- try {
- m.invoke(o, getParamValueFromRs(rs, m));
- } catch (IllegalArgumentException e) {
- throw new RuntimeException("IllegalArgumentException:" + e + "\nMethods:" + m.getName());
- } catch (InvocationTargetException e) {
- throw new RuntimeException("InvocationTargetException:" + e + "\nMethods:" + m.getName());
- }
- }
- }
- return o;
- }
- /***
- *
- * @param rs 查詢的結果集
- * @param m dao映射字段對應的一個set方法
- * @return
- * @throws SQLException
- */
- private static Object getParamValueFromRs(ResultSet rs, Method m) throws SQLException
- {
- String fieldName = m.getName().substring(3);
- Type type = m.getGenericParameterTypes()[0];//獲取set方法參數的類型
- return getValueFromRs(rs, fieldName, type);
- }
- /**
- * 獲取數據庫一條記錄的一個列值
- * @param rs 查詢的結果集
- * @param fieldName dao數據字段,也就是數據庫記錄的數據字段類型
- * @param t 參數的數據類型
- * @return
- * @throws SQLException
- */
- private static Object getValueFromRs(ResultSet rs, String fieldName, Type t) throws SQLException{
- String type = t.toString();
- try{
- if(type.equals("int") || type.equals("class java.lang.Integer")){
- return rs.getInt(fieldName);
- }else if(type.equals("float") || type.equals("class java.lang.Float")){
- return rs.getFloat(fieldName);
- }else if(type.equals("double") || type.equals("class java.lang.Double")){
- return rs.getDouble(fieldName);
- }else if(type.equals("long") || type.equals("class java.lang.Long")){
- return rs.getLong(fieldName);
- }else if(type.equals("class java.lang.String")){
- return rs.getString(fieldName);
- }else if(type.equals("class java.sql.Timestamp")){
- return rs.getTimestamp(fieldName);
- }else if(type.equals("class java.sql.Date")){
- return rs.getDate(fieldName);
- }else if(type.equals("class java.sql.Time")){
- return rs.getTime(fieldName);
- }
- }catch(SQLException e){
- throw new SQLException("SQLException when get field:" + fieldName + "\n" + e);
- }
- throw new RuntimeException("getValueFromRsByField fail, field type is:" + type + ",field name is:" + fieldName);
- }
- /***
- * 關閉數據庫多個結果集
- * @param rss
- */
- public static void closeRs(ResultSet... rss){
- for(ResultSet rs: rss){
- if(rs != null){
- try {
- rs.close();
- } catch (SQLException e) {
- }
- }
- }
- }
- /**
- * 關閉數據庫多個psts
- * @param psts
- */
- public static void closePst(Statement... psts){
- for(Statement pst: psts){
- if(pst != null){
- try {
- pst.close();
- } catch (SQLException e) {
- }
- }
- }
- }
- /**
- * 關閉數據庫所得到的多個鏈接
- * @param cons
- */
- public static void closeCon(Connection... cons){
- for(Connection con: cons){
- if(con != null)
- {
- try {
- con.close();
- } catch (SQLException e) {
- }
- }
- }
- }
- }
連接Oracle數據庫類
- package com.hewen.dao.manage;
- import java.sql.Connection;
- import java.sql.DriverManager;
- import java.sql.SQLException;
- /***
- * 這個是連接Oracle數據庫
- * @author Administrator
- *
- */
- public class DBTest {
- public static Connection getCon() throws SQLException{
- try {
- Class.forName("oracle.jdbc.driver.OracleDriver");
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- return null;
- }
- String url = "jdbc:oracle:thin:@127.0.0.1:1521:orcl";
- String user = "avls";
- String password = "1";
- return DriverManager.getConnection(url, user, password);
- }
- }
封裝的dao類
- package com.hewen.dao.manage;
- import java.sql.Date;
- import java.util.ArrayList;
- import java.util.List;
- public class Vehicle{
- private long vehicle_Id;//車牌ID
- private String number_plate;//車牌號碼
- private String def_flag;//車牌自定義別名
- private int number_plate_type_id;//拍照類型id
- private int kind_id;//車輛類型ID,如物流、出租等
- private String style;//車輛品牌如:夏利、奔馳、本田等
- private String color;//車輛顏色
- private String sn;//序列號
- private String interphoneid;//對講機身份碼
- private String interphoneno;//對講機號
- private int channelno;//頻道號
- private float initdistance;//初始里程
- private Date lastmaintaintime;//最后保養時間
- private String vehiclestate;//車輛狀態,如正常使用,作廢等
- private Date buydate;//購買時間
- private Date usedate;//使用時間
- private Date regdate;//登記時間
- private int isdeleted;//是否刪除
- private Date deletedate;//刪除時間
- private Date last_update_time;//最后更新時間
- private String remark;//備注
- private Date stamp;//入庫時間
- private String superviser;//責任人姓名
- private String tel;//責任人電話一
- private String tel2;//責任人電話2
- public String getRemark() {
- return remark;
- }
- public void setRemark(String remark) {
- this.remark = remark;
- }
- public String toString(){
- return "vehicle_id: "+this.vehicle_Id+" numberPlate: "+this.number_plate+" deleteDate: "+this.deletedate;
- }
- //@Override
- public String tagetTableName() {
- return "t_used_vehicle";
- }
- //@Override
- public List<String> unEditFields() {
- List<String> list = new ArrayList<String>();
- list.add("remark");
- return list;
- }
- public String getNumber_plate() {
- return number_plate;
- }
- public void setNumber_plate(String number_plate) {
- this.number_plate = number_plate;
- }
- public String getDef_flag() {
- return def_flag;
- }
- public void setDef_flag(String def_flag) {
- this.def_flag = def_flag;
- }
- public int getNumber_plate_type_id() {
- return number_plate_type_id;
- }
- public void setNumber_plate_type_id(int number_plate_type_id) {
- this.number_plate_type_id = number_plate_type_id;
- }
- public int getKind_id() {
- return kind_id;
- }
- public void setKind_id(int kind_id) {
- this.kind_id = kind_id;
- }
- public String getStyle() {
- return style;
- }
- public void setStyle(String style) {
- this.style = style;
- }
- public String getColor() {
- return color;
- }
- public void setColor(String color) {
- this.color = color;
- }
- public String getSn() {
- return sn;
- }
- public void setSn(String sn) {
- this.sn = sn;
- }
- public String getInterphoneid() {
- return interphoneid;
- }
- public void setInterphoneid(String interphoneid) {
- this.interphoneid = interphoneid;
- }
- public String getInterphoneno() {
- return interphoneno;
- }
- public void setInterphoneno(String interphoneno) {
- this.interphoneno = interphoneno;
- }
- public int getChannelno() {
- return channelno;
- }
- public void setChannelno(int channelno) {
- this.channelno = channelno;
- }
- public float getInitdistance() {
- return initdistance;
- }
- public void setInitdistance(float initdistance) {
- this.initdistance = initdistance;
- }
- public Date getLastmaintaintime() {
- return lastmaintaintime;
- }
- public void setLastmaintaintime(Date lastmaintaintime) {
- this.lastmaintaintime = lastmaintaintime;
- }
- public String getVehiclestate() {
- return vehiclestate;
- }
- public void setVehiclestate(String vehiclestate) {
- this.vehiclestate = vehiclestate;
- }
- public Date getBuydate() {
- return buydate;
- }
- public void setBuydate(Date buydate) {
- this.buydate = buydate;
- }
- public Date getUsedate() {
- return usedate;
- }
- public void setUsedate(Date usedate) {
- this.usedate = usedate;
- }
- public Date getRegdate() {
- return regdate;
- }
- public void setRegdate(Date regdate) {
- this.regdate = regdate;
- }
- public int getIsdeleted() {
- return isdeleted;
- }
- public void setIsdeleted(int isdeleted) {
- this.isdeleted = isdeleted;
- }
- public Date getDeletedate() {
- return deletedate;
- }
- public void setDeletedate(Date deletedate) {
- this.deletedate = deletedate;
- }
- public Date getLast_update_time() {
- return last_update_time;
- }
- public void setLast_update_time(Date last_update_time) {
- this.last_update_time = last_update_time;
- }
- public Date getStamp() {
- return stamp;
- }
- public void setStamp(Date stamp) {
- this.stamp = stamp;
- }
- public String getSuperviser() {
- return superviser;
- }
- public void setSuperviser(String superviser) {
- this.superviser = superviser;
- }
- public String getTel() {
- return tel;
- }
- public void setTel(String tel) {
- this.tel = tel;
- }
- public String getTel2() {
- return tel2;
- }
- public void setTel2(String tel2) {
- this.tel2 = tel2;
- }
- public long getVehicle_Id() {
- return vehicle_Id;
- }
- public void setVehicle_Id(long vehicle_Id) {
- this.vehicle_Id = vehicle_Id;
- }
- }
運行的結果
- [{vehiclestate=待命狀態(對應現場返回), vehicle_id=2, interphoneid=null, deletetime=null, number_plate=蘇B10001, regdate=null, initdistance=0, superviser=null, style=null, number_plate_type_id=4, tel=null, buydate=2010-02-26, isdeleted=0, kind_id=1, channelno=1, usedate=2010-02-26, remark=null, sn=陸震,(822)22911,13771000789, last_update_time=2010-02-26, interphoneno=null, color=null, tel2=null, stamp=2010-02-26, lastmaintaintime=null, def_flag=null}, {vehiclestate=待命狀態(對應現場返回), vehicle_id=3, interphoneid=null, deletetime=null, number_plate=蘇B90003, regdate=null, initdistance=0, superviser=楊興華, style=面包車, number_plate_type_id=4, tel=22916, buydate=2010-02-26, isdeleted=0, kind_id=3, channelno=1, usedate=2010-02-26, remark=null, sn=陸震,(822)22911,13771000789, last_update_time=2010-02-26, interphoneno=null, color=白, tel2=13151000793, stamp=2010-02-26, lastmaintaintime=null, def_flag=null}]
- vehicle:vehicle_id: 2 numberPlate: 蘇B10001 deleteDate: null
- vehicle:vehicle_id: 3 numberPlate: 蘇B90003 deleteDate: null
- [vehicle_id: 2 numberPlate: 蘇B10001 deleteDate: null,
- vehicle_id: 3 numberPlate: 蘇B90003 deleteDate: null]