多表查詢時,findById、findAll 等邏輯高度相似,僅實體類型不同。利用泛型 + 反射可將通用邏輯提取到父類。
// User 實體
public class User {
private Integer id;
private String username;
private String password;
private String hobby;
// getter/setter 省略
}
// Student 實體
public class Student {
private Integer id;
private String name;
private Integer age;
// getter/setter 省略
}
// 基礎接口 public interface BaseDao{ T findById(Integer id); List findAll(); int insert(T entity); int update(T entity); int deleteById(Integer id); } // 具體接口繼承並固定泛型 public interface UserDao extends BaseDao {} public interface StudentDao extends BaseDao {}
public abstract class BaseDaoImplimplements BaseDao { private final String tableName; private final Class entityClass; private final QueryRunner qr = new QueryRunner(DataSourceUtils.getDataSource()); @SuppressWarnings("unchecked") protected BaseDaoImpl() { // 1. 獲取子類泛型實際類型:UserDaoImpl extends BaseDaoImpl Type superclass = getClass().getGenericSuperclass(); ParameterizedType pt = (ParameterizedType) superclass; this.entityClass = (Class ) pt.getActualTypeArguments()[0]; this.tableName = entityClass.getSimpleName().toLowerCase(); // 假設表名=類名小寫 } @Override public T findById(Integer id) { String sql = "SELECT * FROM " + tableName + " WHERE id = ?"; try { return qr.query(sql, new BeanHandler<>(entityClass), id); } catch (SQLException e) { throw new DaoException(e); } } @Override public List findAll() { String sql = "SELECT * FROM " + tableName; try { return qr.query(sql, new BeanListHandler<>(entityClass)); } catch (SQLException e) { throw new DaoException(e); } } @Override public int insert(T entity) { // 利用反射獲取非空屬性構建 INSERT Map props = BeanUtils.describe(entity); props.remove("class"); props.values().removeIf(Objects::isNull); String columns = String.join(", ", props.keySet()); String placeholders = props.keySet().stream().map(k -> "?").collect(Collectors.joining(", ")); String sql = "INSERT INTO " + tableName + " (" + columns + ") VALUES (" + placeholders + ")"; try { return qr.update(sql, props.values().toArray()); } catch (SQLException e) { throw new DaoException(e); } } // update/delete 同理... }
public class UserDaoImpl extends BaseDaoImplimplements UserDao {} public class StudentDaoImpl extends BaseDaoImpl implements StudentDao {}
@Test
public void testGenericDao() {
UserDao userDao = new UserDaoImpl();
User u = userDao.findById(1);
System.out.println("User: " + u);
StudentDao studentDao = new StudentDaoImpl();
Student s = studentDao.findById(1);
System.out.println("Student: " + s);
}
getClass().getGenericSuperclass() → ParameterizedType → getActualTypeArguments()[0]@Table("t_user") 顯式指定PageHelper 或手寫 LIMIT/OFFSETExample 模式或 QueryDSL@Transactional 或手動 Connection.setAutoCommit(false)findById此模式是 MyBatis、Hibernate、JdbcTemplate 等 ORM 框架的核心設計思想縮影。