mybatis plus是我国程序员对mybatis框架的一个近国人开发习惯的改版,使用起来风格特殊,大部分的CRUD接口都需要自己尝试测试之后才能熟悉它的作用,使用起来的好处也很明显,相比于mybatis框架而言,它的dao层与service层对CRUD代码量进行了进一步的减少,使得我们在开发过程中只需要对controller层与特殊的核心业务进行大精力开发,常规业务它都给我们内置了。
关于这个框架的常规CRUD接口,大家可以查看官方文档,讲解的比较详细:CRUD 接口 | MyBatis-PlusMyBatis-Plus 官方文档https://www.baomidou.com/pages/49cc81/#service-crud-%E6%8E%A5%E5%8F%A3
我通过一个登录的controller层代码对mybatis plus进行经验分享:
下面先看代码:
@Slf4j
@RestController
@RequestMapping("/User")
public class UserController {
@Autowired
private UserService userService;
@Autowired
private RequestUtils requestUtils;
@Autowired
private AppinfoService appinfoService;
/**
* 用户采用微信登录
*
* @return 登录成功返回响应对象
*/
@GetMapping("/wechatLogin")
@ResponseBody
public R<Object> WeChatLogin(@RequestParam("code") String code, @RequestParam("userName") String userName) {
try {
/*1.优化点:如果可以让第一次登录的openId存储在小程序本地内存中,只需在前端判断是否这个openId为空,
如果不为空,直接传到后端查数据库,避免了每一次都要访问一次微信登录网址的时间消耗。*/
QueryChainWrapper<Appinfo> query = appinfoService.query();
Appinfo appinfo = query.list().get(0);
String weChatOPenId = requestUtils.getWeChatOPenId(appinfo.getAppId(), appinfo.getAppSecrtet(), code);
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.ge("WeChatId", weChatOPenId);
int count = (int) userService.count(wrapper);
if (count == 1) {
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("WeChatId", weChatOPenId);
User user = new User();
user.setUserName(userName);
boolean update = userService.update(user, updateWrapper);
if (update) return R.success("登陆成功!");
else throw new Exception("更新用户名异常!");
} else {
User user = new User();
user.setWeChatId(weChatOPenId);
user.setUserName(userName);
user.setUserAccount(weChatOPenId);
boolean save = userService.save(user);
if (save) return R.success("登录成功!");
else throw new Exception("新用户openId插入数据库异常!");
}
} catch (Exception e) {
e.printStackTrace();
return R.error("微信登录异常!");
}
}
}
涉及到微信小程序中后台获取openId的方法,我把代码先放这里:
@Component
public class RequestUtils {
/**
* 获取微信小程序openid
*
* @param AppId 小程序Id
* @param AppSecret 小程序密钥
* @param code 小程序wx.login接口返回的临时的临时状态码
* @return openid
*/
public String getWeChatOPenId(String AppId, String AppSecret, String code) {
try {
/*1.拼接微信登录获取openId所需要访问的URL*/
StringBuilder URL = new StringBuilder(WeChatConfig.loginUrl).append("?appId=").append(AppId).
append("&secret=").append(AppSecret).append("&js_code=").append(code).
append("&grant_type=authorization_code");
/*2.创建http连接的客户端对象*/
BasicHttpClientConnectionManager connectionManager;
connectionManager = new BasicHttpClientConnectionManager(
RegistryBuilder.<ConnectionSocketFactory>create().
register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", SSLConnectionSocketFactory.getSocketFactory())
.build(), null, null, null
);
CloseableHttpClient httpClient = HttpClientBuilder.create().
setConnectionManager(connectionManager)
.build();
HttpGet httpGet = new HttpGet(URL.toString());
/*3.执行访问一次请求获取响应体内容*/
HttpResponse httpResponse = httpClient.execute(httpGet);
HttpEntity entity = httpResponse.getEntity();
String s = EntityUtils.toString(entity, "UTF-8");
JSONObject jsonObject = new JSONObject(s);
/*4.从响应体中拿到openId并返回*/
return jsonObject.get("openid").toString();
} catch (Exception e) {
System.out.println("---------小程序获取微信登录openId异常--------");
e.printStackTrace();
}
/*出现异常即返回null*/
return null;
}
}
下面我们来逐步看登录的代码:
1、获取小程序关键信息调用getWeChatOpenId方法获取openId。
QueryChainWrapper<Appinfo> query = appinfoService.query();
Appinfo appinfo = query.list().get(0);
String weChatOPenId = requestUtils.getWeChatOPenId(appinfo.getAppId(),appinfo.getAppSecrtet(), code);
这里使用到了service层从IService<T>继承来的方法query();
我们看下query的源码:
default QueryChainWrapper<T> query() {
return ChainWrappers.queryChain(this.getBaseMapper());
}
它的返回值类型是QueryChainWrapper<T>,我们去看看这个类型的源码:
public class QueryChainWrapper<T>
extends AbstractChainWrapper<T, String, QueryChainWrapper<T>, QueryWrapper<T>>
implements ChainQuery<T>, Query<QueryChainWrapper<T>, T, String>
它继承了AbstractChainWrapper父类,实现了ChainQuery接口,我们来看看这个接口的方法:
public interface ChainQuery<T> extends ChainWrapper<T> {
default List<T> list() {
return this.getBaseMapper().selectList(this.getWrapper());
}
default T one() {
return this.getBaseMapper().selectOne(this.getWrapper());
}
default Optional<T> oneOpt() {
return Optional.ofNullable(this.one());
}
default Long count() {
return SqlHelper.retCount(this.getBaseMapper().selectCount(this.getWrapper()));
}
default boolean exists() {
return this.count() > 0L;
}
default <E extends IPage<T>> E page(E page) {
return this.getBaseMapper().selectPage(page, this.getWrapper());
}
}
这里不难发现,我们的query.list();方法就是从这里得来的,显然就是从BaseMapper这个通用的类里得来的方法,这么看其实这些CRUD接口方法不过是从底层的BaseMapper拓展出来的。
这里是IService<T>可调用关于查询的方法
// 根据 ID 查询
T getById(Serializable id);
// 根据 Wrapper,查询一条记录。结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1")
T getOne(Wrapper<T> queryWrapper);
// 根据 Wrapper,查询一条记录
T getOne(Wrapper<T> queryWrapper, boolean throwEx);
// 根据 Wrapper,查询一条记录
Map<String, Object> getMap(Wrapper<T> queryWrapper);
// 根据 Wrapper,查询一条记录
<V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);
// 查询所有
List<T> list();
// 查询列表
List<T> list(Wrapper<T> queryWrapper);
// 查询(根据ID 批量查询)
Collection<T> listByIds(Collection<? extends Serializable> idList);
// 查询(根据 columnMap 条件)
Collection<T> listByMap(Map<String, Object> columnMap);
// 查询所有列表
List<Map<String, Object>> listMaps();
// 查询列表
List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper);
// 查询全部记录
List<Object> listObjs();
// 查询全部记录
<V> List<V> listObjs(Function<? super Object, V> mapper);
// 根据 Wrapper 条件,查询全部记录
List<Object> listObjs(Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录
<V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);
// 无条件分页查询
IPage<T> page(IPage<T> page);
// 条件分页查询
IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper);
// 无条件分页查询
IPage<Map<String, Object>> pageMaps(IPage<T> page);
// 条件分页查询
IPage<Map<String, Object>> pageMaps(IPage<T> page, Wrapper<T> queryWrapper);
// 查询总记录数
int count();
// 根据 Wrapper 条件,查询总记录数
int count(Wrapper<T> queryWrapper);
2、查询数据库是否存在主键WeChatId等于该登录用户openId的记录。
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.ge("WeChatId", weChatOPenId);
int count = (int) userService.count(wrapper);
这里涉及到我们自行创建的实例wrapper,也即是常说的条件构造器。看一下它的源码:
public class QueryWrapper<T>
extends AbstractWrapper<T, String, QueryWrapper<T>>
implements Query<QueryWrapper<T>, T, String>
继承了统一类型,看一下AbstractWrapper的源码:
public <V> Children allEq(boolean condition, Map<R, V> params, boolean null2IsNull) {
if (condition && CollectionUtils.isNotEmpty(params)) {
params.forEach((k, v) -> {
if (StringUtils.checkValNotNull(v)) {
this.eq(k, v);
} else if (null2IsNull) {
this.isNull(k);
}
});
}
return this.typedThis;
}
public <V> Children allEq(boolean condition, BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull) {
if (condition && CollectionUtils.isNotEmpty(params)) {
params.forEach((k, v) -> {
if (filter.test(k, v)) {
if (StringUtils.checkValNotNull(v)) {
this.eq(k, v);
} else if (null2IsNull) {
this.isNull(k);
}
}
});
}
return this.typedThis;
}
public Children eq(boolean condition, R column, Object val) {
return this.addCondition(condition, column, SqlKeyword.EQ, val);
}
public Children ne(boolean condition, R column, Object val) {
return this.addCondition(condition, column, SqlKeyword.NE, val);
}
public Children gt(boolean condition, R column, Object val) {
return this.addCondition(condition, column, SqlKeyword.GT, val);
}
public Children ge(boolean condition, R column, Object val) {
return this.addCondition(condition, column, SqlKeyword.GE, val);
}
public Children lt(boolean condition, R column, Object val) {
return this.addCondition(condition, column, SqlKeyword.LT, val);
}
public Children le(boolean condition, R column, Object val) {
return this.addCondition(condition, column, SqlKeyword.LE, val);
}
public Children like(boolean condition, R column, Object val) {
return this.likeValue(condition, SqlKeyword.LIKE, column, val, SqlLike.DEFAULT);
}
public Children notLike(boolean condition, R column, Object val) {
return this.likeValue(condition, SqlKeyword.NOT_LIKE, column, val, SqlLike.DEFAULT);
}
public Children likeLeft(boolean condition, R column, Object val) {
return this.likeValue(condition, SqlKeyword.LIKE, column, val, SqlLike.LEFT);
}
public Children likeRight(boolean condition, R column, Object val) {
return this.likeValue(condition, SqlKeyword.LIKE, column, val, SqlLike.RIGHT);
}
public Children between(boolean condition, R column, Object val1, Object val2) {
return this.maybeDo(condition, () -> {
this.appendSqlSegments(this.columnToSqlSegment(column), SqlKeyword.BETWEEN, () -> {
return this.formatParam((String)null, val1);
}, SqlKeyword.AND, () -> {
return this.formatParam((String)null, val2);
});
});
}
public Children notBetween(boolean condition, R column, Object val1, Object val2) {
return this.maybeDo(condition, () -> {
this.appendSqlSegments(this.columnToSqlSegment(column), SqlKeyword.NOT_BETWEEN, () -> {
return this.formatParam((String)null, val1);
}, SqlKeyword.AND, () -> {
return this.formatParam((String)null, val2);
});
});
}
public Children and(boolean condition, Consumer<Children> consumer) {
return this.and(condition).addNestedCondition(condition, consumer);
}
public Children or(boolean condition, Consumer<Children> consumer) {
return this.or(condition).addNestedCondition(condition, consumer);
}
public Children nested(boolean condition, Consumer<Children> consumer) {
return this.addNestedCondition(condition, consumer);
}
public Children not(boolean condition, Consumer<Children> consumer) {
return this.not(condition).addNestedCondition(condition, consumer);
}
public Children or(boolean condition) {
return this.maybeDo(condition, () -> {
this.appendSqlSegments(SqlKeyword.OR);
});
}
public Children apply(boolean condition, String applySql, Object... values) {
return this.maybeDo(condition, () -> {
this.appendSqlSegments(WrapperKeyword.APPLY, () -> {
return this.formatSqlMaybeWithParam(applySql, (String)null, values);
});
});
}
public Children last(boolean condition, String lastSql) {
if (condition) {
this.lastSql.setStringValue(" " + lastSql);
}
return this.typedThis;
}
public Children comment(boolean condition, String comment) {
if (condition) {
this.sqlComment.setStringValue(comment);
}
return this.typedThis;
}
public Children first(boolean condition, String firstSql) {
if (condition) {
this.sqlFirst.setStringValue(firstSql);
}
return this.typedThis;
}
public Children exists(boolean condition, String existsSql, Object... values) {
return this.maybeDo(condition, () -> {
this.appendSqlSegments(SqlKeyword.EXISTS, () -> {
return String.format("(%s)", this.formatSqlMaybeWithParam(existsSql, (String)null, values));
});
});
}
public Children notExists(boolean condition, String existsSql, Object... values) {
return this.not(condition).exists(condition, existsSql, values);
}
public Children isNull(boolean condition, R column) {
return this.maybeDo(condition, () -> {
this.appendSqlSegments(this.columnToSqlSegment(column), SqlKeyword.IS_NULL);
});
}
public Children isNotNull(boolean condition, R column) {
return this.maybeDo(condition, () -> {
this.appendSqlSegments(this.columnToSqlSegment(column), SqlKeyword.IS_NOT_NULL);
});
}
public Children in(boolean condition, R column, Collection<?> coll) {
return this.maybeDo(condition, () -> {
this.appendSqlSegments(this.columnToSqlSegment(column), SqlKeyword.IN, this.inExpression(coll));
});
}
public Children in(boolean condition, R column, Object... values) {
return this.maybeDo(condition, () -> {
this.appendSqlSegments(this.columnToSqlSegment(column), SqlKeyword.IN, this.inExpression(values));
});
}
public Children notIn(boolean condition, R column, Collection<?> coll) {
return this.maybeDo(condition, () -> {
this.appendSqlSegments(this.columnToSqlSegment(column), SqlKeyword.NOT_IN, this.inExpression(coll));
});
}
public Children notIn(boolean condition, R column, Object... values) {
return this.maybeDo(condition, () -> {
this.appendSqlSegments(this.columnToSqlSegment(column), SqlKeyword.NOT_IN, this.inExpression(values));
});
}
不难发现里面有很多与linux的脚本程序的逻辑代码eq之类的两个字母代表逻辑关系。这些都是数据库常用的比较方法。
3、如果用户登录过,需要实时更新用户的微信昵称。
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("WeChatId", weChatOPenId);
User user = new User();
user.setUserName(userName);
boolean update = userService.update(user, updateWrapper);
if (update) return R.success("登陆成功!");
else throw new Exception("更新用户名异常!");
这里需要注意的是wrapper条件构造器给的是比较,也就是sql语句中where的作用,实际上的user.setUserName(userName);决定的是set的作用(这里说的是update 表名 set 属性 = value where 属性 = value 中的关键字)。
4、如果用户没有登录过,就可以将唯一标识与相关信息插入数据库
User user = new User();
user.setWeChatId(weChatOPenId);
user.setUserName(userName);
user.setUserAccount(weChatOPenId);
boolean save = userService.save(user);
if (save) return R.success("登录成功!");
else throw new Exception("新用户openId插入数据库异常!");
这个就很简单的逻辑了,大家自行查看mybatisplus的CRUD接口描述也可以弄清楚的。
至于微信登录后台发请求获取openId的方法,大家可以看我写的这两篇文章:
http://t.csdn.cn/vklXHhttp://t.csdn.cn/vklXHhttp://t.csdn.cn/SA2xQhttp://t.csdn.cn/SA2xQ