一、概述
工程基于Spring-boot 2.1.5.RELEASE 版本构建,工程父项目为heima-leadnews,并通过继承方式集成Spring-boot。
父项目下分5个公共子项目:
1.heima-leadnews-common : 是整个工程的配置核心,包括所有集成三方框架的配置定义,比如redis、kafka等。除此之外还包括项目每个模块及整个项目的常量定义;
2.heima-leadnews-model :项目中用到的Dto、Pojo、Mapper、Enums定义工程;
3.heima-leadnews-utils : 工程公用工具类项目,包含加密/解密、Date、JSON等工具类;
4.heima-leadnew-apis : 整个项目微服务暴露的接口的定义项目,按每个模块进行子包拆分;
5.heima-leadnew-tests : 用于存放工程中通用的测试用例;
二、项目需求
三、技术架构
本章实现功能如下:
1.基础工程的导入以及熟悉
一、基础工程的创建与导入
mysql.core.jdbc.url=jdbc:mysql://localhost:3306/heima-leadnews?autoReconnect=true&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
mysql.core.jdbc.username=root
mysql.core.jdbc.password=toor
mysql.core.jdbc.driver=com.mysql.jdbc.Driver
mysql.core.root.mapper=mappers
mysql.core.aliases.package=com.heima.model.**
mysql.core.tx.scan.package=execution(* com.heima..service.*.*(..))
二、文章的更新功能实现
情况分有
1.是否登录
2.上拉、下拉
3.加载更多,加载更新
文章信息表,存储已发布的文章
字段名称 | 类型 | 说明 |
---|---|---|
id | int(11) | 主键 |
title | varchar(50) | 标题 |
author_id | int(11) | 文章作者的ID |
author_name | varchar(20) | 作者昵称 |
channel_id | int(10) | 文章所属频道ID |
channel_name | varchar(10) | 频道名称 |
layout | tinyint(1) | 文章布局 0 无图文章 1 单图文章 2 多图文章 |
flag | tinyint(3) | 文章标记 0 普通文章 1 热点文章 2 置顶文章 3 精品文章 4 大V 文章 |
images | varchar(1000) | 文章图片 多张逗号分隔 |
labels | varchar(500) | 文章标签最多3个 逗号分隔 |
likes | int(5) | 点赞数量 |
collection | int(5) | 收藏数量 |
comment | int(5) | 评论数量 |
views | int(5) | 阅读数量 |
province_id | int(11) | 省市 |
city_id | int(11) | 市区 |
county_id | int(11) | 区县 |
created_time | datetime | 创建时间 |
publish_time | datetime | 发布时间 |
sync_status | tinyint(1) | 同步状态 |
origin | tinyint(1) | 来源 |
ap_user_article_list APP用户文章列表
字段名称 | 类型 | 说明 |
---|---|---|
id | int(11) | 主键 |
user_id | int(11) | 用户ID |
channel_id | int(11) | 频道ID |
article_id | int(11) | 文章ID |
is_show | tinyint(1) | 是否展示 |
recommend_time | datetime | 推荐时间 |
is_read | tinyint(1) | 是否阅读 |
strategy_id | int(5) | 推荐算法 |
ap_show_behaviorAPP文章展现行为表
字段 | 类型 | 描述 |
---|---|---|
id | int(11) | 主键 |
entry_id | int(11) | 实体ID |
article_id | int(11) | 文章ID |
is_click | tinyint(1) | 是否点击 |
show_time | datetime | 文章加载时间 |
created_time | datetime | 登录时间 |
ap_behavior_entry app行为实体表
APP行为实体表,一个行为实体可能是用户或者设备,或者其它
字段 | 类型 | 描述 |
---|---|---|
id | int(11) | 主键 |
type | tinyint(1) | 实体类型 0终端设备 1用户 |
entry_id | int(11) | 实体ID |
created_time | datetime | 创建时间 |
burst | varchar(40) | 分片 |
由需求分析可知用户在首页的是可能触发的行为有加载文章列表,刷新(上拉刷新、下拉刷新)等动作,这也就是我们后端需要对应的几个数据接口,其中还包含一个隐含的用户行为接口用户记录用户是否阅读某一篇文章的行为接口,则我们可以分析出后端需要的接口有:
load接口,分两种情况,一个是登录,一个是未登录,加载多条数据(有条数的限制,size),用户可以选择频道进行数据的切换
登录,从后台获取用户信息,作为条件查询
未登录,直接加载默认数据即可。
当用户进行刷新是,在我们的系统中定义了两种操作,第一种是上拉刷新也就是load_more,第二种是下拉刷新load_new接口,这两个接口的区别在于加载的内容的时间不同。
用户进入系统的时间TimeA浏览了一会首次加载的数据之后到了TimeB时间,这时候如果用户继续上拉看后面的内容则我们调用load_more接口并把TimeB时间传递到后端,后端根据TimeB时间查找当前时间之前发布的内容;
用户下拉刷新则说明用户需要当前最新的内容则调用load_new接口,将TimeA时间传到后端后端查找TimeA时间之后发布的内容。其请求参数接口设计和load接口相同。
记录用户操作行为
一系列相关功能对应一个子项目
在原有的工程中创建一个普通的maven工程模块,选择其作为heima-leadnews作为父工程,并给当前模块命名为heima-leadnews-article 配置我们需要的jar的maven坐标,以及我们项目中模块的项目依赖 注意我们的数据库相关的实体以及Mapper接口和配置文件都是存放在Model 模块。
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>heima-leadnewsartifactId>
<groupId>com.heimagroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>heima-leadnews-articleartifactId>
<dependencies>
<dependency>
<groupId>com.heimagroupId>
<artifactId>heima-leadnews-modelartifactId>
dependency>
<dependency>
<groupId>com.heimagroupId>
<artifactId>heima-leadnews-commonartifactId>
dependency>
<dependency>
<groupId>com.heimagroupId>
<artifactId>heima-leadnews-apisartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-loggingartifactId>
exclusion>
<exclusion>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
exclusion>
<exclusion>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-accessartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.jsoupgroupId>
<artifactId>jsoupartifactId>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformatgroupId>
<artifactId>jackson-dataformat-cborartifactId>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformatgroupId>
<artifactId>jackson-dataformat-xmlartifactId>
dependency>
dependencies>
project>
注意这里可以只创建基础的包结构即可,后续会讲解每个具体的类的作用以及实现;此处可能你又疑问为什么我们的控制器加了个v1,这里我们的做法是为了兼容不同的终端版本所设立的版本号
项目根路径添加文件:maven_dev.properties
# log4j
log.level=DEBUG
log.pattern=%d{DEFAULT}^|%sn^|%level^|%t^|%c^|%M^|%msg%n
项目根路径添加文件:maven_prod.properties
# log4j
log.level=DEBUG
log.pattern=%d{DEFAULT}^|%sn^|%level^|%t^|%c^|%M^|%msg%n
项目根路径添加文件:maven_test.properties
#log4j
log.level=DEBUG
log.pattern=%d{DEFAULT}^|%sn^|%level^|%t^|%c^|%M^|%msg%n
在resource目录下创建application.properties和log4j2.xml
application.properties
server.port=${port.article}
spring.application.name=${sn.article}
log4j2.xml
<configuration>
<properties>
<property name="CONSOLE_PATTERN">${log.pattern}property>
<property name="FILE_NAME">${project.build.finalName}property>
properties>
<appenders>
<console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="${CONSOLE_PATTERN}"/>
console>
<RollingFile name="RollingFileInfo" fileName="${sys:user.home}/logs/${FILE_NAME}.log"
filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/${FILE_NAME}-%d{yyyy-MM-dd}-%i.log">
<PatternLayout pattern="${CONSOLE_PATTERN}"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="100 MB"/>
Policies>
RollingFile>
appenders>
<loggers>
<logger name="org.springframework" level="INFO">logger>
<logger name="org.mybatis" level="INFO">logger>
<logger name="org.apache.http" level="INFO">logger>
<logger name="org.apache.kafka" level="INFO">logger>
<logger name="com.netflix.discovery" level="INFO">logger>
<logger name="org.hibernate" level="INFO">logger>
<root level="${log.level}">
<appender-ref ref="Console"/>
<appender-ref ref="RollingFileInfo"/>
root>
loggers>
configuration>
mysql初始化扫描配置类com.heima.article.config.MysqlConfig
@Configuration
@ComponentScan("com.heima.common.mysql.core")
public class MysqlConfig {
}
(0)定义dto 数据传输对象
@Data
public class ArticleHomeDto {
// 省市
Integer provinceId;
// 市区
Integer cityId;
// 区县
Integer countyId;
// 最大时间
Date maxBehotTime;
// 最小时间
Date minBehotTime;
// 分页size
Integer size;
// 数据范围,比如频道ID
String tag;
}
(1)接口定义,在apis模块中我们建立包com.heima.article.apis并定义接口ArticleHomeControllerApi
/**
* 首頁文章
*/
public interface ArticleHomeControllerApi {
/**
* 加載首頁文章
* @param dto 封装参数对象
* @return 文章列表数据
*/
ResponseResult load(ArticleHomeDto dto);
/**
* 加载更多
* @param dto 封装参数对象
* @return 文章列表数据
*/
ResponseResult loadMore(ArticleHomeDto dto);
/**
* 加载最新的数据
* @param dto 封装参数对象
* @return 文章列表
*/
ResponseResult loadNew(ArticleHomeDto dto);
}
(2)在定义完数据接口之后,我们需要做的就是去article模块定义我们的控制器, 如果你是拷贝的下面代码你可能发现你的代码中ArticleIndexService没有定义,报错了这里不用慌我们后面就是service层的编写,后面service到dao层也是同样的
@RestController
@RequestMapping("/api/v1/article")
public class ArticleHomeController implements ArticleHomeControllerApi {
@Autowired
private AppArticleService appArticleService;
@Override
@GetMapping("/load")
public ResponseResult load(ArticleHomeDto dto) {
return appArticleService.load( ArticleConstans.LOADTYPE_LOAD_MORE, dto);
}
@Override
@GetMapping("/loadmore")
public ResponseResult loadMore(ArticleHomeDto dto) {
return appArticleService.load( ArticleConstans.LOADTYPE_LOAD_MORE, dto);
}
@Override
@GetMapping("/loadnew")
public ResponseResult loadNew(ArticleHomeDto dto) {
return appArticleService.load( ArticleConstans.LOADTYPE_LOAD_NEW, dto);
}
}
定义常量com.heima.common.article.constans.ArticleConstans
public class ArticleConstans{
public static final Short LOADTYPE_LOAD_MORE = 1;//加载更多
public static final Short LOADTYPE_LOAD_NEW = 2;//加载更新
public static final String DEFAULT_TAG = "__all__"//所有频道
}
(3)文章service接口定义
public interface AppArticleService {
/**
*
* @param type 1 加载更多 2 加载更新
* @param dto 封装数据
* @return 数据列表
*/
public ResponseResult load(Short type, ArticleHomeDto dto);
}
(4)AppArticleServiceImpl实现类
@Service
public class AppArticleServiceImpl implements AppArticleService {
// 单页最大加载的数字
private final static short MAX_PAGE_SIZE = 50;
@Autowired
private ApArticleMapper apArticleMapper; //默认加载
@Autowired
private ApUserArticleListMapper apUserArticleListMapper; //用户登录下的加载
/**
*
* @param time 时间节点
* @param type 1 加载更多 2 加载更新
* @param size 每次返回数据量
* @return 数据列表
*/
public ResponseResult load(Short type, ArticleHomeDto dto) {
ApUser user = AppThreadLocalUtils.getUser();
Integer size = dto.getSize();
String tag = dto.getTag();
// 分页参数校验
if (size == null || size <= 0) {
size = 20;
}
size = Math.min(size,MAX_PAGE_SIZE);
dto.setSize(size);
// 类型参数校验
if (!type.equals(ArticleConstans.LOADTYPE_LOAD_MORE) && !type.equals(ArticleConstans.LOADTYPE_LOAD_NEW))
type = ArticleConstans.LOADTYPE_LOAD_MORE;
// 文章频道参数验证
if (StringUtils.isEmpty(tag)) {
dto.setTag(ArticleConstans.DEFAULT_TAG);
}
// 最大时间处理
if(dto.getMaxBehotTime()==null){
dto.setMaxBehotTime(new Date());
}
// 最小时间处理
if(dto.getMinBehotTime()==null){
dto.setMinBehotTime(new Date());
}
// 数据加载
if(user!=null){
return ResponseResult.okResult(getUserArticle(user,dto,type));
}else{
return ResponseResult.okResult(getDefaultArticle(dto,type));
}
}
/**
* 先从用户的推荐表中查找文章,如果没有再从大文章列表中获取
* @param user
* @param dto
* @param type
* @return
*/
private List<ApArticle> getUserArticle(ApUser user,ArticleHomeDto dto,Short type){
List<ApUserArticleList> list = apUserArticleListMapper.loadArticleIdListByUser(user,dto,type);
if(!list.isEmpty()){
List<ApArticle> temp = apArticleMapper.loadArticleListByIdList(list);
return temp;
}else{
return getDefaultArticle(dto,type);
}
}
/**
* 从默认的大文章列表中获取文章
* @param dto
* @param type
* @return
*/
private List<ApArticle> getDefaultArticle(ArticleHomeDto dto,Short type){
return apArticleMapper.loadArticleListByLocation(dto,type);
}
}
(5)ApArticleMapper
public interface ApArticleMapper {
/**
* 照用户地理位置,加载文章
* @param dto 参数封装对象
* @param type 加载方向
* @return
*/
List<ApArticle> loadArticleListByLocation(@Param("dto") ArticleHomeDto dto, @Param("type") short type);
/**
* 依据文章IDS来获取文章详细内容
* @param list 文章ID
* @return
*/
List<ApArticle> loadArticleListByIdList(@Param("list") List<ApUserArticleList> list);
}
(6)ApArticleMapper 对应xml
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.heima.model.mappers.app.ApArticleMapper">
<resultMap id="resultMap" type="com.heima.model.article.pojos.ApArticle">
<id column="id" property="id"/>
<result column="title" property="title"/>
<result column="author_id" property="authorId"/>
<result column="author_name" property="authorName"/>
<result column="channel_id" property="channelId"/>
<result column="channel_name" property="channelName"/>
<result column="layout" property="layout"/>
<result column="flag" property="flag"/>
<result column="images" property="images"/>
<result column="labels" property="labels"/>
<result column="likes" property="likes"/>
<result column="collection" property="collection"/>
<result column="comment" property="comment"/>
<result column="views" property="views"/>
<result column="province_id" property="provinceId"/>
<result column="city_id" property="cityId"/>
<result column="county_id" property="countyId"/>
<result column="created_time" property="createdTime"/>
<result column="publish_time" property="publishTime"/>
<result column="sync_status" property="syncStatus"/>
resultMap>
<sql id="Base_Column_List">
id, title, author_id, author_name, channel_id, channel_name, layout, flag, images,
labels, likes, collection, comment, views, province_id, city_id, county_id, created_time,
publish_time,sync_status
sql>
<select id="loadArticleListByLocation" resultMap="resultMap">
select * from ap_article a
<where>
<if test="dto.provinceId!=null">
and a.province_id=#{dto.provinceId}
if>
<if test="dto.cityId!=null">
and a.city_id=#{dto.cityId}
if>
<if test="dto.countyId!=null">
and a.county_id=#{dto.countyId}
if>
<if test="type != null and type == 1">
and a.publish_time #{dto.minBehotTime}
if>
<if test="type != null and type == 2">
and a.publish_time ]]> #{dto.maxBehotTime}
if>
<if test="dto.tag != '__all__'">
and a.channel_id = #{dto.tag}
if>
where>
limit #{dto.size}
select>
<select id="loadArticleListByIdList" resultMap="resultMap">
select * from ap_article where id in(
<trim prefix="" suffixOverrides=",">
<foreach item="item" collection="list" separator=",">
#{item.articleId},
foreach>
trim>
)
select>
mapper>
(7)ApUserArticleListMapper
package com.heima.article.mysql.core.model.mappers.app;
import com.heima.article.mysql.core.model.dtos.ArticleHomeDto;
import com.heima.article.mysql.core.model.pojos.app.ApUser;
import com.heima.article.mysql.core.model.pojos.app.ApUserArticleList;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ApUserArticleListMapper {
/**
* 按照用户属性阅读习惯,加载文章id
* @param user 当前登录的用户
* @param dto 参数封装对象
* @param type 加载方向
* @return
*/
List<ApUserArticleList> loadArticleIdListByUser(@Param("user") ApUser user, @Param("dto") ArticleHomeDto dto, @Param("type") short type);
}
(8)ApUserArticleListMapper 对应xml
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.heima.article.mysql.core.model.mappers.app.ApUserArticleListMapper">
<resultMap id="BaseResultMap" type="com.heima.model.user.pojos.ApUserArticleList">
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="channel_id" property="channelId"/>
<result column="article_id" property="articleId"/>
<result column="is_show" property="isShow" javaType="java.lang.Boolean" jdbcType="BIT" />
<result column="recommend_time" property="recommendTime" javaType="java.util.Date" jdbcType="TIMESTAMP" />
<result column="is_read" property="isRead" javaType="java.lang.Boolean" jdbcType="BIT" />
<result column="strategy_id" property="strategyId"/>
resultMap>
<sql id="Base_Column_List">
id, user_id, channel_id, article_id, is_show, recommend_time, is_read, strategy_id
sql>
<select id="loadArticleIdListByUser" parameterType="map" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from ap_user_article_list
<where>
user_id=#{user.id} and is_show=0 and is_read=0
<if test="type != null and type == 1">
and recommend_time #{dto.minBehotTime}
if>
<if test="type != null and type == 2">
and recommend_time ]]> #{dto.maxBehotTime}
if>
<if test="dto.tag != '__all__'">
and channel_id = #{dto.tag}
if>
where>
limit #{dto.size}
select>
mapper>
(9)单元测试
/**
* 测试文章列表相关接口
*/
@SpringBootTest(classes = ArticleJarApplication.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class ArticleTest {
@Autowired
private AppArticleService appArticleService;
/**
* 测试load
*/
@Test
public void testLoad() {
ApUser apUser = new ApUser();
apUser.setId(1l);
AppThreadLocalUtils.setUser(apUser);
ArticleHomeDto dto = new ArticleHomeDto();
ResponseResult data = appArticleService.load( ArticleConstans.LOADTYPE_LOAD_MORE, dto);
System.out.println(data.getData());
}
}
思路分析
导入heima-leadnews-behavior工程
dto(数据传输对象)的定义:
com.heima.model.behavior.dtos.ShowBehaviorDto
@Data
public class ShowBehaviorDto {
// 设备ID
@IdEncrypt
Integer equipmentId;
List<ApArticle> articleIds;
}
定义控制器以及控制器接口
(1)接口定义,com.heima.article.apis.BehaviorControllerApi
/**
* 行为
*/
public interface BehaviorControllerApi {
ResponseResult saveShowBehavior(ShowBehaviorDto dto);
}
(2)定义控制器:com.heima.behavior.controller.v1.BehaviorController
@RestController
@RequestMapping("/api/v1/behavior")
public class BehaviorController implements BehaviorControllerApi {
@Autowired
private AppShowBehaviorService appShowBehaviorService;
@Override
@PostMapping("/show_behavior")
public ResponseResult saveShowBehavior(@RequestBody ShowBehaviorDto dto) {
return appShowBehaviorService.saveShowBehavior(dto);
}
}
(3)行为服务层接口:com.heima.behavior.service.AppShowBehaviorService
public interface AppShowBehaviorService {
/**
* 存储行为数据
* @param dto
* @return
*/
public ResponseResult saveShowBehavior(ShowBehaviorDto dto);
}
(4)AppShowBehaviorService 实现
@Service
@SuppressWarnings("all")
public class AppShowBehaviorServiceImpl implements AppShowBehaviorService {
@Autowired
private ApShowBehaviorMapper apShowBehaviorMapper;
@Autowired
private ApBehaviorEntryMapper apBehaviorEntryMapper;
@Override
public ResponseResult saveShowBehavior(ShowBehaviorDto dto){
ApUser user = AppThreadLocalUtils.getUser();
// 用户和设备不能同时为空
if(user==null&& (dto.getArticleIds()==null||dto.getArticleIds().isEmpty())){
return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
}
Long userId = null;
if(user!=null){
userId = user.getId();
}
ApBehaviorEntry apBehaviorEntry = apBehaviorEntryMapper.selectByUserIdOrEquipment(userId, dto.getEquipmentId());
// 行为实体找以及注册了,逻辑上这里是必定有值得,除非参数错误
if(apBehaviorEntry==null){
return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
}
// 过滤新数据
Integer[] temp = new Integer[dto.getArticleIds().size()];
for (int i = 0; i < temp.length; i++) {
temp[i]=dto.getArticleIds().get(i).getId();
}
List<ApShowBehavior> list = apShowBehaviorMapper.selectListByEntryIdAndArticleIds(apBehaviorEntry.getId(), temp);
List<Integer> stringList = new ArrayList(Arrays.asList(temp));
if(!list.isEmpty()){
list.forEach(item->{
stringList.remove(item.getArticleId());
});
}
// 插入新数据
if(!stringList.isEmpty()) {
temp = new Integer[stringList.size()];
stringList.toArray(temp);
apShowBehaviorMapper.saveBehaviors(apBehaviorEntry.getId(), temp);
}
return ResponseResult.okResult(0);
}
}
(5)heima-leadnews-model中定义行为mapper接口:com.heima.article.mysql.core.model.mappers.app.ApShowBehaviorMapper
public interface ApShowBehaviorMapper {
/**
* 获取以及存在的用户数据
* @param entryId
* @param articleIds
* @return
*/
List<ApShowBehavior> selectListByEntryIdAndArticleIds(@Param("entryId") Integer entryId, @Param("articleIds") Integer[] articleIds);
/**
* 保存用户展现行为数据
* @param articleIds 文章IDS
* @param entryId 实体ID
*/
void saveBehaviors(@Param("entryId") Integer entryId, @Param("articleIds") Integer[] articleIds);
}
(6)heima-leadnews-model中定义行为mapper文件:mappers/app/ApShowBehaviorMapper.xml
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.heima.article.mysql.core.model.mappers.app.ApShowBehaviorMapper" >
<resultMap id="BaseResultMap" type="com.heima.article.mysql.core.model.pojos.app.ApShowBehavior" >
<id column="id" property="id" />
<result column="entry_id" property="entryId" />
<result column="article_id" property="articleId" />
<result column="is_click" property="isClick"/>
<result column="show_time" property="showTime" />
<result column="created_time" property="createdTime" />
resultMap>
<sql id="Base_Column_List" >
id, entry_id, article, is_click, show_time, created_time
sql>
<select id="selectListByEntryIdAndArticleIds" resultMap="BaseResultMap" >
select * from ap_show_behavior a where a.entry_id=#{entryId} and article_id in(
<foreach item="item" collection="articleIds" separator=",">
#{item}
foreach>
)
select>
<insert id="saveBehaviors">
/*!mycat:catlet=io.mycat.route.sequence.BatchInsertSequence */
insert into ap_show_behavior ( entry_id, article_id,is_click, show_time, created_time) values
<foreach item="item" collection="articleIds" separator=",">
(#{entryId}, #{item},0, now(),now())
foreach>
insert>
mapper>
(7)ApBehaviorEntryMapper
public interface ApBehaviorEntryMapper {
ApBehaviorEntry selectByUserIdOrEquipment(@Param("userId") Integer userId, @Param("equipmentId") Integer equipmentId);
}
(8)ApBehaviorEntryMapper 对应映射配置
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.heima.article.mysql.core.model.mappers.app.ApBehaviorEntryMapper" >
<resultMap id="BaseResultMap" type="com.heima.article.mysql.core.model.pojos.app.ApBehaviorEntry" >
<id column="id" property="id" />
<result column="type" property="type"/>
<result column="entry_id" property="entryId" />
<result column="created_time" property="createdTime" />
<result column="burst" property="burst"/>
resultMap>
<sql id="Base_Column_List" >
id, type, entry_id, created_time
sql>
<select id="selectByUserIdOrEquipment" resultMap="BaseResultMap" >
select * from ap_behavior_entry a
<where>
<if test="userId!=null">
and a.entry_id=#{userId} and type=1
if>
<if test="userId==null and equipmentId!=null">
and a.entry_id=#{equipmentId} and type=0
if>
where>
limit 1
select>
mapper>
行为接口测试
/**
* 测试文章列表相关接口
*/
@SpringBootTest(classes = BehaviorJarApplication.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class BehaviorTest {
@Autowired
private AppShowBehaviorService showBehaviorService;
@Test
public void testSaveBehavior() {
ApUser apUser = new ApUser();
apUser.setId(1l);
AppThreadLocalUtils.setUser(apUser);
ShowBehaviorDto dto = new ShowBehaviorDto();
List<ApArticle> articles = new ArrayList<>();
ApArticle apArticle = new ApArticle();
apArticle.setId(1);
articles.add(apArticle);
showBehaviorService.saveShowBehavior(dto);
//articleIndexService.saveBehaviors(data);
}
}
你可能遇到的问题:
问题1:导入工程出现—project出现红线
1.修改configure为none.
2.修改依赖版本号
问题2:数据库连接不上
1.mysql5.7情况下url使用如下:mysql.core.jdbc.url=jdbc:mysql://localhost:3306/hiema_headnews?useUnicode=true&characterEncoding=utf8&useSSL=false
总结功能开发的步骤:
1.在api模块定义好功能接口
2.配置类配置好mysql配置
3.在具体的功能模块下的controller来控制
4.在具体功能模块的service层实现接口和实现类
4.在持久层统一写在了common模块的mapper包和resource下mapper.xml
相关知识点复习:
mybatis
1.trim使用
2.javaType和jdbcType
3.Mybatis的参数传递
MyBatis的传入参数parameterType类型分两种
基本数据类型:int、string、long、Date; 复杂数据类型:类(JavaBean、Integer等)和Map
如何获取参数中的值:
2.1 基本数据类型:#{参数} 获取参数中的值
2.2 复杂数据类型:#{属性名} ,map中则是#{key}
4.sql标签的使用
最后附上资源:
1.基础工程搭建说明书以及代码
2.数据设计PowerDesigner的使用、设计规范以及sql文件导入资源.