我的编程空间,编程开发者的网络收藏夹
学习永远不晚

怎么开发一个MyBatis通用Mapper的轮子

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

怎么开发一个MyBatis通用Mapper的轮子

本篇内容介绍了“怎么开发一个MyBatis通用Mapper的轮子”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

    需求

    通用Mapper起码应该包含以下功能:

    批量增

    批量删

    只更新指定字段

    分页查询查当前页

    分页查询查总数

    字典字段翻译

    数据权限控制

    大概长下面这个样子: 

    public interface BaseMapper<T,K> {    int insert(T t);    int batchInsert(List<T> entity);        int deleteById(K id);        int deleteBatchIds(Collection<K> ids);        int updateById(T entity);        int updateSelectiveById(T entity);        T selectById(K id);        List<T> selectBatchIds(Collection<K> ids);        List<T> selectAll();        List<T> selectPage(PageRequest<T> pageRequest);        Long selectCount(T entity);}

    实现原理

    1、基于MyBatis3提供的SqlProvider构建动态Sql

    怎么开发一个MyBatis通用Mapper的轮子

    例如如下代码:

    @SelectProvider(type = UserSqlBuilder.class, method = "buildGetUsersByName")List<User> getUsersByName(String name);class UserSqlBuilder {  public static String buildGetUsersByName(final String name) {    return new SQL(){{      SELECT("*");      FROM("users");      if (name != null) {        WHERE("name like #{value} || '%'");      }      ORDER_BY("id");    }}.toString();  }}

    2、基于自定义注解,为实体和数据库表建立对应关系

    例如如下代码:

    @Table("user")public class User {    @Id(auto = true)    @Column(value = "id")    private Long id;    @Column(value = "name", filterOperator = FilterOperator.LIKE)    @OrderBy(orderPriority = 0)    private String name;    @OrderBy(order = Order.DESC, orderPriority = 1)    private Integer age;    private String email;    @Transient    private String test;}

    基于以上两个原理,当方法被调用时,我们便可构建出相应的动态Sql,从而实现该通用Mapper。

    代码实现

    1、自定义注解

    1)@Table

    了解Jpa的朋友一定很熟悉,这个就是为实体指定表名,实体不加这个注解就认为实体名与表名一致:

    @Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface Table {    //表名,不指定则使用实体类名    String value() default "";}

    2)@Column

    指定完表名,该指定列名了,同样的如果字段不指定则认为字段名与表列名一致:

    @Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface Column {    //对应数据库列名    String value() default "";    //查询时的过滤类型    FilterOperator filterOperator() default FilterOperator.EQ;    //是否查询,select是否带上该字段    boolean selectable() default true;    //是否插入,insert是否带上该字段    boolean insertable() default true;    //是否更新,update是否带上该字段    boolean updatable() default true;}

    3)@Id

    这个注解就是为了表明该字段是否是数据库主键。当然,这个注解可以与@Column合并,但为了更清晰,我还是决定单独使用这个注解。并且,也方便后期扩展。

    @Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface Id {    //主键是否自动生成    boolean auto() default false;}

    4)@OrderBy

    这个注解来标明查询时的排序字段,同时考虑如果排序字段有多个,可定义优先级:

    @Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface OrderBy {    //排序    Order order() default Order.ASC;    //多个排序字段先后顺序    int orderPriority() default 0;}

    5)@Transient

    考虑实体中有些字段在数据库中不存在的情况。使用这个注解来标注这样的字段:

    @Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface Transient {}

    2、几个pojo,用来保存实体对应的信息

    1)TableInfo,表示一个实体对应的数据库表信息

    public class TableInfo {    //表对应的实体类型    private Class<?> entityClass;    //表名    private String tableName;    //列    private List<ColumnInfo> columns;    //是否联合主键    private boolean isUnionId;}

    2)ColumnInfo,表示实体中的一个字段对应的数据库表字段信息

    public class ColumnInfo {    //对应的java类型    private Class<?> fieldClass;    private Field field;    private FilterOperator filterOperator;    //数据库列    private String column;    //是否主键    private boolean isPrimaryKey;    //主键填充方式    private boolean isPrimaryKeyAuto;    //排序    private Order orderBy;    private int orderByPriority;    //是否参与insert    private boolean insertable;    //是否参与update    private boolean updatable;    //是否参与select    private boolean selectable;}

    以上只需要注意一点,如何判断一个实体是否是联合主键。这里用的比较粗暴的方法,如果有多个字段加了@Id,那么认为是联合主键。

    3、定义开头说的BaseMapper

    这个BaseMapper的定义模仿了SpringDataJpa,它需要两个泛型,T表示实体类型,K表示主键类型。

    一般情况下K为简单数据类型,比如Long,String;

    联合主键情况下,K为自定义的一个复杂数据类型,具体使用方法见文章最后章节。

    public interface BaseMapper<T,K> {    @InsertProvider(type = SqlProvider.class,method = "insert")    @Options(useGeneratedKeys = true, keyProperty = "id",keyColumn = "id")    int insert(T t);    @InsertProvider(type = SqlProvider.class,method = "batchInsert")    int batchInsert(@Param("list") List<T> entity);    @DeleteProvider(type = SqlProvider.class,method = "deleteById")    int deleteById(@Param("id") K id);    @DeleteProvider(type = SqlProvider.class,method = "deleteBatchIds")    int deleteBatchIds(@Param("ids") Collection<K> ids);    @UpdateProvider(type = SqlProvider.class,method = "updateById")    int updateById(T entity);    @UpdateProvider(type = SqlProvider.class,method = "updateSelectiveById")    int updateSelectiveById(T entity);    @SelectProvider(type = SqlProvider.class,method = "selectById")    T selectById(@Param("id") K id);    @SelectProvider(type = SqlProvider.class,method = "selectBatchIds")    List<T> selectBatchIds(@Param("ids") Collection<K> ids);    @SelectProvider(type = SqlProvider.class,method = "selectAll")    List<T> selectAll();    @SelectProvider(type = SqlProvider.class,method = "selectPage")    List<T> selectPage(PageRequest<T> pageRequest);    @SelectProvider(type = SqlProvider.class,method = "selectCount")    Long selectCount(T entity);}

    4、SqlProvider

    public class SqlProvider<T> {    private static Logger logger = LoggerFactory.getLogger(SqlProvider.class);    private static Map<Class<?>, TableInfo> tableCache = new ConcurrentHashMap<>();    public String insert(T entity, ProviderContext context){        TableInfo tableInfo = getTableInfo(context);        String tableName = tableInfo.getTableName();        String intoColumns = tableInfo.getColumns()                .stream()                .filter(ColumnInfo::isInsertable)                .map(ColumnInfo::getColumn)                .collect(Collectors.joining(","));        String values = tableInfo.getColumns()                .stream()                .filter(ColumnInfo::isInsertable)                .map(ColumnInfo::variable)                .collect(Collectors.joining(","));        String sql = new SQL()                .INSERT_INTO(tableName)                .INTO_COLUMNS(intoColumns)                .INTO_VALUES(values).toString();        logger.info("sql->{},params->{}",sql,entity);        return sql;    }    public String batchInsert(@Param("list" ) List<?> entity, ProviderContext context){        TableInfo tableInfo = getTableInfo(context);        String tableName = tableInfo.getTableName();        String intoColumns = tableInfo.getColumns()                .stream()                .filter(ColumnInfo::isInsertable)                .map(ColumnInfo::getColumn)                .collect(Collectors.joining(","));        String values = tableInfo.getColumns()                .stream()                .filter(ColumnInfo::isInsertable)                .map(column->column.variableWithPrefix("item"))                .collect(Collectors.joining(","));        String sql = new SQL()                .INSERT_INTO(tableName)                .INTO_COLUMNS(intoColumns).toString();        sql += " values ";        sql += "<foreach collection=\"list\" item=\"item\" separator=\",\">" +                "  <trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">" +                "    " + values +                "  </trim>" +                "</foreach>";        sql = "<script>"+sql+"</script>";        logger.info("sql->{},params->{}",sql,entity);        return sql;    }    public String deleteById(@Param("id") T entity, ProviderContext context){        TableInfo tableInfo = getTableInfo(context);        String tableName = tableInfo.getTableName();        String[] where = null;        if (tableInfo.isUnionId()){            where = tableInfo.getColumns()                    .stream()                    .filter(ColumnInfo::isPrimaryKey)                    .map(columnInfo -> columnInfo.getColumn()+" = #{id."+columnInfo.getField().getName()+"}")                    .toArray(String[]::new);        }else {            where = tableInfo.getColumns()                    .stream()                    .filter(ColumnInfo::isPrimaryKey)                    .map(columnInfo -> columnInfo.getColumn()+" = #{id}")                    .toArray(String[]::new);        }        String sql = new SQL()                .DELETE_FROM(tableName)                .WHERE(where)                .toString();        logger.info("sql->{},params->{}",sql,entity);        return sql;    }    public String deleteBatchIds(@Param("ids") Collection<?> entity, ProviderContext context){        TableInfo tableInfo = getTableInfo(context);        String tableName = tableInfo.getTableName();        if (tableInfo.isUnionId()){            String[] where = new String[entity.size()];            for (int i = 0; i < entity.size(); i++){                List<String> list = new ArrayList<>();                String s = "%s=#{ids[%d].%s}";                for (ColumnInfo columnInfo:tableInfo.getColumns()){                    if (columnInfo.isPrimaryKey()){                        list.add(String.format(s,columnInfo.getColumn(),i,columnInfo.getField().getName()));                    }                }                where[i] = "("+StringUtils.join(list," and ")+")";            }            String sql = "delete from %s where %s ";            sql = String.format(sql,tableName,StringUtils.join(where," or "));            logger.info("sql->{},params->{}",sql,entity);            return sql;        }else {            String idName = tableInfo.getColumns()                    .stream()                    .filter(ColumnInfo::isPrimaryKey)                    .findFirst()                    .get()                    .getColumn();            String sql = "DELETE FROM %s WHERE %s IN (%s) ";            String[] arr = new String[entity.size()];            for (int i = 0; i < entity.size(); i++){                arr[i] = "#{ids["+i+"]}";            }            sql = String.format(sql,tableName,idName,StringUtils.join(arr,","));            logger.info("sql->{},params->{}",sql,entity);            return sql;        }    }    public String updateById(T entity, ProviderContext context){        TableInfo tableInfo = getTableInfo(context);        String tableName = tableInfo.getTableName();        String[] where = tableInfo.getColumns()                .stream()                .filter(ColumnInfo::isPrimaryKey)                .map(columnInfo -> columnInfo.getColumn()+" = "+columnInfo.variable())                .toArray(String[]::new);        String sql = new SQL().UPDATE(tableName).SET(tableInfo.updateSetColumn()).WHERE(where).toString();        logger.info("sql->{},params->{}",sql,entity);        return sql;    }    public String updateSelectiveById(T entity, ProviderContext context){        TableInfo tableInfo = getTableInfo(context);        String tableName = tableInfo.getTableName();        String[] where = tableInfo.getColumns()                .stream()                .filter(ColumnInfo::isPrimaryKey)                .map(columnInfo -> columnInfo.getColumn()+" = "+columnInfo.variable())                .toArray(String[]::new);        String sql = new SQL().UPDATE(tableName).SET(tableInfo.updateSetSelectiveColumn(entity)).WHERE(where).toString();        logger.info("sql->{},params->{}",sql,entity);        return sql;    }    public String selectById(@Param("id")T entity, ProviderContext context){        TableInfo tableInfo = getTableInfo(context);        String[] where = null;        if (tableInfo.isUnionId()){            where = tableInfo.getColumns().stream().filter(ColumnInfo::isPrimaryKey)                    .map(columnInfo -> columnInfo.getColumn()+" = #{id."+columnInfo.getField().getName()+"}")                    .toArray(String[]::new);        }else {            where = tableInfo.getColumns().stream().filter(ColumnInfo::isPrimaryKey)                    .map(columnInfo -> columnInfo.getColumn()+" = #{id}")                    .toArray(String[]::new);        }        String sql = new SQL()                .SELECT(tableInfo.selectColumnAsProperty())                .FROM(tableInfo.getTableName())                .WHERE(where)                .toString();        logger.info("sql->{},params->{}",sql,entity);        return sql;    }    public String selectBatchIds(@Param("ids")Collection<?> entity, ProviderContext context){        TableInfo tableInfo = getTableInfo(context);        String tableName = tableInfo.getTableName();        if (tableInfo.isUnionId()){            String[] where = new String[entity.size()];            for (int i = 0; i < entity.size(); i++){                List<String> list = new ArrayList<>();                String s = "%s=#{ids[%d].%s}";                for (ColumnInfo columnInfo:tableInfo.getColumns()){                    if (columnInfo.isPrimaryKey()){                        list.add(String.format(s,columnInfo.getColumn(),i,columnInfo.getField().getName()));                    }                }                where[i] = "("+StringUtils.join(list," and ")+")";            }            String sql = "select %s from %s where %s";            sql = String.format(sql,tableInfo.selectColumnAsProperty(),tableInfo.getTableName(),StringUtils.join(where," or "));            logger.info("sql->{},params->{}",sql,entity);            return sql;        }else {            String idName = tableInfo.getColumns()                    .stream()                    .filter(ColumnInfo::isPrimaryKey)                    .findFirst()                    .get()                    .getColumn();            String sql = "select %s from %s where %s in (%s) ";            String[] arr = new String[entity.size()];            for (int i = 0; i < entity.size(); i++){                arr[i] = "#{ids["+i+"]}";            }            sql = String.format(sql,tableInfo.selectColumnAsProperty(),tableName,idName,StringUtils.join(arr,","));            logger.info("sql->{},params->{}",sql,entity);            return sql;        }    }    public String selectAll(T entity, ProviderContext context){        TableInfo tableInfo = getTableInfo(context);        SQL sql =  new SQL()                .SELECT(tableInfo.selectColumnAsProperty())                .FROM(tableInfo.getTableName());        String orderBy = tableInfo.orderByColumn();        if (StringUtils.isNotEmpty(orderBy)){            sql.ORDER_BY(orderBy);        }        return sql.toString();    }    public String selectPage(PageRequest<T> entity, ProviderContext context){        TableInfo tableInfo = getTableInfo(context);        SQL sql = new SQL()                .SELECT(tableInfo.selectColumnAsProperty())                .FROM(tableInfo.getTableName());        String[] where = tableInfo.getColumns().stream()                .filter(column -> {                    Field field = column.getField();                    T bean = entity.getPageParams();                    Object value = Util.getFieldValue(bean, field);                    if (value == null) {                        return false;                    }                    return StringUtils.isNotEmpty(value.toString());                })                .map(column -> {                    String param = " #{pageParams." + column.getField().getName()+"}";                    if (column.getFilterOperator() == FilterOperator.LIKE){                        param = "concat('%', "+param+", '%')";                    }                    if (column.getFilterOperator() == FilterOperator.LEFTLIKE){                        param = "concat("+param+", '%')";                    }                    if (column.getFilterOperator() == FilterOperator.RIGHTLIKE){                        param = "concat('%', "+param+")";                    }                    return column.getColumn()+column.filterOperator()+param;                })                .toArray(String[]::new);        sql.WHERE(where);        if (StringUtils.isNotEmpty(entity.getOrder())){            ColumnInfo columnInfo = tableInfo.getColumns().stream()                    .filter(columnInfo1 -> columnInfo1.getField().getName().equalsIgnoreCase(entity.getOrder()))                    .findFirst().orElse(null);            if (columnInfo != null){                String direction = entity.getOrderDirection();                direction = (StringUtils.isEmpty(direction) || direction.equalsIgnoreCase("asc"))?" asc ":" desc ";                sql.ORDER_BY(columnInfo.getColumn() + direction);            }        }else {            String orderBy = tableInfo.orderByColumn();            if (StringUtils.isNotEmpty(orderBy)){                sql.ORDER_BY(orderBy);            }        }        sql.OFFSET("#{offset}").LIMIT("#{pageSize}");        String s = sql.toString();        logger.info("sql->{},params->{}",s,entity);        return s;    }    public String selectCount(T entity, ProviderContext context){        TableInfo tableInfo = getTableInfo(context);        SQL sql = new SQL()                .SELECT("count(1)")                .FROM(tableInfo.getTableName());        String[] where = tableInfo.getColumns().stream()                .filter(column -> {                    Field field = column.getField();                    Object value = Util.getFieldValue(entity, field);                    if (value == null) {                        return false;                    }                    return StringUtils.isNotEmpty(value.toString());                })                .map(column -> {                    String param = " #{" + column.getField().getName()+"}";                    if (column.getFilterOperator() == FilterOperator.LIKE){                        param = "concat('%', "+param+", '%')";                    }                    if (column.getFilterOperator() == FilterOperator.LEFTLIKE){                        param = "concat("+param+", '%')";                    }                    if (column.getFilterOperator() == FilterOperator.RIGHTLIKE){                        param = "concat('%', "+param+")";                    }                    return column.getColumn()+column.filterOperator()+param;                })                .toArray(String[]::new);        sql.WHERE(where);        String s = sql.toString();        logger.info("sql->{},params->{}",s,entity);        return s;    }    private TableInfo getTableInfo(ProviderContext context){        Class<?> clz = getEntityType(context);        return tableCache.computeIfAbsent(context.getMapperType(), t-> Util.tableInfo(clz));    }    private Class<?> getEntityType(ProviderContext context) {        return Stream.of(context.getMapperType().getGenericInterfaces())                .filter(ParameterizedType.class::isInstance)                .map(ParameterizedType.class::cast)                .filter(type -> type.getRawType() == BaseMapper.class)                .findFirst()                .map(type -> type.getActualTypeArguments()[0])                .filter(Class.class::isInstance)                .map(Class.class::cast)                .orElseThrow(() -> new IllegalStateException("未找到BaseMapper的泛型类 " + context.getMapperType().getName() + "."));    }}

    5、实体类转TableInfo

    public static TableInfo tableInfo(Class<?> entityClass) {        TableInfo info = new TableInfo();        info.setEntityClass(entityClass);        Table table = entityClass.getAnnotation(Table.class);        String tableName = entityClass.getSimpleName();        if (table != null && StringUtils.isNotEmpty(table.value())){            tableName = table.value();        }        info.setTableName(tableName);        Field[] allFields = getFields(entityClass);        Field[] fields = Stream.of(allFields)                //过滤@Transient注解的field                .filter(field -> !field.isAnnotationPresent(Transient.class))                .toArray(Field[]::new);        List<ColumnInfo> columns = new ArrayList<>();        int idCount = 0;        for (Field field:fields){            ColumnInfo columnInfo = new ColumnInfo();            columnInfo.setFieldClass(field.getDeclaringClass());            columnInfo.setField(field);            Id id = field.getAnnotation(Id.class);            idCount = idCount + (id == null?0:1);            columnInfo.setPrimaryKey(id == null?Boolean.FALSE:Boolean.TRUE);            columnInfo.setPrimaryKeyAuto(id == null?Boolean.FALSE:id.auto());            Column column = field.getAnnotation(Column.class);            String columnName = field.getName();            if (column != null && StringUtils.isNotEmpty(column.value())){                columnName = column.value();            }            columnInfo.setColumn(columnName);            FilterOperator filterOperator = FilterOperator.EQ;            if (column != null && column.filterOperator() != null){                filterOperator = column.filterOperator();            }            columnInfo.setFilterOperator(filterOperator);            if (columnInfo.isPrimaryKeyAuto()){                columnInfo.setInsertable(false);            }else {                columnInfo.setInsertable(true);                if (column != null){                    columnInfo.setInsertable(column.insertable());                }            }            columnInfo.setUpdatable(true);            columnInfo.setSelectable(true);            if (column != null){                columnInfo.setSelectable(column.selectable());                columnInfo.setUpdatable(column.updatable());            }            OrderBy orderBy = field.getAnnotation(OrderBy.class);            if (orderBy != null){                columnInfo.setOrderBy(orderBy.order());                columnInfo.setOrderByPriority(orderBy.orderPriority());            }            columns.add(columnInfo);        }        if (idCount > 1){            info.setUnionId(Boolean.TRUE);        }        info.setColumns(columns);        return info;    }

    6、字典字段自动翻译

    简单实现思路:对需要翻译的字段加上@FieldTrans注解来表明这个字段需要翻译,通过AOP方式对结果数据进行增强,来将字段进行翻译更新。

    此部分内容留待后续实现,同时调研一下是否还有更优雅简单的实现方式。

    7、数据权限

    我们先来思考一下数据权限到底要干啥?一句话来概括:查一张表的数据时在where条件中追加“and 控制权限的列 in (???)”。

    简单实现方法:在控制权限的字段加上@DataAuthrity注解来表明通过这个字段控制权限,而???的内容肯定是由业务代码来生成的,因此考虑给这个注解增加一个属性,用来指明权限数据由执行哪个接口或方法来获取。

    此部分内容留待后续实现,同时调研一下是否还有更优雅简单的实现方式。

    使用示例

    1、数据库表

    CREATE TABLE `user` (  `id` bigint(20) NOT NULL AUTO_INCREMENT,  `name` varchar(255) DEFAULT NULL,  `age` int(11) DEFAULT NULL,  `email` varchar(255) DEFAULT NULL,  PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

    2、实体

    @Table("user")public class User {    @Id(auto = true)    @Column(value = "id")    private Long id;    @Column(value = "name", filterOperator = FilterOperator.LIKE)    @OrderBy(orderPriority = 0)    private String name;    @OrderBy(order = Order.DESC, orderPriority = 1)    private Integer age;    private String email;    @Transient    private String test;}

    3、Mapper

    public interface UserMapper extends BaseMapper<User, Long> {}

    至此,不需要写任何mapper.xml,UserMapper已经具备了增删改查能力。

    4、联合主键示例

    public class User1 {    @Id    @Column(value = "id1")    private String id1;        @Id    @Column(value = "id2")    private String id2;        @Column(value = "name", filterOperator = FilterOperator.LIKE)    @OrderBy(orderPriority = 0)    private String name;        @OrderBy(order = Order.DESC, orderPriority = 1)    private Integer age;        private String email;        @Transient    private String test;}public class User1Id {    private String id1;    private String id2;}public interface User1Mapper extends BaseMapper<User1,User1Id> {}

    “怎么开发一个MyBatis通用Mapper的轮子”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

    免责声明:

    ① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

    ② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

    怎么开发一个MyBatis通用Mapper的轮子

    下载Word文档到电脑,方便收藏和打印~

    下载Word文档

    猜你喜欢

    怎么开发一个MyBatis通用Mapper的轮子

    本篇内容介绍了“怎么开发一个MyBatis通用Mapper的轮子”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!需求通用Mapper起码应该包
    2023-07-04

    详解如何开发一个MyBatis通用Mapper的轮子

    因为一些原因,例如:通用数据权限控制、MyBatis-Plus好像不支持联合主键等,我们不得不开发一个MyBatis通用Mapper的轮子。文中的示例代码讲解详细,需要的可以参考一下
    2022-12-21

    怎么使用Vue开发一个五子棋小游戏

    这篇文章主要讲解了“怎么使用Vue开发一个五子棋小游戏”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么使用Vue开发一个五子棋小游戏”吧!1.绘制游戏区域和游戏元素开始写代码之前,一定要记
    2023-07-02

    使用WebIDE怎么开发一个Android应用

    本篇文章给大家分享的是有关使用WebIDE怎么开发一个Android应用,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。C4C里做Android开发用的是Google的Andro
    2023-06-04

    Android开发中怎么实现一个沉浸式通知栏

    Android开发中怎么实现一个沉浸式通知栏?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。①DrawerLayout+Toolbar添加依赖库(谷歌提供)compile com
    2023-05-31

    使用Python怎么开发一个个人云盘应用

    本篇文章为大家展示了使用Python怎么开发一个个人云盘应用,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。python的数据类型有哪些?python的数据类型:1. 数字类型,包括int(整型)、l
    2023-06-14

    如何使用MongoDB开发一个简单的电子商务网站

    如何使用MongoDB开发一个简单的电子商务网站作为一种流行的非关系型数据库,MongoDB在电子商务网站的开发中具有很大的优势。它的可伸缩性和灵活性使得它成为构建强大而且易于扩展的电子商务网站的理想选择。本文将向您介绍如何使用MongoD
    2023-10-22

    怎么用MyBatis创建一个简单的程序

    这篇文章主要介绍“怎么用MyBatis创建一个简单的程序”,在日常操作中,相信很多人在怎么用MyBatis创建一个简单的程序问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么用MyBatis创建一个简单的程序
    2023-06-29

    怎么用spring注解开发一个RESTful接口

    本篇内容主要讲解“怎么用spring注解开发一个RESTful接口”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么用spring注解开发一个RESTful接口”吧!一、开发REST接口在本专栏
    2023-06-29

    如何用Go语言开发一个简单的电子商务平台

    如何用Go语言开发一个简单的电子商务平台一、引言近年来,随着互联网的迅猛发展,电子商务平台成为了现代商业发展的重要组成部分。而在开发电子商务平台的过程中,选择一门合适的编程语言显得尤为重要。Go语言因其高效、协程、并发等特点,成为了开发电子
    如何用Go语言开发一个简单的电子商务平台
    2023-11-20

    如何用Go语言开发一个简单的即时通讯应用

    如何用Go语言开发一个简单的即时通讯应用随着互联网的发展和人们对实时沟通需求的增加,即时通讯应用在我们生活中扮演着越来越重要的角色。Go语言作为一种开源的高性能编程语言,越来越受开发者们的喜爱。本文将介绍如何使用Go语言开发一个简单的即时通
    如何用Go语言开发一个简单的即时通讯应用
    2023-11-20

    怎么用Android开发一个学生管理系统

    本篇内容介绍了“怎么用Android开发一个学生管理系统”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!效果演示随手做的一个小玩意,还有很多功
    2023-06-25

    如何利用MySQL和Ruby开发一个简单的电子商务网站

    要利用MySQL和Ruby开发一个简单的电子商务网站,你可以按照以下步骤进行:1. 安装MySQL数据库和Ruby开发环境:首先,在你的计算机上安装MySQL数据库和Ruby开发环境。你可以从官方网站下载并按照说明进行安装。2. 创建数据库
    2023-10-10

    怎么使用vue和electron开发一个桌面应用

    本文小编为大家详细介绍“怎么使用vue和electron开发一个桌面应用”,内容详细,步骤清晰,细节处理妥当,希望这篇“怎么使用vue和electron开发一个桌面应用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧
    2023-07-05

    怎么用Python开发一个简单的猜数字游戏

    本篇文章给大家分享的是有关怎么用Python开发一个简单的猜数字游戏,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。如何使用Python制作一个简单的猜数字游戏。游戏规则玩家将猜
    2023-06-16

    Android开发中怎么启动另一个应用中的Activity

    这篇文章将为大家详细讲解有关Android开发中怎么启动另一个应用中的Activity,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。Android 启动另一个App/apk中的Activit
    2023-05-31

    利用Java怎么开发一个企业级项目

    今天就跟大家聊聊有关利用Java怎么开发一个企业级项目,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。什么是企业级项目开发  "企业级项目"、企业级项目开发,Java 也是企业级项目开
    2023-05-31

    编程热搜

    • Python 学习之路 - Python
      一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
      Python 学习之路 - Python
    • chatgpt的中文全称是什么
      chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
      chatgpt的中文全称是什么
    • C/C++中extern函数使用详解
    • C/C++可变参数的使用
      可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
      C/C++可变参数的使用
    • css样式文件该放在哪里
    • php中数组下标必须是连续的吗
    • Python 3 教程
      Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
      Python 3 教程
    • Python pip包管理
      一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
      Python pip包管理
    • ubuntu如何重新编译内核
    • 改善Java代码之慎用java动态编译

    目录