这里为了使不必在所有Service和DAO都写基础的增删查改,我写了两个基类接口,BaseService和BaseDAO
BaseDAO.java (这里使用了泛型,PK 是主键,T是实体类型),这样其他DAO只要继承这个DAO就可以实现基础增删查改了。
1 package dao; 2 3 import java.io.Serializable; 4 import java.util.List; 5 6 /* 7 * 所有DAO接口继承此接口获取基本的增删查改规范 8 */ 9 public interface BaseDAO<PK extends Serializable, T> { 10 // 保存实体 11 public void save(T t); 12 13 // 删除实体 14 public void delete(PK id); 15 16 // 更新实体 17 public void update(T t); 18 19 // 根据ID查询实体 20 public T findById(PK id); 21 22 // 查询所有实体 23 public List<T> find(); 24 }
当然只是创建接口是不够的,还要有实现,下面是BaseDAO的实现类。BaseDAOImpl,这里主要利用Java的反射机制,根据泛型动态拼凑SQL脚本,动态生成实体对象。
但是有两点约定,就是数据库表名要以“tb_”+实体类名小写命名。如 Type实体对应的数据库表名tb_type,还有一点就是实体类应该是贫血的Bean,而且主键要以id命名。
当然对于表名和主键名我后续会改为自己创建注解的方式,让其可以动态指定
BaseDAOImpl.java
1 package dao.impl; 2 3 import java.beans.PropertyDescriptor; 4 import java.io.Serializable; 5 import java.lang.reflect.Field; 6 import java.lang.reflect.Method; 7 import java.lang.reflect.ParameterizedType; 8 import java.sql.Connection; 9 import java.sql.PreparedStatement; 10 import java.sql.ResultSet; 11 import java.sql.SQLException; 12 import java.sql.Statement; 13 import java.util.ArrayList; 14 import java.util.List; 15 16 import dao.BaseDAO; 17 import util.DBUtil; 18 import util.LogUtil; 19 20 @SuppressWarnings("rawtypes") 21 public class BaseDAOImpl<PK extends Serializable, T> implements BaseDAO<PK, T> { 22 23 protected Connection conn = null; 24 protected PreparedStatement pstmt = null; 25 protected Statement stmt = null; 26 protected ResultSet rs = null; 27 28 // 泛化类 29 private Class clazz = null; 30 // 基本类型包装类 31 private String[] baseType = { "Byte", "Short", "Char", "Integer", "Long", "Float", "Double", "Boolean" }; 32 33 public BaseDAOImpl() { 34 // 获取参数类型信息 35 ParameterizedType paramType = (ParameterizedType) this.getClass().getGenericSuperclass(); 36 clazz = (Class) paramType.getActualTypeArguments()[1]; 37 } 38 39 @Override 40 public void save(T t) { 41 // 获取类的所有属性 42 Field[] fields = t.getClass().getDeclaredFields(); 43 // 拼凑SQL语句 44 StringBuffer insertSQL = new StringBuffer("insert into tb_").append(t.getClass().getSimpleName().toLowerCase()) 45 .append(" ("); 46 for (int i = 0; i < fields.length - 1; i++) { 47 insertSQL = insertSQL.append(fields[i].getName()).append(","); 48 } 49 insertSQL = insertSQL.append(fields[fields.length - 1].getName()).append(") values("); 50 for (int i = 0; i < fields.length - 1; i++) { 51 insertSQL = insertSQL.append("? ,"); 52 } 53 insertSQL = insertSQL.append("?)"); 54 55 // 打印日志 56 LogUtil.log(insertSQL.toString()); 57 58 try { 59 conn = DBUtil.getConn(); 60 pstmt = conn.prepareStatement(insertSQL.toString()); 61 for (int i = 0; i < fields.length; i++) { 62 // 设置参数 63 pstmt.setObject(i + 1, 64 t.getClass() 65 .getMethod(// 获取t对象的getter方法并调用 66 "get" + fields[i].getName().substring(0, 1).toUpperCase() 67 + fields[i].getName().substring(1).toLowerCase()) 68 .invoke(t, null)); 69 } 70 pstmt.executeUpdate(); 71 } catch (Exception e) { 72 LogUtil.log(e.getMessage()); 73 throw new RuntimeException(e); 74 } finally { 75 try { 76 if (pstmt != null) { 77 pstmt.close(); 78 } 79 if (conn != null) { 80 conn.close(); 81 } 82 } catch (SQLException e) { 83 LogUtil.log(e.getMessage()); 84 throw new RuntimeException(e); 85 } 86 } 87 } 88 89 @Override 90 public void delete(PK id) { 91 StringBuffer deleteSQL = new StringBuffer("delete from tb_").append(clazz.getSimpleName().toLowerCase()) 92 .append(" where id = ").append(id); 93 94 // 打印日志 95 LogUtil.log(deleteSQL.toString()); 96 try { 97 conn = DBUtil.getConn(); 98 stmt = conn.createStatement(); 99 stmt.executeUpdate(deleteSQL.toString()); 100 } catch (SQLException e) { 101 LogUtil.log(e.getMessage()); 102 throw new RuntimeException(e); 103 } finally { 104 try { 105 if (stmt != null) { 106 stmt.close(); 107 } 108 if (conn != null) { 109 conn.close(); 110 } 111 } catch (SQLException e) { 112 LogUtil.log(e.getMessage()); 113 throw new RuntimeException(e); 114 } 115 } 116 } 117 118 @Override 119 public void update(T t) { 120 // 获取类的所有属性 121 Field[] fields = t.getClass().getDeclaredFields(); 122 // 平凑SQL语句 123 StringBuffer updateSQL = new StringBuffer("update tb_").append(t.getClass().getSimpleName().toLowerCase()); 124 125 for (int i = 1; i < fields.length - 1; i++) { 126 updateSQL = updateSQL.append(" set ").append(fields[i].getName()).append(" = ?,"); 127 } 128 129 try { 130 updateSQL = updateSQL.append(" set ").append(fields[fields.length - 1].getName()).append(" = ?") 131 .append(" where id = ") 132 .append(t.getClass() 133 .getMethod(// 获取t对象的getter方法并调用 134 "get" + fields[0].getName().substring(0, 1).toUpperCase() 135 + fields[0].getName().substring(1).toLowerCase()) 136 .invoke(t, null).toString()); 137 // 打印日志 138 LogUtil.log(updateSQL.toString()); 139 140 conn = DBUtil.getConn(); 141 pstmt = conn.prepareStatement(updateSQL.toString()); 142 for (int i = 1; i < fields.length; i++) { 143 // 设置参数 144 pstmt.setObject(i, 145 t.getClass() 146 .getMethod(// 获取t对象的getter方法并调用 147 "get" + fields[i].getName().substring(0, 1).toUpperCase() 148 + fields[i].getName().substring(1).toLowerCase()) 149 .invoke(t, null)); 150 } 151 int i = pstmt.executeUpdate(); 152 } catch (Exception e) { 153 LogUtil.log(e.getMessage()); 154 throw new RuntimeException(e); 155 } finally { 156 try { 157 if (pstmt != null) { 158 pstmt.close(); 159 } 160 if (conn != null) { 161 conn.close(); 162 } 163 } catch (SQLException e) { 164 LogUtil.log(e.getMessage()); 165 throw new RuntimeException(e); 166 } 167 } 168 } 169 170 @Override 171 public T findById(PK id) { 172 StringBuffer findSQL = new StringBuffer("select * from tb_").append(clazz.getSimpleName().toLowerCase()) 173 .append(" where id = ").append(id); 174 // 打印日志 175 LogUtil.log(findSQL.toString()); 176 T t = null; 177 Method m = null; 178 PropertyDescriptor pd = null; 179 boolean isBaseType = false; 180 try { 181 conn = DBUtil.getConn(); 182 stmt = conn.createStatement(); 183 rs = stmt.executeQuery(findSQL.toString()); 184 if (rs.next()) { 185 // 创建实体对象 186 t = (T) clazz.newInstance(); 187 Field[] fields = clazz.getDeclaredFields(); 188 for (int i = 0; i < fields.length; i++) { 189 // 利用反射为对象设置属性 190 pd = new PropertyDescriptor(fields[i].getName(), t.getClass()); 191 fields[i].setAccessible(false); 192 m = pd.getWriteMethod(); 193 String type = fields[i].getType().toString() 194 .substring(fields[i].getType().toString().lastIndexOf(".") + 1); 195 isBaseType = false; 196 for (int j = 0; j < baseType.length; j++) { 197 if (baseType[j].equals(type)) { 198 // 根据基本类型为属性设置值 199 invoke4BaseType(t, m, rs, i + 1, j); 200 isBaseType = true; 201 break; 202 } 203 } 204 if (!isBaseType) { 205 m.invoke(t, rs.getObject(i + 1)); 206 } 207 } 208 } 209 } catch (Exception e) { 210 LogUtil.log(e.getMessage()); 211 throw new RuntimeException(e); 212 } finally { 213 try { 214 if (stmt != null) { 215 stmt.close(); 216 } 217 if (conn != null) { 218 conn.close(); 219 } 220 } catch (SQLException e) { 221 LogUtil.log(e.getMessage()); 222 throw new RuntimeException(e); 223 } 224 } 225 return t; 226 } 227 228 @Override 229 public List<T> find() { 230 List<T> ts = new ArrayList<T>(); 231 StringBuffer findSQL = new StringBuffer("select * from tb_").append(clazz.getSimpleName().toLowerCase()); 232 233 // 打印日志 234 LogUtil.log(findSQL.toString()); 235 T t = null; 236 Method m = null; 237 PropertyDescriptor pd = null; 238 boolean isBaseType = false; 239 try { 240 conn = DBUtil.getConn(); 241 stmt = conn.createStatement(); 242 rs = stmt.executeQuery(findSQL.toString()); 243 while (rs.next()) { 244 // 创建实体对象 245 t = (T) clazz.newInstance(); 246 Field[] fields = clazz.getDeclaredFields(); 247 for (int i = 0; i < fields.length; i++) { 248 // 利用反射为对象设置属性 249 pd = new PropertyDescriptor(fields[i].getName(), t.getClass()); 250 fields[i].setAccessible(false); 251 m = pd.getWriteMethod(); 252 String type = fields[i].getType().toString() 253 .substring(fields[i].getType().toString().lastIndexOf(".") + 1); 254 isBaseType = false; 255 for (int j = 0; j < baseType.length; j++) { 256 if (baseType[j].equals(type)) { 257 // 根据基本类型为属性设置值 258 invoke4BaseType(t, m, rs, i + 1, j); 259 isBaseType = true; 260 break; 261 } 262 } 263 if (!isBaseType) { 264 m.invoke(t, rs.getObject(i + 1)); 265 } 266 } 267 ts.add(t); 268 } 269 } catch (Exception e) { 270 LogUtil.log(e.getMessage()); 271 throw new RuntimeException(e); 272 } finally { 273 try { 274 if (stmt != null) { 275 stmt.close(); 276 } 277 if (conn != null) { 278 conn.close(); 279 } 280 } catch (SQLException e) { 281 LogUtil.log(e.getMessage()); 282 throw new RuntimeException(e); 283 } 284 } 285 return ts; 286 } 287 288 /* 289 * 根据基本类型为对象设置属性 290 */ 291 private void invoke4BaseType(Object obj, Method m, ResultSet rs, int columnIndex, int typeIndex) throws Exception { 292 // "Byte", "Short", "Character", "Integer", 293 // "Long", "Float", "Double","Boolean" 294 switch (typeIndex) { 295 case 0: 296 m.invoke(obj, rs.getByte(columnIndex)); 297 break; 298 case 1: 299 m.invoke(obj, rs.getShort(columnIndex)); 300 break; 301 case 2: 302 m.invoke(obj, rs.getString(columnIndex).charAt(0)); 303 break; 304 case 3: 305 m.invoke(obj, rs.getInt(columnIndex)); 306 break; 307 case 4: 308 m.invoke(obj, rs.getLong(columnIndex)); 309 break; 310 case 5: 311 m.invoke(obj, rs.getFloat(columnIndex)); 312 break; 313 case 6: 314 m.invoke(obj, rs.getDouble(columnIndex)); 315 break; 316 case 7: 317 m.invoke(obj, rs.getBoolean(columnIndex)); 318 break; 319 default: 320 m.invoke(m, rs.getObject(columnIndex)); 321 } 322 } 323 }
这里我将Connection这些变量声明成protected方便子类使用。
这里还用到了两个工具类,DBUtil(数据库工具类)、LogUtil(日志记录类)
DBUtil.java
1 package util; 2 3 import java.sql.Connection; 4 import java.sql.DriverManager; 5 6 public class DBUtil { 7 8 private static final String URL = "jdbc:mysql:///mybatis"; 9 private static final String USERNAME = "root"; 10 private static final String PASSWORD = "root"; 11 private static Connection conn = null; 12 13 private DBUtil() { 14 } 15 16 public static Connection getConn() { 17 try { 18 Class.forName("com.mysql.jdbc.Driver"); 19 conn = DriverManager.getConnection(URL, USERNAME, PASSWORD); 20 } catch (Exception e) { 21 LogUtil.log(e.getMessage()); 22 throw new RuntimeException(e); 23 } 24 return conn; 25 } 26 }
LogUtil.java (这里我有个习惯就是有异常的时候一定要记录日志,说句不好听的话,死也要知道自己是怎么死的吧? )
1 package util; 2 3 import java.text.SimpleDateFormat; 4 import java.util.Date; 5 6 public class LogUtil { 7 8 private LogUtil(){} 9 10 public static void log(String... str) { 11 SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 12 13 StringBuffer sb = new StringBuffer("--- ").append(format.format(new Date())).append(" [") 14 .append(Thread.currentThread().getName()).append("] --- "); 15 for (int i = 0; i < str.length; i++) { 16 sb = sb.append(str[i]).append(" "); 17 } 18 System.out.println(sb.toString()); 19 } 20 }
利用LogUtil工具就可以看到日志信息了:--- 2015-09-13 13:21:48 [http-80-1] --- select * from tb_type
这里只是简单的打印出来而已,并没有像Log4j等开源框架一样有多种选择。
至此BaseBAO就完成了。接下来是BaseService。BaseService比较简单,由于这里的业务逻辑也比较简单,所有之间调用DAO就行了。
BaseService.java
1 package service; 2 3 import java.io.Serializable; 4 import java.util.List; 5 /* 6 * 所有Serivce接口继承此接口获取基本的增删查改规范 7 */ 8 public interface BaseServcice<PK extends Serializable, T> { 9 // 保存实体 10 public void save(T t); 11 12 // 删除实体 13 public void delete(PK id); 14 15 // 更新实体 16 public void update(T t); 17 18 // 根据ID查询实体 19 public T findById(PK id); 20 21 // 查询所有实体 22 public List<T> find(); 23 }
BaseService的实现类——BaseServiceImpl
BaseServiceImpl.java
1 package service.impl; 2 3 import java.io.Serializable; 4 import java.util.List; 5 6 import dao.BaseDAO; 7 import service.BaseServcice; 8 9 public abstract class BaseServiceImpl<PK extends Serializable, T> implements BaseServcice<PK, T> { 10 11 // 子类必须实现该方法提供对应的DAO 12 protected abstract BaseDAO<PK, T> getBaseDAO(); 13 14 @Override 15 public void save(T t) { 16 getBaseDAO().save(t); 17 } 18 19 @Override 20 public void delete(PK id) { 21 getBaseDAO().delete(id); 22 } 23 24 @Override 25 public void update(T t) { 26 getBaseDAO().update(t); 27 } 28 29 @Override 30 public T findById(PK id) { 31 return getBaseDAO().findById(id); 32 } 33 34 @Override 35 public List<T> find() { 36 return getBaseDAO().find(); 37 } 38 }
值得注意的是BaseServiceImpl是一个抽象类,继承此类的子类需要实现抽象方法,并提供直接或间接继承BaseDAO的子类对象供BaseServiceImpl使用。
至此BaseDAO和BaseService都完成了。
接下来就是完成自己想要的TypeService了,TypeService直接继承BaseService并提供自己的主键类型和实体类型就可以了。
TypeService.java
1 public interface TypeService extends BaseServcice<Long, Type>{ 2 }
TypeService的实现TypeServiceImpl,TypeServiceImp继承BaseServiceImpl并实现自己的接口,然后提供自己的DAO就可以了。
TypeServiceImpl
1 package service.impl; 2 3 import dao.BaseDAO; 4 import dao.TypeDAO; 5 import dao.impl.TypeDAOImpl; 6 import model.Type; 7 import service.TypeService; 8 9 public class TypeServiceImpl extends BaseServiceImpl<Long, Type>implements TypeService { 10 11 private TypeDAO typeDAO = new TypeDAOImpl(); 12 13 // 提供必要的DAO 14 @Override 15 protected BaseDAO<Long, Type> getBaseDAO() { 16 return typeDAO; 17 } 18 }
TypeDAO和TypeDAOImpl跟Service层类型。
1 public interface TypeDAO extends BaseDAO<Long, Type>{ 2 }
1 public class TypeDAOImpl extends BaseDAOImpl<Long, Type> implements TypeDAO{ 2 }
这样就方便很多了,就不用每个DAO都去重复的写增删查改操作了。