各位好,我们把之前的坑填一下,我在上上篇文章中写了springboot集成es7 的方法,并且集成了es原生客户端  High  Level Rest Client, 也说明了原因, 我用的版本较高, spring-data封装的es版本较低,所以使用了原生的。

当我们把这一切都准备好的时候,剩下的就是要体验Es的功能了,Es中突出能力就是他的搜索能力。 要想搜索,必须先有数据,而在es中的数据结构, 是由索引,类型和文档组成的,分别对应关系数据库中的,库,表,行。所以使用es的第一步,就是设计我们的数据结构。 那就需要先得有索引,而在创建索引的时候,就必须给出他的结构,比如有哪些字段,是什么类型,需不需要分词。

关于ES中的数据类型,可参照官网: https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html

关于ES中索引的api, 可参照官网: https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.x/java-rest-high-create-index.html

关于使用java HighLevelRestClient如何操作索引,可参照官网: https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.x/java-rest-high-create-index.html

接下来书接上回,上次我们封装了一个工具类用于创建索引,举例创建了一个只有四个字段的索引是如何创建的,比如我创建一个Person的索引,索引结构包括: 姓名,年龄, 描述, 和id . ES中的索引其实就是一个大的json结构,所以我们可以直接通过restful请求,发送json参数来实现。我们这里中电说下如何使用 java api 完成。 再贴一遍代码:

    /*** 创建索引(默认分片数为5和副本数为1)* @param indexName* @throws IOException*/public boolean createIndex(String indexName) throws IOException {CreateIndexRequest request = new CreateIndexRequest(indexName);request.settings(Settings.builder()// 设置分片数为3, 副本为2.put("index.number_of_shards", 3).put("index.number_of_replicas", 2));// 这里创建索引结构request.mapping(generateBuilder());CreateIndexResponse response = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);// 指示是否所有节点都已确认请求boolean acknowledged = response.isAcknowledged();// 指示是否在超时之前为索引中的每个分片启动了必需的分片副本数boolean shardsAcknowledged = response.isShardsAcknowledged();if (acknowledged || shardsAcknowledged) {log.info("创建索引成功!索引名称为{}", indexName);return true;}return false;}private XContentBuilder generateBuilder() throws IOException {XContentBuilder builder = XContentFactory.jsonBuilder();builder.startObject();{builder.startObject("properties");{// es7及以后去掉了映射类型--personbuilder.startObject("name");{builder.field("type", "text");builder.field("analyzer", "ik_smart");}builder.endObject();}{builder.startObject("age");{builder.field("type", "integer");}builder.endObject();}{builder.startObject("desc");{builder.field("type", "text");builder.field("analyzer", "ik_smart");}builder.endObject();}{builder.startObject("id");{builder.field("type", "integer");}builder.endObject();}builder.endObject();}builder.endObject();/*.startObject().field("properties").startObject().field("person").startObject("name").field("type" , "text").field("analyzer", "ik_smart").endObject().startObject("age").field("type" , "int").endObject().startObject("desc").field("type", "text").field("analyzer", "ik_smart").endObject().endObject().endObject();*/return builder;}

上面可以看到,这里调用了一个generateBuilder() 方法来创建接构,这个方法里就是组装我们的数据结构。 也就是我们的四个字段。

这里我们将name 和 desc 设置成了text字段, text字段属于String, 并指定了ik_smart 就是ik的中文分词器,也就是这个字段就在存储的时候就会自动分词,那么我们查询的时候就可以根据分词进行查询,除了text类型,String中还有一种叫做keyword,就是关键字,keyword是不会分词的,会把整个字段作为关键字搜索, 比如身份证号,订单号,这些不是由一些单词组成的,一般都是整个匹配的,那么就是用keyword。

好了回归正题, 这个时候我们发现如果我们要设计一个相对比较复杂的索引结构,按照上面的写法就太麻烦了,各种大括号,可能早就绕晕了,其实我们无非是想把我们需要索引的字段按照一定的格式设置到json结构中去,所以我们完全可以将一个实体映射成一个我们想要的索引结构,比如有一个类Person ,里面有上面的四个属性,我们可以根据Person的类结构,获取里面的所有字段去构建索引结构,唯一的问题就是,我们可能没法区分哪些字段映射成哪些类型。那么我们完全可以通过一些标识来标注生成索引时他们映射成什么类型。所以可以使用自定义注解。

这里我们定义一个Field注解,用在类的属性上,标注这个字典升射成es中的什么类型,以及使用什么分词器。

/*** 作用在字段上,用于定义类型,映射关系* @author ls*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
@Inherited
public @interface Field  {FieldType type() default FieldType.TEXT;/*** 指定分词器* @return*/AnalyzerType analyzer() default AnalyzerType.STANDARD;}

这里边的FieldType和 AnalyzerType是定义好的枚举,保存一些常用类型

/*** es 类型参看* https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html*/
@Getter
public enum FieldType {/*** text*/TEXT("text"),KEYWORD("keyword"),INTEGER("integer"),DOUBLE("double"),DATE("date"),/*** 单条数据*/OBJECT("object"),/*** 嵌套数组*/NESTED("nested"),;FieldType(String type){this.type = type;}private String type;}
@Getter
public enum AnalyzerType {NO("不使用分词"),/*** 标准分词,默认分词器*/STANDARD("standard"),/*** ik_smart:会做最粗粒度的拆分;已被分出的词语将不会再次被其它词语占有*/IK_SMART("ik_smart"),/*** ik_max_word :会将文本做最细粒度的拆分;尽可能多的拆分出词语*/IK_MAX_WORD("ik_max_word");private String type;AnalyzerType(String type){this.type = type;}}

然后在我们的类上标识: 我这里用一个事件类,并且做了对象嵌套, Event类中还有一个action

/*** @className: Event* @description: 定义事件的文档类型* @author: sh.Liu* @create: 2020-05-27 10:02*/
@Data
public class Event {private static final long serialVersionUID=1L;@Field(type = FieldType.KEYWORD)private Integer eventId;/*** 唯一标识码*/@Field(type = FieldType.KEYWORD)private String uniqueCode;/*** 任务号*/@Field(type = FieldType.KEYWORD)private String eventCode;/*** 事件来源编号*/@Field(type = FieldType.INTEGER)private Integer eventSrcId;/*** 事件来源名称*/@Field(type = FieldType.TEXT, analyzer = AnalyzerType.IK_SMART)private String eventSrcName;/*** 来源分组*/@Field(type = FieldType.KEYWORD)private String srcGroupCode;/*** 事件大类编码*/@Field(type = FieldType.KEYWORD)private String eventTypeCode;/*** 事件大类名称*/@Field(type = FieldType.KEYWORD)private String eventTypeName;/*** 事件小类编码*/@Field(type = FieldType.KEYWORD)private String eventSubtypeCode;/*** 事件小类名称*/@Field(type = FieldType.KEYWORD)private String eventSubtypeName;/*** 重要程度*/@Field(type = FieldType.INTEGER)private Integer eventGradeId;/*** 重要程度名称*/@Field(type = FieldType.KEYWORD)private String eventGradeName;/***紧急程度标识*/@Field(type = FieldType.INTEGER)private Integer eventUrgencyId;/***紧急程度名称*/@Field(type = FieldType.KEYWORD)private String eventUrgencyName;/***事件级别标识*/@Field(type = FieldType.INTEGER)private Integer eventLevelId;/***事件级别名称*/@Field(type = FieldType.KEYWORD)private String eventLevelName;/***事件升级标志*/@Field(type = FieldType.INTEGER)private Integer eventUpgradeFlag;/***处置级别标识*/@Field(type = FieldType.INTEGER)private Integer dealLevelId;/***处置级别标识*/@Field(type = FieldType.KEYWORD)private String dealLevelName;/***公众上报人名称*/@Field(type = FieldType.TEXT , analyzer = AnalyzerType.IK_SMART)private String publicReporterName;/***公众上报人身份证号*/@Field(type = FieldType.KEYWORD)private String publicReporterIdcard;/***公众上报人联系方式*/@Field(type = FieldType.KEYWORD)private String publicReporterTel;/*** 事件描述*/@Field(type = FieldType.TEXT, analyzer = AnalyzerType.IK_SMART)private String eventDesc;/*** 地址*/@Fieldprivate String address;/*** 地区名称*/@Field(type = FieldType.KEYWORD)private String areaRegionName;/*** 地区编码*/@Field(type = FieldType.KEYWORD)private String areaRegionCode;/*** 社区名称*/@Field(type = FieldType.KEYWORD)private String commRegionName;/*** 区编码*/@Field(type = FieldType.KEYWORD)private String commRegionCode;/*** 街道名称*/@Field(type = FieldType.KEYWORD)private String streetRegionName;/*** 街道编码*/@Field(type = FieldType.KEYWORD)private String streetRegionCode;/*** 社区名称*/@Field(type = FieldType.KEYWORD)private String regionName;/*** 社区编码*/@Field(type = FieldType.KEYWORD)private String regionCode;/*** 经度*/private BigDecimal coordX;/*** 纬度*/private BigDecimal coordY;/***坐标系*/private String mapcoordinate;/***网格员标识*/private Integer griderId;/***网格员标识*/private String griderName;/***网格员电话*/private String griderPhone;/***核实状态标识*/@Field(type = FieldType.INTEGER)private Integer verifyStateId;/***核查状态标识*/@Field(type = FieldType.INTEGER)private Integer checkStateId;/***事件建立时间*/@Field(type = FieldType.DATE)@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")private Date createTime;/***流程结束时间*/@Field(type = FieldType.DATE)@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")private Date endTime;/***延期天数*/private Float postponedDays;/***延期标志*/private Integer postponedFlag;/***受理时间*/@Field(type = FieldType.DATE)@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")private Date acceptTime;/***立案时间*/@Field(type = FieldType.DATE)@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")private Date establishTime;/***调度时间*/@Field(type = FieldType.DATE)@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")private Date dispatchTime;/***流程开始时间*/@Field(type = FieldType.DATE)@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")private Date procStartTime;/***流程结束时间*/@Field(type = FieldType.DATE)@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")private Date procEndTime;/***流程截止时间*/@Field(type = FieldType.DATE)@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")private Date procDeadLine;/***流程警告时间*/@Field(type = FieldType.DATE)@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")private Date procWarningTime;/***处置开始时间*/@Field(type = FieldType.DATE)@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")private Date funcBeginTime;/***处置完成时间*/@Field(type = FieldType.DATE)@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")private Date funcFinishTime;/***自处置标识*/private Integer gridDealFlag;/***跨网格标志*/private Integer overGridFlag;/***是否督办*/@Field(type = FieldType.INTEGER)private Integer pressFlag;/***是否催办*/@Field(type = FieldType.INTEGER)private Integer hurryFlag;/***超期标志*/@Field(type = FieldType.INTEGER)private Integer overtimeFlag;/***活动属性*/@Field(type = FieldType.INTEGER)private Integer actPropertyId;/***活动属性名称*/@Field(type = FieldType.KEYWORD)private String actPropertyName;/***流程实例标识*/@Field(type = FieldType.KEYWORD)private String procInstId;/***流程定义标识*/@Field(type = FieldType.KEYWORD)private String procDefId;/***事件状态*/@Field(type = FieldType.INTEGER)private Integer eventStateId;/*** 上一操作项*/@Field(type = FieldType.KEYWORD)private String preActionName;/*** 登记人Id*/@Field(type = FieldType.INTEGER)private Integer registerId;/*** 登记人姓名*/@Field(analyzer = AnalyzerType.IK_SMART)private String registerName;/*** 回访标识:0-未回访 1-已回访 2-无法回访*/@Field(type = FieldType.INTEGER)private Integer visitorStateId;/*** 删除标识*/@Field(type = FieldType.INTEGER)private Integer deleteFlag;/*** 删除用户标识*/@Field(type = FieldType.INTEGER)private Integer deleteUserId;/*** 删除时间*/@Field(type = FieldType.DATE)@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")private Date deleteTime;/*** 是否下发督查* 0:否,1:是*/@Field(type = FieldType.INTEGER)private Integer overseerFlag;@Field(type = FieldType.DATE)@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")private Date updateTime;@Field(type = FieldType.OBJECT)private Act action;}

注意,最后的act就是一个对象, es中还有嵌套数组的支持。这里以嵌套对象为例

/*** @className: Action* @description: 事件表* @author: sh.Liu* @create: 2020-05-27 10:15*/
@Data
public class Act implements Serializable {private static final long serialVersionUID = 1L;@Field(type = FieldType.INTEGER)private Integer actId;/*** 任务标识*/@Field(type = FieldType.KEYWORD)private String taskId;/*** 流程定义标识*/@Field(type = FieldType.KEYWORD)private String procDefId;/*** 流程实例标识*/@Field(type = FieldType.KEYWORD)private String procInstId;/*** 子流程实例标识*/@Field(type = FieldType.KEYWORD)private String subInstId;/*** 节点定义标识*/@Field(type = FieldType.KEYWORD)private String nodeId;/*** 节点定义名称*/@Field(type = FieldType.KEYWORD)private String nodeName;/*** 业务主键标识*/@Field(type = FieldType.KEYWORD)private String bizId;/*** 参与者标识*/@Field(type = FieldType.KEYWORD)private String partId;/*** 参与者姓名*/@Field(type = FieldType.TEXT)private String partName;/*** 部门id*/@Field(type = FieldType.KEYWORD)private String unitId;/*** 部门名称*/@Field(type = FieldType.TEXT)private String unitName;/*** 角色标识*/@Field(type = FieldType.KEYWORD)private String roleId;/*** 角色名称*/@Field(type = FieldType.TEXT, analyzer = AnalyzerType.IK_SMART)private String roleName;/*** 上一活动标识*/@Field(type = FieldType.TEXT, analyzer = AnalyzerType.IK_SMART)private String preActId;/*** 上一活动参与者标识*/private String prePartId;/*** 上一活动参与者名称*/@Field(type = FieldType.TEXT, analyzer = AnalyzerType.IK_SMART)private String prePartName;/*** 上一活动定义标识*/private String preNodeId;/*** 上一活动定义名称*/@Field(type = FieldType.TEXT, analyzer = AnalyzerType.IK_SMART)private String preNodeName;/*** 上一活动意见*/@Field(type = FieldType.TEXT, analyzer = AnalyzerType.IK_SMART)private String preOpinion;/*** 上一活动操作项名称*/@Field(type = FieldType.TEXT, analyzer = AnalyzerType.IK_SMART)private String preActionName;/*** 上一活动操作项显示名称*/@Field(type = FieldType.TEXT, analyzer = AnalyzerType.IK_SMART)private String preActionLabel;/*** 创建时间*/@Field(type = FieldType.DATE)private Date createTime;/*** 截止时间*/@Field(type = FieldType.DATE)private Date deadLine;/*** 警告时间*/@Field(type = FieldType.DATE)private Date warningTime;/*** 结束时间*/@Field(type = FieldType.DATE)private Date endTime;/*** 活动红绿灯*/@Field(type = FieldType.INTEGER)private Integer actTimeStateId;/*** 活动时限*/@Field(type = FieldType.DOUBLE)private BigDecimal timeLimit;/*** 计时单位*/@Field(type = FieldType.INTEGER)private Integer timeUnit;/*** 活动时限分钟*/@Field(type = FieldType.INTEGER)private Integer timeLimitM;/*** 已用时*/@Field(type = FieldType.INTEGER)private Integer actUsedTime;/*** 剩余时*/@Field(type = FieldType.INTEGER)private Integer actRemainTime;/*** 活动时限信息*/@Field(type = FieldType.TEXT, analyzer = AnalyzerType.IK_SMART)private String actLimitInfo;/*** 活动已用时间字符串*/@Field(type = FieldType.TEXT, analyzer = AnalyzerType.IK_SMART)private String actUsedTimeChar;/*** 活动剩余时间字符串*/@Field(type = FieldType.TEXT, analyzer = AnalyzerType.IK_SMART)private String actRemainTimeChar;/*** 累计计时标志*/@Field(type = FieldType.INTEGER)private Integer timeAddUpFlag;/*** 暂停前节点用时*/@Field(type = FieldType.INTEGER)private Integer actUsedTimeBeforeStop;/*** 恢复计时时间*/@Field(type = FieldType.DATE)private Date actRestart;/*** 已读标志*/@Field(type = FieldType.INTEGER)private Integer readFlag;/*** 已读时间*/@Field(type = FieldType.DATE)private Date readTime;/*** 批转意见*/@Field(type = FieldType.TEXT, analyzer = AnalyzerType.IK_SMART)private String transOpinion;/*** 操作项名称*/@Field(type = FieldType.TEXT, analyzer = AnalyzerType.IK_SMART)private String actionName;/*** 操作项显示名称*/@Field(type = FieldType.TEXT, analyzer = AnalyzerType.IK_SMART)private String actionLabel;/*** 活动属性id*/@Field(type = FieldType.INTEGER)private Integer actPropertyId;/*** 活动属性名称*/@Field(type = FieldType.TEXT, analyzer = AnalyzerType.IK_SMART)private String actPropertyName;/*** 抄送参与者*/@Field(type = FieldType.TEXT, analyzer = AnalyzerType.IK_SMART)private String ccPart;/*** 抄送参与者名称*/@Field(type = FieldType.TEXT, analyzer = AnalyzerType.IK_SMART)private String ccPartName;/*** 抄送标志*/@Field(type = FieldType.INTEGER)private Integer ccFlag;

然后我们在工具类中定义个方法,创建一个索引,传入Class类型的参数,这个根据Class对象我们就可以获取这个类上的所以属性,以及属性上的注解,根据注解我们可以得到它所映射的es类型,以及分词器,然后创建出我们想要的索引。

public XContentBuilder generateBuilder(Class clazz) throws IOException {// 获取索引名称及类型// Document doc = (Document) clazz.getAnnotation(Document.class);// System.out.println(doc.index());// System.out.println(doc.type());XContentBuilder builder = XContentFactory.jsonBuilder();builder.startObject();builder.startObject("properties");Field[] declaredFields = clazz.getDeclaredFields();for (Field f : declaredFields) {if (f.isAnnotationPresent(com.lsqingfeng.action.es.annotation.Field.class)) {// 获取注解com.lsqingfeng.action.es.annotation.Field declaredAnnotation = f.getDeclaredAnnotation(com.lsqingfeng.action.es.annotation.Field.class);// 如果嵌套对象:/*** {*   "mappings": {*     "properties": {*       "region": {*         "type": "keyword"*       },*       "manager": {*         "properties": {*           "age":  { "type": "integer" },*           "name": {*             "properties": {*               "first": { "type": "text" },*               "last":  { "type": "text" }*             }*           }*         }*       }*     }*   }* }*/if (declaredAnnotation.type() == FieldType.OBJECT) {// 获取当前类的对象-- ActionClass<?> type = f.getType();Field[] df2 = type.getDeclaredFields();builder.startObject(f.getName());builder.startObject("properties");// 遍历该对象中的所有属性for (Field f2 : df2) {if (f2.isAnnotationPresent(com.lsqingfeng.action.es.annotation.Field.class)) {// 获取注解com.lsqingfeng.action.es.annotation.Field declaredAnnotation2 = f2.getDeclaredAnnotation(com.lsqingfeng.action.es.annotation.Field.class);builder.startObject(f2.getName());builder.field("type", declaredAnnotation2.type().getType());// keyword不需要分词if (declaredAnnotation2.type() == FieldType.TEXT) {builder.field("analyzer", declaredAnnotation2.analyzer().getType());}builder.endObject();}}builder.endObject();builder.endObject();}else{builder.startObject(f.getName());builder.field("type", declaredAnnotation.type().getType());// keyword不需要分词if (declaredAnnotation.type() == FieldType.TEXT) {builder.field("analyzer", declaredAnnotation.analyzer().getType());}builder.endObject();}}}// 对应propertybuilder.endObject();builder.endObject();return builder;}

这里直接重载了generateBuilder() 方法,传一个Class即可。 这样你调用最上面的createIndex方法,把里边的generateBuilder() 改为  generateBuilder(Event.class); 这样就会根据Event的实体结构创建出Event对应的索引结构。 这里其实我们也可以定义一个注解来标识你想定义的索引名称。

这样在执行createIndex方法的时候,就按照Event类生成了相应的索引结构。虽然写的注解比较多,但是应该比多而乱的大括号好一些,当然大家也可以直接读取类中所有字段,这样就不用每个字段上都加注解了,然后有注解的特殊处理,其余的走默认值,这样也能减少不少代码。主要是原理搞清楚了,剩下的就靠自己发挥了。

好了这篇文章就介绍到这里,下次我们介绍下,es中的一些常用查询方法,如何实现全文检索,分页和高亮显示。

如果文章对你有帮助,打赏就不用了,给波关注吧,如果有问题,欢迎评论区下方留言,一起交流。

springboot集成es7.2自定义注解创建索引相关推荐

  1. spring boot集成swagger,自定义注解,拦截器,xss过滤,异步调用,定时任务案例...

    本文介绍spring boot集成swagger,自定义注解,拦截器,xss过滤,异步调用,定时任务案例 集成swagger--对于做前后端分离的项目,后端只需要提供接口访问,swagger提供了接口 ...

  2. 在SpringBoot项目中,自定义注解+拦截器优雅的实现敏感数据的加解密!

    在实际生产项目中,经常需要对如身份证信息.手机号.真实姓名等的敏感数据进行加密数据库存储,但在业务代码中对敏感信息进行手动加解密则十分不优雅,甚至会存在错加密.漏加密.业务人员需要知道实际的加密规则等 ...

  3. SpringBoot+拦截器+自定义异常+自定义注解+全局异常处理简单实现接口权限管理...

    点击关注公众号,实用技术文章及时了解 来源:blog.csdn.net/weixin_44102992/article/ details/107335702 前言 提到权限管理这块肯定很多人第一想到的 ...

  4. SpringBoot系列之使用自定义注解校验用户是否登录

    记得今年年初刚开始面试的时候,被问的最多的就是你知道Spring的两大核心嘛?那你说说什么是AOP,什么是IOC?我相信你可能也被问了很多次了. 1.到底是什么是AOP? 所谓AOP也就是面向切面编程 ...

  5. springboot微信公众号自定义菜单创建及响应

    微信公众号自定义菜单创建及响应 前言 本篇博客是为了速度开发微信公众号而进行讲解,对于深入的原理性讲解推荐去观看微信公众平台的官方文档. 微信公众号分为订阅号,服务号,小程序,本篇主要是介绍的订阅号开 ...

  6. 如何在springboot项目中使用自定义注解实现系统操作日志的功能

    通常我们的项目中都需要记录操作日志,方便回溯问题,找到根源. 因为给项目添加日志记录功能是属于系统级别的功能,所以这个问题我们马上会想到spring的AOP,可以通过切面的形式.那么怎么来实现呢? 先 ...

  7. SpringBoot集成Redis--配置自定义的RedisCacheManager

    2019独角兽企业重金招聘Python工程师标准>>> 配置自定义的RedisCacheManager--1自定义键生成规则 默认的键生成器 当不指定缓存的key时,SpringBo ...

  8. SpringBoot集成Groovy、Mybatis注解 实现动态SQL,帮你摆脱繁琐的XML配置

    SpringBoot的超简洁配置,为我们省去了宝贵的配置时间. Mybatis3在这方面也提供了很好的支持,通过注解让我们摆脱了繁琐的mapper xml,写DAO层的时候再也不用在java接口和xm ...

  9. SpringBoot集成Mybatis(0配置注解版)

    Mybatis初期使用比较麻烦,需要各种配置文件.实体类.dao层映射关联.还有一大推其它配置.当然Mybatis也发现了这种弊端,初期开发了generator可以根据表结构自动生成实体类.配置文件和 ...

最新文章

  1. python编辑器,作为初学者该如何抉择?
  2. 浅谈数据库乐观锁、悲观锁
  3. vue源码构建代码分析
  4. Django之url和视图函数
  5. 实用--HTML的命名规范
  6. 29 | 堆的应用:如何快速获取到Top 10最热门的搜索关键词?
  7. 强一致性和弱一致性的区别
  8. 在Ubuntu Server上使用vtk处理体数据,直接得到渲染结果图片避免显示窗口
  9. 工商银行黄金开户问答题答案
  10. asp.net ftp上传文件到服务器,.net 文件上传到服务器上
  11. TextView IME option
  12. idea怎么给项目改名_IDEA相关配置【java项目改造成web项目】
  13. 计算机体系结构.是系统结构还是体系结构
  14. smali java_Android逆向——初识smali与java类
  15. 用拉格朗日插值法,牛顿插值和分段线性插值计算近似值
  16. POI java.lang.IllegalArgumentException: Merged region xxx must contain 2 or more cells问题解决
  17. MOV格式视频转MP4
  18. 南师大计算机专硕士复试分数线,南京师范大学2020年硕士研究生复试分数线的通知...
  19. 通过 Amazon EFS 对 NFS 文件系统权限进行细粒度控制
  20. python多线程爬堆糖的图片

热门文章

  1. 关闭 php X-Powered-By 信息
  2. 组态王图素制作_组态王帮助界面的制作
  3. 现在做网站到底需要多少钱?
  4. 苹果手机用的linux系统,苹果最差的手机竟是它,你有用过吗?
  5. 如何复制cmd命令行文字
  6. PLSQL Developer 最新版安装、汉化、激活工具
  7. 微信小程序 wxss之 background 属性
  8. 用python自动绘制小猪佩奇
  9. Bzoj1502【NOI2005】月下柠檬树
  10. 基于音频指纹技术的微信“摇一摇搜歌”和QQ音乐“听歌识曲”