目录
例子:使用Mybatis框架且同步发送请求,采用前后端不分离形式,对产品进行插入,查询,删除,修改的操作
例子:使用Mybatis框架且异步发送请求,采用前后端分离形式,对用户进行注册登录
基于Mybatis框架weibo上传文件流程(使用ElementUI组件)
Mybatis框架在XML配置文件中书写SQL语句的用法(常用)
POJO :指简单的Java对象 ,是实体类Entity和值对象VO,还有DTO数据传输对象的统称
如何创建SpringBoot工程?
1. 创建Spring Initalizer 工程, 修改URL
推荐修改为 https://start.springboot.io 如果不能用则可以修改为https://start.aliyun.com
2. 在第二个页面中勾选Web->Spring Web 然后点击finish
3. 创建完工程后检查Build里面是否出现了绿色的对勾
如何使用MyBatis框架?
创建boot工程打3个勾:SpringWeb,MybatisFramework,MySQLDriver
如果工程中包含了Mybatis框架,启动工程时需要用到 application.properties 里面配置的连接数据库的信息。
基于Mybatis框架且前端同步发出请求
同步: 指单线程依次做几件事
同步请求(页面整体刷新): 指客户端只有一个主线程,线程负责页面渲染和监听操作。如果需要主线程发出请求时,会停止页面渲染(清空页面) 只负责发请求。当服务器响应了数据之后,主线程再次恢复渲染的职责,把服务器响应的数据显示到页面中,这个过程是将页面内容进行了整体的改变,称为整体刷新。同步请求只能实现页面的整体刷新无法实现局部刷新。
同步请求方式:地址栏发请求、表单发请求、超链接发请求。
例子:使用Mybatis框架且同步发送请求,采用前后端不分离形式,对产品进行插入,查询,删除,修改的操作
准备工作:创建bbsdb数据库,在product表中创建id,title,price,num字段
在application.properties中配置连接数据库信息 spring.datasource.url=jdbc:mysql://localhost:3306/bbsdb?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true spring.datasource.username=root spring.datasource.password=root
1:创建3个前端页面index.html,insert.html,update.html,部分代码
index.html <h1>Mybatis版本商品管理系统</h1> <a href="/insert.html">添加商品</a> <a href="/select">商品列表</a> <a href="/update.html">修改商品</a>insert.html <h1>添加商品页面</h1> <form action="/insert"> <input type="text" name="title" placeholder="商品标题"> <input type="text" name="price" placeholder="商品价格"> <input type="text" name="num" placeholder="商品销量"> <input type="submit" value="添加"> </form>update.html <h1>修改商品页面</h1> <form action="/update"> <input type="text" name="id" placeholder="请输入修改商品的id"> <input type="text" name="title" placeholder="标题"> <input type="text" name="price" placeholder="价格"> <input type="text" name="num" placeholder="库存"> <input type="submit" value="修改商品"> </form>
2:创建entity.Product类,属性名要和数据库中的字段一致,添加get,set,toString方法
3:创建controller.productController类,将前端传入的信息封装到实体类Product中
@RestController:
使用RestController 相当于在每一个方法的上面都添加@ResponseBody注解
@Autowired:
自动装配,此框架添加之后,Mybatis和Spring框架会自动生成ProductMapper的实现类,并且实例化该实现类(实现类里面会实现ProductMapper中的抽象方法,实现的方法里面写的就是JDBC代码),并且把实例化好的对象赋值给了mapper变量(ProductMapper mapper
package cn.tedu.boot03.controller;
import cn.tedu.boot03.entity.Product;
import cn.tedu.boot03.mapper.ProductMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
//使用RestController 相当于在每一个方法的上面都添加了@ResponseBody注解
@RestController
public class ProductController {
/*自动装配 此框架添加之后,Mybatis和Spring框架会生成
ProductMapper的实现类,并且实例化该实现类(实现类里面会实现ProductMapper中的
抽象方法,实现的方法里面写的就是JDBC代码),并且把实例化好的对象赋值给了mapper变量*/
@Autowired
ProductMapper mapper;
@RequestMapping("/insert")
public String insert(Product product){
System.out.println("product = " + product);
mapper.insert(product);
return "添加完成!<a href='/'>返回首页</a>";
}
@RequestMapping("/select")
public String select(){
List<Product> list = mapper.select();
String html = "<table border='1'>";
html+="<caption>商品列表</caption>";
html+="<tr><th>id</th><th>商品标题</th><th>价格</th><th>库存</th><th>操作</th></tr>";
for (Product p: list) {
html+="<tr>";
html+="<td>"+p.getId()+"</td>";
html+="<td>"+p.getTitle()+"</td>";
html+="<td>"+p.getPrice()+"</td>";
html+="<td>"+p.getNum()+"</td>";
html+="<td><a href='/delete?id="+p.getId()+"'>删除</a></td>";
html+="</tr>";
}
html+="</table>";
return html;
}
@RequestMapping("/delete")
public String delete(int id){
mapper.deleteById(id);
return "删除完成!<a href='/select'>返回列表页面</a>";
}
@RequestMapping("/update")
public String update(Product product){
System.out.println("product = " + product);
mapper.update(product);
return "修改完成!<a href='/select'>返回列表页面</a>";
}
}
4:创建mapper.ProductMapper接口
在Mapper接口中写实体类和数据库之间的对应关系,Mybatis框架会自动通过关系生成JDBC代码
@Mapper注释:
是由Mybatis框架中定义的一个扫描数据层接口的注解,注解起到一个描述作用,用于告诉Spring框架此接口的实现类由Mybatis负责创建,并将其实现类对象存储到Spring容器中。
package cn.tedu.boot03.mapper;
import cn.tedu.boot03.entity.Product;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface ProductMapper {
//在Mapper接口中书写实体类和数据库中表之间的对应关系
//Mybatis框架会通过此关系生成JDBC代码.
//#{xxx} 代表从参数列表中找同名的参数,
// 如果找不到则调用参数列表中对象的getXXX方法获取数据
@Insert("insert into product values(null,#{title},#{price},#{num})")
void insert(Product product);
//查询到的数据会封装到对象里面并且把对象装进list集合,把集合作为返回值
@Select("select id,title,price,num from product")
List<Product> select();
@Delete("delete from product where id=#{id}")
void deleteById(int id);
@Update("update product set title=#{title},price=#{price}" +
",num=#{num} where id=#{id}")
void update(Product product);
}
基于Mybatis框架且前端异步(axios框架)发送请求
异步:多线程同时做几件事
异步请求(局部刷新页面,常用): 指客户端的主线程负责页面渲染和监听操作,由子线程发出请求获取数据,获取到数据后将数据展示到原有页面,这种就叫做页面的局部刷新,只有通过异步请求才可以实现。
客户端如何发出异步请求:通过Axios框架发出异步请求
Axios:是一个js框架文件,在html页面中引入此文件即可。
<https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js>
发送请求时:GET和POST区别
GET:请求参数写在请求地址的后面,由于参数在地址栏中可见所以不能传递带有敏感信息的参数,,参数大小有限制只能传递几k的数据。
应用场景: 一般的查询请求都是用get,删除数据一般也使用get。
POST:请求参数在请求体里面,在请求体里面的参数是不可见的所以可以传递带有敏感信息的参数,,请求参数的大小也没有限制 。
应用场景: 有敏感信息的请求, 上传请求, 参数较多时使用
在前端中,如果发送请求方式是post请求,并且传递过来的是js自定义对象接收参数时
需要在Controller中,要添加@RequestBody注解,如果不加注解,接收参数是null
例子:使用Mybatis框架且异步发送请求,采用前后端分离形式,对用户进行注册登录
准备工作:创建user表字段是username,password,nickname
在application.properties中配置连接数据库信息 spring.datasource.url=jdbc:mysql://localhost:3306/bbsdb?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true spring.datasource.username=root spring.datasource.password=root
1:创建3个前端页面index.html,reg.html,login.html
index.html <h1>工程首页异步测试</h1> <a href="/reg.html">注册</a> <a href="/login.html">登录</a>
red.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>注册页面</h1>
<div>
<input type="text" placeholder="用户名" v-model="user.username"><br>
<input type="text" placeholder="密码" v-model="user.password"><br>
<input type="text" placeholder="昵称" v-model="user.nickname"><br>
<input type="button" value="注册" @click="reg()">
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
<script>
let v = new Vue({
el:"body>div",
data:{
user:{
username:"",
password:"",
nickname:""
}
},
methods:{
reg(){
//异步发送请求
axios.post("/reg",v.user).then(function (response) {
if (response.data==1){
alert("注册成功!")
location.href = "/"; //回到首页
}else{
alert("用户名已存在!");
}
})
}
}
})
</script>
</body>
</html>
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<h1>登录页面</h1>
<input type="text" placeholder="用户名" v-model="user.username">
<input type="password" placeholder="密码" v-model="user.password">
<input type="button" value="登录" @click="login()">
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
<script>
let v = new Vue({
el:"body>div",
data:{
user:{
username:"",
password:""
}
},
methods:{
login(){
//发出异步请求 服务器返回1成功,2用户名不存在,3密码错误
axios.post("/login",v.user).then(function (response) {
if (response.data==1){
alert("登录成功!");
location.href="/";
}else if(response.data==2){
alert("用户名不存在!");
}else{
alert("密码错误!");
}
})
}
}
})
</script>
</body>
</html>
2:创建controller.UserController类,根据前端发送的请求,处理注册登录业务,
package cn.tedu.boot05.controller;
import cn.tedu.boot05.entity.User;
import cn.tedu.boot05.mapper.UserMapper;
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.RestController;
@RestController
public class UserController {
@Autowired
UserMapper mapper;
@RequestMapping("/reg")
//在前端中,如果发送请求方式是post请求,并且传递过来的是js自定义对象接收参数时
//需要在Controller中,要添加@RequestBody注解,如果不加注解,接收参数是null
public int reg(@RequestBody User user){
User u = mapper.selectByUsername(user.getUsername());
if (u!=null){
return 2;//用户名已存在
}
mapper.insert(user);
return 1;//注册成功
}
@RequestMapping("/login")
public int login(@RequestBody User user){
User u = mapper.selectByUsername(user.getUsername());
if (u!=null){
if (user.getPassword().equals(u.getPassword())){
return 1;//登录成功
}
return 3;//密码错误
}
return 2;//用户名不存在
}
}
3:创建entity.User实体类,属性名和数据库字段一致
4:创建mapper.UserMapper接口,从数据库中通过用户名查询密码
package cn.tedu.boot05.mapper;
import cn.tedu.boot05.entity.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface UserMapper {
@Select("select password from user where username=#{username}")
User selectByUsername(String username);
@Insert("insert into user values(null,#{username},#{password},#{nickname})")
void insert(User user);
}
基于Mybatis框架weibo上传文件流程(使用ElementUI组件)
准备工作:创建weibo表,字段为id,content,url,created
1. 创建新工程boot071
2. 在application.properties配置文件中添加5行内容,其中3行数据库相关, 2行文件上传相关
spring.datasource.url=jdbc:mysql://localhost:3306/bbsdb?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
spring.datasource.username=root
spring.datasource.password=root
#客户端只能访问 static文件下的图片
#所以要设置一个静态资源文件夹,classpath:static指原来static文件夹
spring.web.resources.static-locations=file:d:/files,classpath:static
#设置上传文件大小,默认为1MB
spring.servlet.multipart.max-file-size=10MB
3. 创建index.html首页
添加上传页面超链接,请求地址 /upload.html,部分代码
<h1>文件上传测试</h1>
<a href="/upload.html">上传页面</a>
4. 创建upload.html页面
从elementUI文档中找到 Upload上传组件中的照片墙,,然后把3部分代码复制到自己的页面中,给el-upload添加 action设置 请求路径为/upload,添加name为pic,添加limit设置上传文件的数量。
属性:
action:设置上传地址
action="/upload"
name:设置上传参数名称
name="pic"
limit:上传图片的数量
limit="3"
el-upload组件里面添加 :on-success="handleSuccess" 并且实现handleSuccess方法。这样上传完成后,就会响应此方法。此方法中的response参数就是服务器响应的数据,把响应的图片路径显示到页面中。
文件传输成功后调用的方法:
:on-success="handleSuccess"
handleSuccess(response,file,fileList){
//response代表服务器上传成功时响应的数据,此处的response和Axios框架
//发出请求得到的response不同,类似于response.data
//file代表当前上传成功的文件
//fileList代表上传成功的所有文件(是一个数组)
v.weibo.url = response;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<!-- import CSS -->
<link rel="stylesheet" href="https://cdn.staticfile.org/element-ui/2.15.9/theme-chalk/index.css">
</head>
<body>
<div id="app">
<input type="text" placeholder="说点儿什么" v-model="weibo.content">
<!--action设置上传地址
name设置上传参数的名称
limit设置上传图片数量
-->
<el-upload
action="/upload"
name="pic"
limit="3"
list-type="picture-card"
:on-success="handleSuccess"
:on-preview="handlePictureCardPreview"
:on-remove="handleRemove">
<i class="el-icon-plus"></i>
</el-upload>
<el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="dialogImageUrl" alt="">
</el-dialog>
<hr>
<input type="button" value="发布微博" @click="insert()">
</div>
</body>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
<!-- import Vue before Element -->
<script src="https://cdn.staticfile.org/vue/2.6.14/vue.min.js"></script>
<!-- import JavaScript -->
<script src="https://cdn.staticfile.org/element-ui/2.15.9/index.min.js"></script>
<script>
let v = new Vue({
el: '#app',
data: function() {
return {
dialogImageUrl: '',
dialogVisible: false,
weibo:{
content:"",
url:""
}
}
},
methods:{
handleRemove(file, fileList) {
console.log(file, fileList);
//当删除页面中图片时触发的方法
//发请求删除服务器中的图片
//file代表当前删除的图片文件
// file.response代表上传该文件成功时服务器响应的内容(当前删除图片路径)
axios.get("/remove?url="+file.response).then(function () {
console.log("删除服务器图片完成!")
})
},
handlePictureCardPreview(file) {
this.dialogImageUrl = file.url;
this.dialogVisible = true;
},
handleSuccess(response,file,fileList){
//response代表服务器上传成功时响应的数据,此处的response和Axios框架
//发出请求得到的response不同,类似于response.data
//file代表当前上传成功的文件
//fileList代表上传成功的所有文件(是一个数组)
v.weibo.url = response;
},
insert(){
if (v.weibo.url==""){
alert("请选择上传的图片!");
return;
}
axios.post("/insert",v.weibo).then(function (response) {
alert("发布完成!");
location.href="/";//回到首页
})
}
}
})
</script>
</html>
5. 创建controller.UploadController
添加 upload方法处理/upload请求,在方法中得到唯一文件名,然后把文件保存到指定的文件夹中, 把/文件名,响应给客户端。
package cn.tedu.boot07.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
@RestController
public class UploadController {
@RequestMapping("/upload")
public String upload(MultipartFile pic) throws IOException {
//pic代表客户端上传的文件参数
System.out.println("pic = " + pic);
//得到图片的原始文件名 a.jpg
String fileName = pic.getOriginalFilename();
System.out.println("原始文件名:"+fileName);
//得到原始文件名的后缀
String suffix = fileName.substring(fileName.lastIndexOf("."));
//得到唯一文件名 UUID.randomUUID()获取唯一标识符
fileName = UUID.randomUUID()+suffix;
System.out.println("唯一文件名:"+fileName);
//准备保存图片的路径
String dirPath = "d:/files";
File dirFile = new File(dirPath);
//判断如果不存在 则创建文件夹
if (!dirFile.exists()){
dirFile.mkdirs();//创建文件夹
}
//得到文件的完整路径 d:/files/xxxx.jpg
String filePath = dirPath+"/"+fileName;
//把图片保存到上面的路径中 异常抛出
pic.transferTo(new File(filePath));
//把图片的url路径响应给客户端 /文件名
return "/"+fileName;
}
@RequestMapping("/remove")
public void remove(String url){
//删除磁盘上的文件
new File("d:/files"+url).delete();
}
}
6:创建controller.weiboController类
package cn.tedu.boot07.controller;
import cn.tedu.boot07.entity.Weibo;
import cn.tedu.boot07.mapper.WeiboMapper;
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.RestController;
import java.io.File;
import java.util.Date;
import java.util.List;
@RestController
public class WeiboController {
@Autowired
WeiboMapper mapper;
@RequestMapping("/insert")
public void insert(@RequestBody Weibo weibo){
System.out.println("weibo = " + weibo);
//new Date()得到当前系统时间
weibo.setCreated(new Date());
/* create table weibo(id int primary key auto_increment,
content varchar(100),url varchar(200),created timestamp);
*/
mapper.insert(weibo);
}
@RequestMapping("/select")
public List<Weibo> select(){
return mapper.select();
}
@RequestMapping("/delete")
public void delete(int id){
//通过微博id查询得到图片的路径
String url = mapper.selectUrlById(id);
// d:/files/xxx.jpg
//删除指定位置的文件
new File("d:/files"+url).delete();
mapper.deleteById(id);
}
}
7:创建entity.Weibo实体类
文件上传设置时间显示的格式:
通过@JsonFormat注解:设置显示时间的格式和时区
package cn.tedu.boot07.entity;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.util.Date;
public class Weibo {
private Integer id;
private String content;
private String url; //微博图片的路径
//通过JsonFormat设置显示的时间格式
// 2022年10月12号 15时23分22秒 2022-10-12 15:23:22
///yyyy年MM月dd号 HH时mm分ss秒 yyyy-MM-dd HH:mm:ss
@JsonFormat(pattern = "yyyy年MM月dd号 HH时mm分ss秒",timezone = "GMT+8")
private Date created; //发布微博时间
@Override
public String toString() {
return "Weibo{" +
"id=" + id +
", content='" + content + '\'' +
", url='" + url + '\'' +
", created=" + created +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
}
8:创建mapper.Weibo接口
package cn.tedu.boot07.mapper;
import cn.tedu.boot07.entity.Weibo;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface WeiboMapper {
@Insert("insert into weibo " +
"values(null,#{content}," +
"#{url},#{created})")
void insert(Weibo weibo);
@Select("select id,content,url,created from weibo")
List<Weibo> select();
@Delete("delete from weibo where id=#{id}")
void deleteById(int id);
@Select("select url from weibo where id=#{id}")
String selectUrlById(int id);
}
之前SQL语句是写在Mapper接口中的注解里面,有以下缺点:
- 如果SQL语句太长 存在字符串折行拼接问题 不够直观
- 一些关联查询的操作不易复用
Mybatis框架在XML配置文件中书写SQL语句的用法(常用)
优点:对于DBA(数据库管理员)更友好,不需要去Java代码中改SQL语句而是从配置文件中进行修改
Mybatis框架使用XML配置文件的步骤:
例子一、对产品进行插入,查询,修改,批量插入,批量删除,动态插入,动态修改
准备工作:创建product表,字段为id,tittle,price,num
1:创建Mybatis配置类(有@Configuration注解的类为配置类),通过@MapperScan注解取代每个Mapper接口类中的@Mapper注解
package cn.tedu.boot08.config;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;
//告诉编译器此工程的所有Mapper接口都在这个包路径里面
@Configuration
@MapperScan("cn.tedu.boot08.mapper")
public class MyBatisConfig {
}
2:创建实体类 entity.Product,和数据库中的字段一致
package cn.tedu.boot08.entity;
public class Product {
private Integer id;
private String title;
private Double price;
private Integer num;
public Product() {
}
public Product(Integer id, String title, Double price, Integer num) {
this.id = id;
this.title = title;
this.price = price;
this.num = num;
}
@Override
public String toString() {
return "Product{" +
"id=" + id +
", title='" + title + '\'' +
", price=" + price +
", num=" + num +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public Integer getNum() {
return num;
}
public void setNum(Integer num) {
this.num = num;
}
}
3:创建mapper.ProductMapper接口
在配置类中使用了@MapperScan注解,所以不需要写@Mapper注解
@Insert等注解也不写,sql语句写在xml配置文件中单独配置
package cn.tedu.boot08.mapper;
import cn.tedu.boot08.entity.Product;
import java.util.List;
//此时使用了MapperScan注解 则不需要再写@Mapper注解
public interface ProductMapper {
//此处不再写@Insert等注解,SQL语句写在xml配置文件中
void insert(Product product);
//删除
void deleteById(int id);
//修改
void update(Product product);
//查询所有
List<Product> select();
//通过id查询单个
Product selectById(int id);
//查询数量
int count();
// 1,3,5,6,7 delete from product where id in(1,3,5)
//批量删除 int返回值 返回的是生效的行数(删除了多少条)
int deleteByIds1(List<Integer> ids); //集合
int deleteByIds2(Integer[] ids); //数组
int deleteByIds3(Integer... ids);
//insert into product values(null,'',100,30),(null,'',100,30),(null,'',100,30)
int insertProducts(List<Product> list);
//动态插入数据,自动识别对象属性是否有值
int dynamicInsert(Product product);
void dynamicUpdate(Product p);
}
4:创建ProductMapper.xml文件:
在resources目录下创建mappers文件夹,网上下载Mybatis Mapper映射文件Mapper.xml,将名字改为ProductMapper.xml,专门用来写sql语句。
1:配置当前配置文件和哪个接口有关系:
namespace="cn.tesu.boot08.mapper.ProductMapper":
2:id和接口中的方法名相同:
id="insert"
3:查询时通过resultType属性,设置返回结果类型
4:批量使用:
foreach循环遍历标签
属性:collection:的值如果是List集合则写list,如果不是List集合则写array
item:代表遍历的集合中变量
separator:代表分隔符
5:动态插入数据时使用:
trim标签
属性:prefix:前缀
suffix:后缀
suffixOverrides去掉多余的逗号 ,
6:动态修改数据使用:
set标签:会自动去掉后面的逗号
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.tedu.boot08.mapper.ProductMapper">
<!--插入数据-->
<insert id="insert">
INSERT INTO product VALUES (
NULL,#{title},#{price},#{num}
)
</insert>
<!--通过id删除数据-->
<delete id="deleteById">
DELETE FROM product WHERE id=#{id}
</delete>
<!--通过id修改数据-->
<update id="update">
UPDATE product SET
title=#{title},price=#{price},num=#{num}
WHERE id=#{id}
</update>
<!--查询所有数据-->
<select id="select" resultType="cn.tedu.boot08.entity.Product">
SELECT id,title,price,num FROM product
</select>
<!--通过id查询数据-->
<select id="selectById" resultType="cn.tedu.boot08.entity.Product">
SELECT id,title,price,num FROM product WHERE id=#{id}
</select>
<!--查询数量-->
<select id="count" resultType="int">
SELECT count(*) FROM product
</select>
<!--批量删除,3种方法-->
<!--foreach循环遍历标签 collection的值如果是List集合则写list
如果不是List集合则写array item代表遍历的集合中变量, separator代表分隔符
-->
<delete id="deleteByIds1">
DELETE FROM product
WHERE id in(
<foreach collection="list" item="id" separator=",">
#{id}
</foreach>
)
</delete>
<delete id="deleteByIds2">
DELETE FROM product
WHERE id in(
<foreach collection="array" item="id" separator=",">
#{id}
</foreach>
)
</delete>
<delete id="deleteByIds3">
DELETE FROM product
WHERE id in(
<foreach collection="array" item="id" separator=",">
#{id}
</foreach>
)
</delete>
<!--批量插入数据-->
<insert id="insertProducts">
INSERT INTO product
VALUES
<foreach collection="list" item="p" separator=",">
(
NULL ,#{p.title},#{p.price},#{p.num}
)
</foreach>
</insert>
<!--动态插入数据 suffixOverrides去掉多余的,-->
<!--自动识别对象属性是否有值,用if判断-->
<insert id="dynamicInsert">
INSERT INTO product
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="title!=null">title,</if>
<if test="price!=null">price,</if>
<if test="num!=null">num</if>
</trim>
VALUES
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="title!=null">#{title},</if>
<if test="price!=null">#{price},</if>
<if test="num!=null">#{num}</if>
</trim>
</insert>
<!--动态修改数据 set标签会自动去掉后面的逗号-->
<update id="dynamicUpdate">
UPDATE product
<set>
<if test="title!=null">title=#{title},</if>
<if test="price!=null">price=#{price},</if>
<if test="num!=null">num=#{num}</if>
</set>
WHERE id=#{id}
</update>
</mapper>
5:在application.properties中,配置Mybatis书写sql语句的xml文件位置
spring.datasource.url=jdbc:mysql://localhost:3306/bbsdb?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true spring.datasource.username=root spring.datasource.password=root spring.web.resources.static-locations=file:d:/files,classpath:static spring.servlet.multipart.max-file-size=10MB #在application.properties中,配置Mybatis书写sql语句的xml文件位置 mybatis.mapper-locations=classpath:mappers/*xml
6:测试内容放在test中,Boot08ApplicationTests用来测试数据是否操作成功
package cn.tedu.boot08;
import cn.tedu.boot08.entity.MyProduct;
import cn.tedu.boot08.entity.Product;
import cn.tedu.boot08.mapper.MyProductMapper;
import cn.tedu.boot08.mapper.ProductMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.ArrayList;
@SpringBootTest
class Boot08ApplicationTests {
@Autowired(required = false)
ProductMapper mapper;
@Test
void contextLoads() {
Product p = new Product();
p.setTitle("xml标题");
p.setPrice(111.0);
p.setNum(222);
mapper.insert(p);
}
@Test
void t1(){
mapper.deleteById(1);
}
@Test
void t2(){
Product p = new Product();
p.setId(6);
p.setTitle("麦当劳");
p.setPrice(10.0);
p.setNum(50);
mapper.update(p);
}
@Test
void t3(){
System.out.println(mapper.selectById(6));
System.out.println(mapper.select());
}
@Test
void t4(){
System.out.println(mapper.count());
}
@Test
void t5(){
// ArrayList<Integer> list = new ArrayList<>();
// list.add(5);
// list.add(6);
// System.out.println(mapper.deleteByIds1(list));
// Integer[] ids = {7,8};
// System.out.println(mapper.deleteByIds2(ids));
System.out.println(mapper.deleteByIds3(9,10));
}
@Test
void t6(){
Product p1 = new Product(null,"a",1.0,1);
Product p2 = new Product(null,"b",2.0,2);
Product p3 = new Product(null,"c",3.0,3);
ArrayList<Product> list = new ArrayList<>();
list.add(p1);
list.add(p2);
list.add(p3);
System.out.println(mapper.insertProducts(list));
}
@Test
void t7(){
Product p = new Product();
p.setTitle("手机");
mapper.dynamicInsert(p);
}
@Test
void t8(){
Product p = new Product();
p.setId(14);
p.setPrice(100.0);
mapper.dynamicUpdate(p);
}
@Autowired(required = false)
MyProductMapper myMapper;
@Test
void t9(){
MyProduct mp = new MyProduct();
mp.setTitle("豆浆");
mp.setSaleCount(5);
mp.setViewCount(1000);
myMapper.insert(mp);
}
@Test
void t10(){
System.out.println(myMapper.selectById(1));
}
@Test
void t11(){
System.out.println(myMapper.select());
}
}
例子二、在查询产品中,当查询字段和属性名不一致时,使用映射查询
实体类entity.MyProduct
package cn.tedu.boot08.entity;
public class MyProduct {
private Integer id;
private String title;
private Integer saleCount;
private Integer viewCount;
@Override
public String toString() {
return "MyProduct{" +
"id=" + id +
", title='" + title + '\'' +
", saleCount=" + saleCount +
", viewCount=" + viewCount +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Integer getSaleCount() {
return saleCount;
}
public void setSaleCount(Integer saleCount) {
this.saleCount = saleCount;
}
public Integer getViewCount() {
return viewCount;
}
public void setViewCount(Integer viewCount) {
this.viewCount = viewCount;
}
}
数据库中的字段是:sale_count,view_Count
java中使用驼峰命名,命名为以下:
private Integer saleCount;
private Integer viewCount;所以会产生不对应关系,需要添加手动映射
手动映射:resultMap标签
<resultMap id="起个名字" type="封装类型">
<result column="sale_count" property="saleCount"></result>
</resultMap>
column="sale_count"是数据库中字段,property="saleCount"是java中的属性名,映射之后就会对应
复用sql语句:sql标签
<!--定义复用的SQL语句-->
<sql id="query">
要复用的sql语句
</sql><!--引用复用的sql语句-->
<include refid="query"></include>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.tedu.boot08.mapper.MyProductMapper">
<insert id="insert">
INSERT INTO my_product
VALUES (
null,#{title},#{saleCount},#{viewCount}
)
</insert>
<!--手动映射resultMap-->
<!--column="sale_count"是数据库中字段,property="saleCount"是java中的属性名,映射之后就会对应-->
<select id="selectById" resultMap="productRM">
<!--引用复用的sql语句-->
<include refid="query"></include>
WHERE id=#{id}
</select>
<resultMap id="productRM" type="cn.tedu.boot08.entity.MyProduct">
<result column="sale_count" property="saleCount"></result>
<result column="view_count" property="viewCount"></result>
</resultMap>
<select id="select" resultMap="productRM">
<include refid="query"></include>
</select>
<!--定义复用的SQL语句-->
<sql id="query">
SELECT id,title,sale_count,view_count FROM my_product
</sql>
</mapper>
POJO :指简单的Java对象 ,是实体类Entity和值对象VO,还有DTO数据传输对象的统称
Entity实体类(和数据库中表字段一致的属性):通常和对应的表字段的数量是一致的。
DTO数据传输对象(前端请求的参数的属性):当客户端给服务器传递参数时,参数的数量可能比实体类中的数量要少。比如实体类中有10个参数,但是客户端只传递过来的3个参数,此时通过DTO接收传递过来的参数,如果使用实体类接收也可以但是会存在很多的null值,使用DTO好处是只要发现null值就能判断出传输出错了。
VO值对象(从数据库中查找出来的属性):从数据库中的某个表查询数据,有多种场景,有的需要查全部,而有的查询只需要查一部分数据。如果只查一部分数据查询回来的数据直接用Entity接收封装的话,则Entity中会存在大量的null值, 这些null值传输给客户端也会占用流量,浪费资源,使用VO则可以解决此问题。
例子一、使用Pojo,关联查询:
准备两张表:
create table team(id int primary key auto_increment,name varchar(20),loc varchar(20)); create table player(id int primary key auto_increment,name varchar(20),team_id int); insert into team values(null,'公牛队','芝加哥'),(null,'火箭队','休斯顿'); insert into player values(null,'乔丹',1),(null,'皮蓬',1),(null,'姚明',2),(null,'麦迪',2);
1:创建实体类(和数据库中表字段一致的属性):entity.Player,entity.Team
package cn.tedu.boot10.pojo.entity;
public class Player {
private Integer id;
private String name;
private Integer teamId;
@Override
public String toString() {
return "Player{" +
"id=" + id +
", name='" + name + '\'' +
", teamId=" + teamId +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getTeamId() {
return teamId;
}
public void setTeamId(Integer teamId) {
this.teamId = teamId;
}
}
package cn.tedu.boot10.pojo.entity;
public class Team {
private Integer id;
private String name;
private String loc;
@Override
public String toString() {
return "Team{" +
"id=" + id +
", name='" + name + '\'' +
", loc='" + loc + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
}
创建VO值对象(从数据库中查找出来的属性):vo.PlayerVo,vo.TeamVo
package cn.tedu.boot10.pojo.vo;
import cn.tedu.boot10.pojo.entity.Team;
public class PlayerVO {
private Integer id;
private String name;
//查运动员信息时,还要查询队伍信息
private Team team;
@Override
public String toString() {
return "PlayerVO{" +
"id=" + id +
", name='" + name + '\'' +
", team=" + team +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Team getTeam() {
return team;
}
public void setTeam(Team team) {
this.team = team;
}
}
package cn.tedu.boot10.pojo.vo;
import cn.tedu.boot10.pojo.entity.Player;
import java.util.List;
public class TeamVO {
private Integer id;
private String name;
private String loc;
//在查询队伍信息时,还要查询队员信息
private List<Player> playerList;
@Override
public String toString() {
return "TeamVO{" +
"id=" + id +
", name='" + name + '\'' +
", loc='" + loc + '\'' +
", playerList=" + playerList +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
public List<Player> getPlayerList() {
return playerList;
}
public void setPlayerList(List<Player> playerList) {
this.playerList = playerList;
}
}
创建接口:mapper.PlayMapper,mapper.TeamMapper
通过id查数据
package cn.tedu.boot10.mapper;
import cn.tedu.boot10.pojo.vo.PlayerVO;
public interface PlayerMapper {
PlayerVO selectById(int id);
}
package cn.tedu.boot10.mapper;
import cn.tedu.boot10.pojo.vo.TeamVO;
public interface TeamMapper {
TeamVO selectById(int id);
}
创建mappers:PlayerMapper.xml,TeamMapper.xml
关联对象类型时:
在PlayerMapper.xml中,还要查询封装在Team对象中的信息
所以使用了association标签
<association property="team" javaType="cn.tedu.boot10.pojo.entity.Team">
<id column="tid" property="id"></id>
<result column="tname" property="name"></result>
<result column="loc" property="loc"></result>
</association>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.tedu.boot10.mapper.PlayerMapper">
<select id="selectById" resultMap="playerRM">
SELECT p.id pid,p.name pname,t.id tid,t.name tname,loc
FROM player p join team t
ON p.team_id=t.id
WHERE p.id=#{id}
</select>
<resultMap id="playerRM" type="cn.tedu.boot10.pojo.vo.PlayerVO">
<!--id特殊,使用id标签;其他仍用result标签-->
<id column="pid" property="id"></id>
<result column="pname" property="name"></result>
<association property="team" javaType="cn.tedu.boot10.pojo.entity.Team">
<id column="tid" property="id"></id>
<result column="tname" property="name"></result>
<result column="loc" property="loc"></result>
</association>
</resultMap>
</mapper>
关联类型对象时:
在TeamMapper.xml中,还要查询封装在集合中的信息
所以使用collection标签,ofType:指定playerList中的类型
<collection property="playerList"
ofType="cn.tedu.boot10.pojo.entity.Player">
<id column="pid" property="id"></id>
<result column="pname" property="name"></result>
</collection>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.tedu.boot10.mapper.TeamMapper">
<select id="selectById" resultMap="deptRM">
SELECT t.id tid,t.name tname,loc,p.id pid,p.name pname
FROM team t join player p
ON t.id = p.team_id
WHERE t.id=#{id}
</select>
<resultMap id="deptRM" type="cn.tedu.boot10.pojo.vo.TeamVO">
<id column="tid" property="id"></id>
<result column="tname" property="name"></result>
<result column="loc" property="loc"></result>
<collection property="playerList"
ofType="cn.tedu.boot10.pojo.entity.Player">
<id column="pid" property="id"></id>
<result column="pname" property="name"></result>
</collection>
</resultMap>
</mapper>