功能开发
通过前几章的学习,我们已经搭建好了博客网站的基本框架。本章我们将正式开始网站的功能开发。
开发前的准备
在正式实现业务逻辑之前,我们先来分析一下完成本应用所需的一些基本框架并将它们集成到工程中。
我们将在开发前做以下准备。
- 本系统需要用到MySQL,持久层框架采用MyBatis。
- 在缓存方面将用到Redis,主要用于用户登录信息、验证码等的存储。Redis在第5章中已经封装,本章将不再赘述。
- 搜索方面,我们采用比较成熟的Elasticsearch开发系统的搜索引擎。
下面我们就来分别集成并封装MyBatis和 Elasticsearch框架。
MyBatis的集成
MyBatis的集成比较简单,按照以下步骤操作即可。
(1)在public工程中添加 MySQL、MyBatis和 Druid的依赖:
代码语言:javascript复制<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId><version>1.3.2</version>
</ dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency><dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.1e</version>
</dependency>
其中 Druid是阿里巴巴开发的一个数据库连接池框架,本系统的数据库连接池框架采用Druid。
(2)在配置中心的远程Git仓库中新增配置文件datasource.yml并配置数据源:
代码语言:javascript复制spring:
datasource:
druid:
url: jdbc:mysql://localhost:3306/blog_db?useUnicode=true&characterEncoding=
UTF-8&useSSL=false
username: root
password:******stat-view- servlet:
login-username: adminlogin-password: admin
mybatis:
#配置mapper.xml的classpath路径
mapper- locations: classpath: /mapper/*Mapper.xmlconfiguration:
#配置项:开启下划线到驼峰的自动转换。作用:将数据库字段根据驼峰规则自动注入到对象属性。
map-underscore-to-camel-case: true
在上述配置中,spring.datasource.druid为数据库连接池Druid的基本配置。其中url为数据库连接字符串,username和 password分别对应数据库的用户名和密码。Druid的强大不仅在于它的数据库查询性能,还在于它提供了强大的Web界面,在该界面中可以查看当前数据库的信息、查询语句的执行效率统计等。在上述配置中,login-username和 login-password可以设置Druid的Web管理界面的用户名和密码,我们集成了Druid的微服务模块,可以通过地址 http:/localhost:8201/druid访问Druid的Web管理界面( 8201为集成了Druid的应用端口号)。当然,我们需要输入上述配置设置的用户名和密码,然后就可以进入其 Web管理主界面,如图10-1所示。
(3)在每个服务的配置中引入 datasource.yml :
代码语言:javascript复制spring:
cloud:
config:
name: eurekaclient , datasourcelabel: master
discovery :
enabled: trueserviceId: configusername: admin
password: admin
eureka:
client:
service-url:
defaultzone: http: //admin: admin123@localhost:8101/eureka/
前面已经提到,在spring.cloud.config.name 中设置要拉取的配置,多个配置之间以逗号分隔,因此要引入哪个配置文件,在逗号后面添加即可。
Elasticsearch 的集成
Elasticsearch是一个分布式的、基于Restful的全文搜索引擎。Elasticsearch是用Java开发的并作为Apache许可条款下的开放源码发布,是一款当前流行的企业级搜索引擎。常被用于云计算中,能够实现实时搜索且稳定、可靠、快速、方便。
我们可以将Elasticsearch看作一个用于全文检索的数据库,通过将需要检索的数据存储到Elasticsearch中,可以提升应用的搜索性能。想要将Elasticsearch集成到应用中,需要先安装Elasticsearch,本节将简单介绍Windows和 Mac两种操作系统的安装步骤。
1.在 Windows系统下安装Elasticsearch
从Elasticsearch官网 https:/www.elastic.co/downloads/elasticsearch中下载Windows版本的压缩包。解压缩文件并进入 bin目录,双击elasticsearch.bat 即可启动Elasticsearch。启动后,访问localhost:9200,如果出现如图10-2所示的界面,说明Elasticsearch安装成功。
2. macOS安装Elasticsearch。
我们可以直接通过命令brew install elasticsearch 完成安装,然后通过命令 /usr/local/cellar/elasticsearch/6.2.4/bin/elasticsearch启动Elasticsearch。
安装过程受限于网络环境,可能会比较耗时,需要耐心等待。
3.Spring Cloud集成Elasticsearch。
首先在search工程添加以下依赖:
代码语言:javascript复制<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId></ dependency>
<dependency>
<groupId>io.searchbox</groupId>
<artifactId>jest</artifactId>
</dependency>
其中, spring-boot-starter-data-elasticsearch为Spring Boot集成Elasticsearch所需的依赖包。elasticsearch已经具备了应用于Elasticsearch的Java API,但不支持 HTTP。Jest弥补了Elasticsearch自带API缺少HTTP客户端的不足。因此,引入Jest依赖可以很方便地访问Elasticsearch服务端。
在配置中心的Git仓库创建elasticsearch.yml文件,内容如下:
代码语言:javascript复制spring:
elasticsearch:
jest:
#本地启动的Elasticsearch开启的HTTP地址,端口默认为9200
uris: http://127.8.0.1:9200
该配置比较简单,只需要通过_uris执行 HTTP请求地址即可,上面指定的地址就是前面介绍Elastisearch安装时浏览器访问的地址。
下面我们进行单元测试,验证 Elasticsearch是否成功集成。注意,在做单元测试的时候,务必先将register和 config两个工程启动,因为服务的配置都存放在Git仓库中,如果不启动config工程,则无法从Git仓库拉取配置。测试代码如下:
代码语言:javascript复制@Data
public class ESBlog {
@JestId
private Long id;
private String title;private String summary;
}
//保存数据到ElasticsearchESBlog blog =new ESBlog();blog.setId(1L);
blog.setTitle("测试标题");blog.setSummary("测试摘要")
Index index = new Index.Builder(blog).index("blog-index" ).type("blog-table").build();
jestclient.execute(index);
1/查询数据
SearchSourceBuilder builder = new SearchSourceBuilder();//指定查询关键词和字段
builder.query(QueryBuilders.multiMatchQuery("摘要" , "title,summary".split(",")))
//分页,类似于MySQL中的limit 0,10
.from(e)
.size(10);
Search search = new Search.Builder(builder.toString())
.addIndex( "blog-index" )
.addType( "blog-table"). build();
JestResult ret = jestclient.execute(search);
List<ESBlog> blogList = ret.getSourceAs0bjectList(ESBlog.class);
system.out.println(list);
在保存数据时,首先通过工ndex类指定index为 blog-index,type为 blog-table,index可以理解为数据库名(相当于MySQL的database ), type可以理解为表名(相当于MysQL的 table ),通过execute方法即可完成数据保存。
在查询数据时,可以实例化searchSourceBuilder对象并执行查询的关键词和字段,当然,它也支持分页,通过from和size方法执行分页参数即可。最后构建Search对象,并执行index和 type,执行execute方法即可完成数据查询。
执行上述代码,可以看到数据保存后被成功返回,这样就完成了Elasticsearch的集成。
利用代码生成器提升开发效率
本应用的持久层采用MyBatis框架,而 MyBatis需要编写原生SQL。应用操作中,占比最多的是一些单表操作或者基础的SQL语句(如增删改),如果每个语句都重新编写,工作量巨大且效率低下。我们可以利用代码生成器帮我们自动生成一些基础代码,以减少开发量。
本节将介绍一款开源的MyBatis代码生成器: mybatis-generator。mybatis-generator可以帮我们生成大量的基础SQL语句。使用方法如下。
(1)在父工程上新建一个工程并将其命名为mybatis-generator,然后编写pom.xml文件:
代码语言:javascript复制<dependencies>
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.2</version>
</dependency></dependencies><build>
<finalName >myabis-generator</finalName><resources>
<resource>
<directory>src/main/java</directory><includes>
<include>**/*.xml</include></includes>
</ resource>
<resource> <!--配置需要被替换的资源文件路径-->
<directory>src/main/resources</directory>cincludes>
<include>**/*.properties</include><include>**/*.xml</include>
</includes>
<filtering>true</filtering></resource>
</resources><plugins>
<!-- mybatis-generator插件--><plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId><version>${mybatis.generator.version}</version>
<dependencies>
<dependency>
<groupId>com. lynn.blog</groupId>
<artifactId>mybatis-generator</ artifactId><version>1.0-SNAPSHOT</version>
< / dependency>
</dependencies><configuration>
<verbose>true</verbose>
<overwrite>true</overwrite>< / configuration>
</plugin>
</plugins>
< /build>
mybatis-generator-core为上述插件所需的依赖包,只需要添加该依赖就能让我们通过编码实现代码生成器规则,但是要执行代码生成器规则还需要指定generator的 Maven插件。在<build>标签后加入对应的Maven插件 mybatis-generator-maven-plugin并指定用于mybatis-generator 工程,我门就可以执行插件了。
(2)新建配置文件 generator.properties:
代码语言:javascript复制generator .jdbc.driver=com.mysql.jdbc.Driver
generator.jdbc.url=jdbc:mysql://localhost:3306/blog_db?useUnicode=true&
characterEncoding=utf-8& autoReconnect=true& useSSL=false
generator.jdbc.username=root
generator.jdbc.password=**丰率**#MySQL 驱动所在全路径
classPathEntry=/Users/lynn/Downloads/mysql-connector-java-5.1.47.jar
上述配置较为简单,只需要指定数据库连接信息和MySQL驱动所在的全路径即可。
(3) mybatis-generator提供了一些默认生成,比如 tinyintW默认生成BIT类型、没有分页等,它提供了插件接口,我们可以自定义插件,扩展代码生成器的规则。下面以分页插件为例讲解自定义插件的生成,读者可以参照本书的配套源码了解其余实现,请看代码:
代码语言:javascript复制public class PaginationPlugin extends PluginAdapter {
@Override
public boolean validate(List<String> list) {
return true;
}
/**
*为每个Example类添加limit和offset属性和set、get方法*/
@override
public boolean modelExampleClassGenerated(TopLevelclass topLevelClass,
IntrospectedTable introspectedTable) {
PrimitiveTypewrapper integerwrapper = FullyQualifiedavaType.getIntInstance().
getPrimitiveTypewrapper();
Field limit = new Field();limit.setName("limit");
limit.setvisibility( Javavisibility. PRIVATE);limit.setType(integerwrapper);
topLevelclass.addField(limit);Method setLimit = new Method();
setLimit.setVisibility ( avavisibility.PUBLIC);setLimit.setName( "setLimit");
setLimit.addParameter(new Parameter(integerwrapper,"limit"));setLimit.addBodyLine( "this.limit = limit; ");
topLeve1class.addMethod(setLimit);
Method getLimit = new Method();
getLimit.setvisibility ( Javavisibility.PUBLIC);
getLimit.setReturnType(integerwrapper);getLimit.setName( "getLimit" );
getLimit.addBodyLine( "return limit; ");topLevelclass .addMethod(getLimit);Field offset = new Field();
offset.setName( "offset");
offset.setvisibility ( Javavisibility. PRIVATE);offset.setType(integerwrapper);
topLevelclass.addField(offset);Method setoffset = new Method();
setOffset.setVisibility (avavisibility. PUBLIC);setOffset.setName( "setOffset");
setOffset. addParameter(new Parameter(integerwrapper,"offset"));setOffset.addBodyLine("this.offset = offset; ");
topLevelclass.addMethod(setOffset);
Method getoffset = new Method();
getOffset.setvisibility( Javavisibility . PUBLIC);getOffset.setReturnType(integerwrapper);
getOffset.setName( "getOffset" );
getOffset.addBodyLine( "return offset; ");topLevelclass.addMethod(getoffset);
return true;
}
@override
public boolean sqlMapSelectByExamplewithoutBLOBsElementGenerated(XmlElement element,
IntrospectedTable introspectedTable){
XmlElement ifLimitNotNullElement = new XmlElement("if");
ifLimitNotNullElement.addAttribute(new Attribute("test", "limit != null"));Xm1Element ifOffsetNotNullElement = new XmlElement("if");
ifOffsetNotNullElement.addAttribute(new Attribute( "test","offset != null"));ifOffsetNotNullElement.addElement(new TextElement("limit ${offset},${limit}"));ifLimitNotNullElement.addElement(ifOffsetNotNul1Element);
Xm1Element ifOffsetNullElement = new Xm1Element("if");
ifOffsetNullElement.addAttribute(new Attribute("test","offset == null"));ifOffsetNullElement.addElement(new TextElement("limit ${limit}"));
ifLimitNotNullElement.addElement(iFOffsetNullElement);
element.addElement(ifLimitNotNullElement);
return true;
}
}
generator提供了一个PluginAdapter类,方便我们实现自定义的代码生成插件,因此想要实现自定义插件,首先应继承PluginAdapter类并重写validate、 modelExampleclassGenerated 和sqlMapSelectByExamplewithoutBLOBsElementGenerated方法。其中, validate用于设置插件是否有效,上述代码返回true,表示始终有效;modelExampleClassGenerated 的作用是生成Example代码,我们生成了分页必须的 offset 和 limit方法,通过设置offset和 limit即可完成分页;sqlMapSelectByExamplewithoutBLOBSElementGenerated的作用是生成Mapper.xml代码,我们知道MySQL是通过limit关键词来分页的,因此上述代码也相应的生成limit语句来完成分页查询语句的创建。
(4)新建generatorConfig.xml:
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8”?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration
1.0//EN""http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >
<generatorConfiguration>
<!--配置文件-->
<properties resource="generator.properties"></properties><!--驱动包-->
<classPathEntry location="${classPathEntry]"/>
<context id="MysqlContext" targetRuntime="MyBatis3" defaultModelType="flat">
<property name="javaFileEncoding" value="UTF-8""/>
<!--由于beginningDelimiter和endingDelimiter 的默认值为双引号(""),在MySQL 中不能
这么写,所以还要将这两个默认值改为`-->
<property name="beginningDelimiter" value="~"/><property name="endingDelimiter" value=""/>
<property name="useActualcolumnNames" value="false"/><!--为生成的Java模型创建一个toString方法-->
<plugin type="org.mybatis.generator.plugins.ToStringPlugin"></plugin><!--为生成的Java模型类添加序列化接口,并生成serialVersionUID字段--><plugin type="com.lynn.blog.generator.plugin.SerializablePlugin">
<property name="suppressJavaInterface" value="false" />
</plugin>
<!--生成一个新的selectByExample方法,这个方法可以接收offset和limit参数,主要用来
实现分页-->
<plugin type=" com.lynn.blog.generator.plugin.PaginationPlugin"></plugin><!-- Java模型生成equals和 hashcode方法-->
<plugin type="org.mybatis.generator.plugins.EqualsHashCodePlugin"></plugin><!--生成的代码添加自定义注释-->
<commentGenerator type="com.lynn.blog.generator.plugin.CommentGenerator">
<property name="suppressAl1Comments" value="true" />
<property name="suppressDate" value="true" / >
</ commentGenerator>
<!--数据库连接-->
<jdbcConnection driverclass="${generator.jdbc.driver}"
connectionURL="${generator.jdbc.url}"
userId="${generator.jdbc.username}"
password="$igenerator.jdbc.password}" />
<javaTypeResolver type="com.lynn.blog.generator.plugin.TypeGenerator" /><!-- model生成-->
<javaModelGenerator targetPackage="com.lynn.blog . pub .domain.entity"
targetProject="src/main/java" />
<!-- MapperXML生成-->
<sqlMapGenerator targetPackage="com. lynn.blog.pub. xml"
targetProject="src/main/java" />
<! -- Mapper接口生成-->
<javaclientGenerator targetPackage="com.lynn.blog.pub.mapper"
targetProject="src/main/java" type="XMLMAPPER"/>
<table tableName="blog" domainobjectName="Blog"><property name=
"useActualColumnNames" value="false" /></table>
<table tableName="category" domainobjectName="category"><property name=
"useActualColumnNames" value="false" /></table>
<table tableName="collect" domainobjectName="cCollect"><property name=
"useActualColumnNames" value="false"/></table>
<table tableName="comment" domain0bjectName="comment"><property name=
"useActualColumnNames" value="false" /></table>
<table tableName="like" domainobjectName="Like"><property name=
"useActualColumnNames" value="false" /></table>
<table tableName="user" domainobjectName="User" ><property name=
"useActualColumnNames" value="false" /></table>
</ context>
</generatorConfiguration>
我们要自动生成相应的数据库CRUDR代码,还需要创建配置文件说明生成原则。在上述配置中,<plugin>标签指定了一些代码生成器的插件,其中可以看到有些是自定义的插件,有些是generator 内置插件;<property>标签指定了基本属性,其中 useActualcolumnNames用于设置是否直接使用数据库的字段名,本处设置为 false,即不用数据库的名字,而是使用驼峰命名;<javaModelGenerator>标签指定了entity生成的目标包名; <sqlMapGenerator>标签指定Mapper.xml所在目录;<javaClientGenerator>标签指定Mapper.java所在包名;<table>标签指定要生成的表名,其中,tableName为数据库对应的表名,domainobjectName为生成后的实体名。
(5)利用mybatis-generator插件生成代码,如图10-3所示。
先编译mybatis-generator工程,再点击Run即可快速生成代码。生成完成后,我们可以在mybatis-generator工程中看到代码,如图10-4所示。
我们可以将这些代码都复制到public工程中,这样就完成了代码的生成,生成的代码包含了基本的CRUD,查询语句支持动态查询、分页、排序等功能。
本文给大家讲解的内容是springcloud实战:网站功能开发,利用代码生成器提升开发效率
- 下篇文章给大家讲解的是springcloud实战:使用代码生成器生成的代码操作数据库;
- 觉得文章不错的朋友可以转发此文关注小编;
- 感谢大家的支持!
本文就是愿天堂没有BUG给大家分享的内容,大家有收获的话可以分享下,想学习更多的话可以到微信公众号里找我,我等你哦。