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

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);
    }
}

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 黑客帝国 设计师:白松林 返回首页