17. jdbc实战-实现类Hibernate 单表增删改查

it2022-05-05  138

jdbc 系列文章列表, 请查看目录: 《jdbc学习笔记》

熟悉Hibernate/JPA的同学知道, Hibernate/JPA对单表的增删改查是相当方便的, 直接操作java 实体即可, 这是jdbc 和 Mybatis 是所不能及的. jdbc 和 Mybatis 在操作单表的增删改错时, 依然需要手写sql, 比较繁琐. 那么我们就使用jdbc 实现一下类似于Hibernate/JPA 对单表的操作.

1. 方案设计

1.1 实体与表映射设计

对于Java实体和数据库表名映射方案, 笔者通过自定义注解类实现. 对于属性和列名, 笔者采取一一对应的关系.

@TableName: 指定Java类与数据库表表名的映射关系@AutoIncId: 指定自增id

1.2 单表增删改sql 缓存设计

如果每次查询都需要使用反射生成Sql, 那样性能会很差, 因此笔者设计了缓存.

EntityMetaDataCache: 缓存接口, 面向接口编程, 可切换多种缓存方式DefaultEntityMetaDataCache: 默认缓存, 笔者采用jvm 内存作为默认缓存.EntityMetadata: 缓存的类基本信息, 包含类基本信息: 类属性, 映射标明, 字段列表, 增删改查sql语句EntityMetadataFactory: 解析类信息

1.3 单表增删改API 设计

对于单表的操作, 笔者仅封装5个API 来演示, 读者可以自行扩展其它API:

方法签名方法描述参数说明public static boolean save(Object entity)保存实体entity: 数据库实体 public static boolean deleteById(Class clz, Integer id)通过id 删除实体clz: 实体类型 id: 主键 public static boolean update(Object entity)更新实体entity: 实体 public static T findById(Class clz, Integer id)通过主键id查询clz: 返回类型 id: 主键 public static List queryAll(Class clz)通过主键id查询clz: 返回类型

2. 自定义注解

对单表的自动化操作需要指定实体与表的对应关系,

2.1 表名映射注解

指定Java实体与数据库表名的映射关系.属性和表字段名一一对应. @Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface TableName { String value(); }

2.2 主键标识注解

笔者使用的是myssql, 为了测试自动回填自增id, 所以需要定义标识自增id的注解.

@Documented @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface AutoIncId { String value(); }

3. 缓存设计

3.1 实体信息-EntityMetadata

public class EntityMetadata { // 实体类型 private Class clz; // 实体映射表名称 private String tableName; // 实体映射表字段列表/实体属性列表,属性名与表中字段名, 一一对应 private List<String> columnNames = new ArrayList<>(); // id列名 private String idColumnName; // 插入语句 private String insertSql; // 更新语句 private String updateSql; // 查询语句:通过id查询 private String findByIdSql; // 删除语句 private String deleteByIdSql; // 查询语句:查询所有记录 private String queryAllSql; // 省略setter/getter/toString 方法 }

3.2 缓存接口IEntityMetaDataCache

public interface IEntityMetaDataCache { /**设置实体基本信息缓存 * @param entityMetadata 实体基本信息 * @since 1.0 * @author zongf * @created 2019-07-18 */ void setCache(EntityMetadata entityMetadata); /** 从缓存中获取实体基本类信息 * @param clz 实体类型 * @return EntityMetadata 类基本信息 * @since 1.0 * @author zongf * @created 2019-07-18 */ EntityMetadata getCache(Class clz); }

3.3 缓存默认实现-DefaultEntityMetaDataCache

public class DefaultEntityMetaDataCache implements IEntityMetaDataCache { private Map<Class, EntityMetadata> cacheMap = new HashMap<>(); @Override public void setCache(EntityMetadata entityMetadata) { cacheMap.put(entityMetadata.getClz(), entityMetadata); } @Override public EntityMetadata getCache(Class clz) { return cacheMap.get(clz); } }

3.4 缓存工厂-EntityMetadataFactory

/** 实体基础信息工厂类 * @since 1.0 * @author zongf * @created 2019-07-18 */ public class EntityMetadataFactory { /** sql 拼接中的空格 **/ private static String SQL_SPACE = " "; /**获取类的相关信息 * @param clz 类型 * @return EntityMetadata * @since 1.0 * @author zongf * @created 2019-07-18 */ public static EntityMetadata newInstance (Class clz){ // 创建类对象信息 EntityMetadata metadata = new EntityMetadata(); // 获取所有声明的属性, 不区分共有私有属性 Field[] declaredFields = clz.getDeclaredFields(); TableName tableNameAnno = (TableName) clz.getAnnotation(TableName.class); if (tableNameAnno == null) { throw new RuntimeException(clz.getName() + "无未使用@TableName 注解指定映射表名!"); } // 设置表名 metadata.setClz(clz); metadata.setTableName(tableNameAnno.value()); for (Field declaredField : declaredFields) { // 添加字段 metadata.getColumnNames().add(declaredField.getName()); // 解析id if (declaredField.isAnnotationPresent(AutoIncId.class)) { metadata.setIdColumnName(declaredField.getName()); } } // 初始化sql语句 initInsertSql(metadata); initUpdateSql(metadata); initDeleteByIdSql(metadata); initFindByIdSql(metadata); initQueryAllSql(metadata); return metadata; } /**初始化查询所有记录sql语句 * @param metadata 类基本信息 * @return null * @since 1.0 * @author zongf * @created 2019-07-18 */ private static void initQueryAllSql(EntityMetadata metadata) { StringBuilder sb = new StringBuilder(); sb.append("SELECT"); sb.append(SQL_SPACE); for (String columnName : metadata.getColumnNames()) { sb.append(columnName).append(",").append(SQL_SPACE); } sb.deleteCharAt(sb.length() - 2); sb.append("FROM"); sb.append(SQL_SPACE); sb.append(metadata.getTableName()); metadata.setQueryAllSql(sb.toString()); } /**初始化通过id查询sql语句 * @param metadata 类基本信息 * @since 1.0 * @author zongf * @created 2019-07-18 */ private static void initFindByIdSql(EntityMetadata metadata) { StringBuilder sb = new StringBuilder(); sb.append("SELECT"); sb.append(SQL_SPACE); for (String columnName : metadata.getColumnNames()) { sb.append(columnName).append(",").append(SQL_SPACE); } sb.deleteCharAt(sb.length() - 2); sb.append("FROM"); sb.append(SQL_SPACE); sb.append(metadata.getTableName()); sb.append(SQL_SPACE); sb.append("WHERE"); sb.append(SQL_SPACE); sb.append(metadata.getIdColumnName()); sb.append("=?"); metadata.setFindByIdSql(sb.toString()); } /**初始化通过id 删除sql * @param metadata 类基本信息 * @return null * @since 1.0 * @author zongf * @created 2019-07-18 */ private static void initDeleteByIdSql(EntityMetadata metadata) { StringBuilder sb = new StringBuilder(); sb.append("DELETE FROM"); sb.append(SQL_SPACE); sb.append(metadata.getTableName()); sb.append(SQL_SPACE); sb.append("WHERE"); sb.append(SQL_SPACE); sb.append(metadata.getIdColumnName()); sb.append("=?"); metadata.setDeleteByIdSql(sb.toString()); } /**初始化更新sql语句 * @param metadata 类基本信息 * @since 1.0 * @author zongf * @created 2019-07-18 */ private static void initUpdateSql(EntityMetadata metadata) { StringBuilder sb = new StringBuilder(); sb.append("UPDATE"); sb.append(SQL_SPACE); sb.append(metadata.getTableName()); sb.append(SQL_SPACE); sb.append("set"); sb.append(SQL_SPACE); for (String column : metadata.getColumnNames()) { sb.append(column).append("=?, "); } sb.deleteCharAt(sb.length() - 2); sb.append("WHERE"); sb.append(SQL_SPACE); sb.append(metadata.getIdColumnName()); sb.append("=?"); metadata.setUpdateSql(sb.toString()); } /**初始化插入sql 语句 * @param metadata 类基本信息 * @return null * @since 1.0 * @author zongf * @created 2019-07-18 */ private static void initInsertSql(EntityMetadata metadata) { StringBuilder sb = new StringBuilder(); sb.append("INSERT INTO"); sb.append(SQL_SPACE); sb.append(metadata.getTableName()); sb.append("("); for (String columnName : metadata.getColumnNames()) { sb.append(columnName).append(", "); } sb.delete(sb.length() - 2, sb.length()); sb.append(")"); sb.append(SQL_SPACE); sb.append("VALUES("); for (int i = 0; i < metadata.getColumnNames().size(); i++) { sb.append("?, "); } sb.delete(sb.length() - 2, sb.length()); sb.append(")"); metadata.setInsertSql(sb.toString()); } }

4. 单表实体管理器

/** 单表实体管理器 * @since 1.0 * @author zongf * @created 2019-07-18 */ public class EntityManager { private static Logger logger = Logger.getLogger(EntityManager.class.toString()); // 缓存,默认使用jvm内存做缓存 private static IEntityMetaDataCache metaDataCache = new DefaultEntityMetaDataCache(); /** 保存实体 * @param entity 数据库实体 * @return true: 保存成功, false: 保存失败 * @since 1.0 * @author zongf * @created 2019-07-18 */ public static boolean save(Object entity) { // 获取entity 信息 EntityMetadata entityMetadata = getEntityMetadata(entity.getClass()); // 获取数据库连接 Connection connection = DbConnUtil.getLocalConnection(); // 自增主键回写结果集 ResultSet callbackKeyRs; // sql 执行影响行数 int cnt = 0; try { // 获取预编译sql语句 PreparedStatement preparedStatement = connection.prepareStatement(entityMetadata.getInsertSql(), Statement.RETURN_GENERATED_KEYS); // 设置参数 for (int i = 1; i <= entityMetadata.getColumnNames().size(); i++) { String columnName = entityMetadata.getColumnNames().get(i - 1); Object value = ReflectUtil.getPropertyValue(entity, columnName); preparedStatement.setObject(i, value); } // 执行插入语句 cnt = preparedStatement.executeUpdate(); // 回写自增主键 callbackKeyRs = preparedStatement.getGeneratedKeys(); if (callbackKeyRs.next()) { int id = callbackKeyRs.getInt(1); ReflectUtil.setPropertyValue(entity, entityMetadata.getIdColumnName(), id); } } catch (SQLException e) { e.printStackTrace(); } // 返回成功或失败 return cnt > 0; } /**通过id 删除实体 * @param clz 实体类型 * @param id 主键id * @return true: 删除成功, false:删除失败 * @since 1.0 * @author zongf * @created 2019-07-18 */ public static <T> boolean deleteById(Class<T> clz, Integer id) { // 获取entity 信息 EntityMetadata entityMetadata = getEntityMetadata(clz); // 获取数据库连接 Connection connection = DbConnUtil.getLocalConnection(); // sql 执行影响行数 int cnt = 0; try { // 获取PreparedStatement 并设置参数 PreparedStatement preparedStatement = connection.prepareStatement(entityMetadata.getDeleteByIdSql()); preparedStatement.setInt(1, id); // 执行sql cnt = preparedStatement.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } return cnt >0; } /**更新实体 * @param entity 实体 * @return true: 更新成功, false:更新失败 * @since 1.0 * @author zongf * @created 2019-07-18 */ public static boolean update(Object entity) { // 获取entity 信息 EntityMetadata entityMetadata = getEntityMetadata(entity.getClass()); // 获取数据库连接 Connection connection = DbConnUtil.getLocalConnection(); // sql 执行影响行数 int cnt = 0; try { // 获取PreparedStatement 并设置参数 PreparedStatement preparedStatement = connection.prepareStatement(entityMetadata.getUpdateSql()); // 设置参数 for (int i = 1; i <= entityMetadata.getColumnNames().size(); i++) { String columnName = entityMetadata.getColumnNames().get(i - 1); Object value = ReflectUtil.getPropertyValue(entity, columnName); preparedStatement.setObject(i, value); } Object pkValue = ReflectUtil.getPropertyValue(entity, entityMetadata.getIdColumnName()); preparedStatement.setObject(entityMetadata.getColumnNames().size()+1,pkValue); // 执行sql cnt = preparedStatement.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } return cnt > 0; } /**通过主键id查询 * @param clz 返回类型 * @param id 主键id * @return T * @since 1.0 * @author zongf * @created 2019-07-18 */ public static <T> T findById(Class<T> clz, Integer id) { // 获取entity 信息 EntityMetadata entityMetadata = getEntityMetadata(clz); // 获取数据库连接 Connection connection = DbConnUtil.getLocalConnection(); // sql执行成功标识 T t = null; try { // 获取PreparedStatement 并设置参数 PreparedStatement preparedStatement = connection.prepareStatement(entityMetadata.getFindByIdSql()); preparedStatement.setInt(1, id); // 执行查询 ResultSet resultSet = preparedStatement.executeQuery(); // 结果集解析 t = ResultSetUtil.toBean(resultSet, clz); } catch (SQLException e) { e.printStackTrace(); } return t; } /**通过主键id查询 * @param clz 返回类型 * @return T * @since 1.0 * @author zongf * @created 2019-07-18 */ public static <T> List<T> queryAll(Class<T> clz) { // 获取entity 信息 EntityMetadata entityMetadata = getEntityMetadata(clz); // 获取数据库连接 Connection connection = DbConnUtil.getLocalConnection(); // sql执行成功标识 List<T> list = null; try { // 获取PreparedStatement 并设置参数 PreparedStatement preparedStatement = connection.prepareStatement(entityMetadata.getQueryAllSql()); // 执行查询 ResultSet resultSet = preparedStatement.executeQuery(); // 结果集解析 list = ResultSetUtil.toBeans(resultSet, clz); } catch (SQLException e) { e.printStackTrace(); } return list; } /**获取类相关信息 * @param clz 类类型 * @return EntityMetadata 类信息 * @since 1.0 * @author zongf * @created 2019-07-18 */ private static EntityMetadata getEntityMetadata(Class clz) { // 尝试从缓存中获取 EntityMetadata entityMetadata = metaDataCache.getCache(clz); // 如果缓存中entityMetadata为空, 则解析并存入缓存 if (entityMetadata == null) { entityMetadata = EntityMetadataFactory.newInstance(clz); metaDataCache.setCache(entityMetadata); logger.info("获取实体信息:-第一次解析"); }else { logger.info("获取实体信息:-从缓存中获取"); } return entityMetadata; } }

5. 测试用例

public class EntityManagerTest { // 测试保存 @Test public void save(){ UserPO userPO = new UserPO(); userPO.setName("zhangsan_" + Instant.now().getEpochSecond()); userPO.setPassword("123456"); boolean isSuccess = EntityManager.save(userPO); Assert.assertEquals(true, isSuccess); Assert.assertNotNull(userPO.getId()); } // 测试通过id 删除 @Test public void deleteById() { // 保存一个实体 UserPO userPO = new UserPO(); userPO.setName("zhangsan_" + Instant.now().getEpochSecond()); userPO.setPassword("abcdefg"); EntityManager.save(userPO); boolean isSuccess = EntityManager.deleteById(UserPO.class, userPO.getId()); Assert.assertEquals(true, isSuccess); } // 测试更新 @Test public void update(){ // 保存一个实体 UserPO userPO = new UserPO(); userPO.setName("zhangsan"); userPO.setPassword("123456"); EntityManager.save(userPO); // 更新名称 userPO.setName("lisi"); userPO.setPassword("123456"); EntityManager.update(userPO); UserPO afterUserPO = EntityManager.findById(UserPO.class, userPO.getId()); Assert.assertEquals("lisi", afterUserPO.getName()); } // 测试查询 @Test public void findById(){ UserPO userPO = new UserPO(); userPO.setName(LocalDateTime.now().toString()); userPO.setPassword("123456"); EntityManager.save(userPO); UserPO afterUserPO = EntityManager.findById(UserPO.class, userPO.getId()); Assert.assertNotNull(afterUserPO); Assert.assertEquals(userPO.getId(), afterUserPO.getId()); Assert.assertEquals(userPO.getName(), afterUserPO.getName()); } // 测试查询所有 @Test public void queryAll(){ List<UserPO> userPOS = EntityManager.queryAll(UserPO.class); Assert.assertNotNull(userPOS); userPOS.forEach(System.out::println); } }

最新回复(0)