一.本文介绍
首先读这篇文章之前如果没有接触过Spring Boot可以看一下,并且读这篇文章还需要你至少能写基本的sql语句。我在写这篇文章之前也想过到底是选择JPA还是Mybaties作为持久层框架(持久层框架我理解就是替你去数据库执行操作并把查询结果处理好了再返给你),JPA底层封装了Hibernate,所以JPA和Mybaties的比较实际就是Hibernate和Mybaties的比较,让我最后选择Mybaties的理由就是Hibernate主要是个全自动的持久层框架而Mybaties是半自动的持久层框架,如果用Hibernate可能不需要自己写sql因为它的底层都给你封装好了,而Mybaties需要自己写好sql框架会替你执行并把查询结果按你的要求处理好返回给你,这样来看实际Mybaties具有更好的灵活性。
二.Spring Boot整合Mybaties
首先建一个数据库例子脚本如下
CREATE TABLE `user_tag_info` ( `id` int(11) NOT NULL AUTO_INCREMENT, `tag` int(11) NOT NULL DEFAULT '0' COMMENT '计时标签', `wx_id` varchar(11) NOT NULL DEFAULT '' COMMENT '微信唯一标识', `start_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP COMMENT '计时开始时间', `end_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP COMMENT '计时结束时间', `status` int(11) NOT NULL COMMENT '0:计时未结束 1: 计时结束', `current_data` date NOT NULL COMMENT '记录时间', PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;
pom.xml增添如下依赖
org.mybatis.spring.boot mybatis-spring-boot-starter 1.3.1 mysql mysql-connector-java com.alibaba druid 1.1.0
在resources目录下新建application.properties文件,内容如下:
#指定数据库驱动spring.datasource.driver-class-name=com.mysql.jdbc.Driver#指定数据库url(其中localhost可以换成指定ip)spring.datasource.url=jdbc:mysql://localhost:3306/demo#数据库账号密码spring.datasource.username=rootspring.datasource.password=root
建立如下图的项目结构
根据表建立对应实体并放入entity包中如下
package cn.test.entity;import java.util.Date;public class UserTagInfo{ private Integer id; /** * 计时标签 */ private Integer tag; /** * 微信唯一标识 */ private String wxId; /** * 计时开始时间 */ private Date startTime; /** * 计时结束时间 */ private Date endTime; /** * 0:计时未结束 1: 计时结束 */ private Integer status; /** * 记录时间 */ private Date currentData; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Integer getTag() { return tag; } public void setTag(Integer tag) { this.tag = tag; } public String getWxId() { return wxId; } public void setWxId(String wxId) { this.wxId = wxId == null ? null : wxId.trim(); } public Date getStartTime() { return startTime; } public void setStartTime(Date startTime) { this.startTime = startTime; } public Date getEndTime() { return endTime; } public void setEndTime(Date endTime) { this.endTime = endTime; } public Integer getStatus() { return status; } public void setStatus(Integer status) { this.status = status; } public Date getCurrentData() { return currentData; } public void setCurrentData(Date currentData) { this.currentData = currentData; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", tag=").append(tag); sb.append(", wxId=").append(wxId); sb.append(", startTime=").append(startTime); sb.append(", endTime=").append(endTime); sb.append(", status=").append(status); sb.append(", currentData=").append(currentData); sb.append("]"); return sb.toString(); }}
在cn.test.mapper下新建一个接口如下
package cn.test.mapper;import cn.test.entity.UserTagInfo;import org.springframework.stereotype.Component;@Componentpublic interface UserTagInfoMapper { int deleteByPrimaryKey(Integer id); int insert(UserTagInfo record); UserTagInfo selectByPrimaryKey(Integer id); int updateByPrimaryKey(UserTagInfo record);}
在resources下的mapper文件夹里新建xml文件如下
delete from user_tag_info where id = #{id,jdbcType=INTEGER} insert into user_tag_info (id, tag, wx_id, start_time, end_time, status, current_data) values (#{id,jdbcType=INTEGER}, #{tag,jdbcType=INTEGER}, #{wxId,jdbcType=VARCHAR}, #{startTime,jdbcType=TIMESTAMP}, #{endTime,jdbcType=TIMESTAMP}, #{status,jdbcType=INTEGER}, #{currentData,jdbcType=DATE}) update user_tag_info set tag = #{tag,jdbcType=INTEGER}, wx_id = #{wxId,jdbcType=VARCHAR}, start_time = #{startTime,jdbcType=TIMESTAMP}, end_time = #{endTime,jdbcType=TIMESTAMP}, status = #{status,jdbcType=INTEGER}, current_data = #{currentData,jdbcType=DATE} where id = #{id,jdbcType=INTEGER}
这里有4点要说一下
1.首先@Component注解的作用是:Mybaties会为加了这个注解的接口生成一个代理对象(接口本身不能new出对象来)执行你的sql并按你的定义返回查询结果,这就要求每个接口最好有一个同名的xml配置文件,也可以看到xml文件里的<mapper namespace="cn.test.mapper.UserTagInfoMapper" >也是这个作用
2.简单说一下xml里的标签的含义
resultMap代表对返回结果的封装,id属性必须唯一,type对应具体的实体,它的子标签id代表数据库中的主键,result代表普通的属性,column代表数据库的列名,property代表实体中的属性名;select、delete、insert、update分别对应查询、删除、插入、修改的sql,这里面的id对用的是接口中的方法名,parameterType代表入参类型
3.在DemoApplication启动类上增加如下注解,否则会报错找不到对应的mapper文件
package com.daojia;import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication@MapperScan("com.daojia.mapper")public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); }}
Description:Field userTagInfoMapper in cn.test.service.impl.UserTagInfoServiceImpl required a bean of type 'cn.test.mapper.UserTagInfoMapper' that could not be found.Action:Consider defining a bean of type 'cn.test.mapper.UserTagInfoMapper' in your configuration.
在cn.test.service下新建业务处理接口类如下
package cn.test.service;import cn.test.entity.UserTagInfo;public interface UserTagInfoService { int insert(UserTagInfo userTagInfo); UserTagInfo select(Integer id); int update(UserTagInfo userTagInfo); int delete(Integer id);}
在cn.test.service.impl下新建业务处理接口实现类,其中需要利用@Autowired注入对用的Mapper如下
package cn.test.service.impl;import cn.test.entity.UserTagInfo;import cn.test.mapper.UserTagInfoMapper;import cn.test.service.UserTagInfoService;import org.springframework.beans.factory.annotation.Autowired;public class UserTagInfoServiceImpl implements UserTagInfoService { @Autowired private UserTagInfoMapper userTagInfoMapper; @Override public int insert(UserTagInfo userTagInfo) { userTagInfo.setCurrentData(new Date()); return userTagInfoMapper.insert(userTagInfo); } @Override public UserTagInfo select(Integer id) { return userTagInfoMapper.selectByPrimaryKey(id); } @Override public int update(UserTagInfo userTagInfo) { return userTagInfoMapper.updateByPrimaryKey(userTagInfo); } @Override public int delete(Integer id) { return userTagInfoMapper.deleteByPrimaryKey(id); }}
在cn.test.controllers下新建调用业务层的controller如下
package cn.test.controllers;import cn.test.entity.UserTagInfo;import cn.test.service.UserTagInfoService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;@RestController@RequestMapping("/usertag")public class UserTagInfoController { @Autowired private UserTagInfoService userTagInfoService; @RequestMapping("/select") public UserTagInfo select(@RequestParam("id")Integer id){ UserTagInfo result = userTagInfoService.select(id); return result; }
//使用@PostMapping支持post请求方式
//使用@RequestBody支持把提交的json数据自动封装成对象 @PostMapping("/insert")public int insert(@RequestBody UserTagInfo userTagInfo){
int id = userTagInfoService.insert(userTagInfo); return id; }}
启动DemoApplication的main函数依然绑定了8080端口,访问http://localhost:8080/usertag/select?id=1报错如下
org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): cn.test.mapper.UserTagInfoMapper.selectByPrimaryKey
这里需要我们在application.properties中加入配置如下
#spring编译的时候不会加入xml文件,需要我们告诉spring哪些xml添加进去不然会报找不到对应的mapper.xmlmybatis.type-aliases-package=cn.test.entitymybatis.mapperLocations=classpath:mapper/*.xml
实际效果如下
在执行插入的时候遇到的一个问题我传时间用的格式是yyyy-MM-dd HH:mm:ss报错如下
大致意思是jackson不支持转换上述格式的字符串为java.util.Date类型,一般直接传时间戳格式解决。
三.总结
到这里Spring Boot整合Mybaties就结束了,首先要引入相关的依赖并在application.properties文件里加入数据库配置;然后创建实体,持久层接口,接口对应的配置文件,要注意配置文件的命名空间及标签id与接口保持一致;然后注意在application.properties文件加入扫描mapper配置,启动类上加上扫描接口的注解,最后就是注意jackson不能把yyyy-MM-dd HH:mm:ss格式的字符串直接转换为java.util.Date类型。