MyBatis-------批量插入
一、在MySQL数据库中
在mysql数据库中,使用的是foreach标签。 foreach元素的属性主要有 collection,item,index,open,separator,close。
常用的有
1.item ,是临时的元素的代表。迭代的时候要用上它,很容易被忽略从而报错。
2.collection标签是集合的代表。如果传入的值是List集合,那么collection的值是list。如果传入的是数组,那么collection的值是array。如果在接口上定义了传递进来的参数,那么collection的值就是那么定义好的参数,比如下面的tags。
public interface ArticleMapper ...
int insertArticleTag(@Param("tags")List tags);
index表示迭代的位置。
open表示从什么地方开始,close表示什么地方结束
separator表示在每次进行迭代之间以什么符号作为分隔符。MySQL中通常是",",Oracle中有时写union。
插入实例。
INSERT INTO t_tag(name,articleId)
VALUES('good','22') , ('nice','22'),('perfect','22')
INSERT INTO t_tag(name,articleId)
VALUES
(#{tag.name},#{tag.articleId})
注意:这里需要使用tag.name,我第一次写的时候,忘记了加上item中定义的临时属性,导致出错。
二、Oracle数据库
MySQL写法,转成Oracle。
INSERT INTO T_REGION
(
ID,
GRADE,
COLLEGE_CODE,
TYPE,
MAX,
GMT_CREATE
)
SELECT 序列.NEXTVAL ID, A.*
FROM(
SELECT
#{entity.grade} GRADE,
#{entity.collegeCode} COLLEGE_CODE,
1 TYPE,
#{entity.maxProjectCredit} MAX,
sysdate GMT_CREATE
FROM dual
)A
INSERT INTO t_region (
grade,
college_code,
type,
max,
gmt_create
)
VALUES
(
#{entity.grade},
#{entity.collegeCode},
2,
#{entity.max},
now()
)
MyBatis--------模糊查询
一、使用${ }
不能够防止SQL注入。MySQL与Oracle通用
二、把模糊查询的 %加上引号
就像这样子, "%"#{param}"%"
多个匹配条件可以像这样写
三、使用函数进行拼
MyBatis--------生成主键
一、在MySQL数据库中的写法
在mysql数据库中,写法比较简单。在Mybatis配置文件中添加 userGeneratedKeys="true" keyProperty="id"
这样就能把我们插入的实体的主键id赋值。下次使用的时候,再调用 对象.getId()就能获取到刚才生成的主键。
注意点:
1.实体的id属性需要有setId()方法。
2.数据库中的表主键设置为 auto_increment
示例
INSERT INTO t_account(nickname,loginName,password)
VALUE (#{nickname},#{loginName},#{password},#{idCard})
在这个例子里面,被插入的Account对象在构造的时候Long id 属性为null。在插入数据库完成后,作为参数的account对象的Long id属性被赋予为数据库中的记录的主键的值。
二、在Oracle数据库中的写法
我们知道,mysql优化里面有一条:为每一张表都设置一个int类型的主键,它能够自增,方便分页和排序。
Oracle数据库中,它不支持主键自增,所以需要我们从序列中取得最新的序列值。
在Mybatis中,可以使用 selectKey标签,在执行SQL语句之前,为实体的ID属性赋值,然后再执行SQL。
select SEQ_EXAMINEE.nextval from dual
insert into T_Register(id,idCard,cellPhone,password,name)
values(#{id},#{idCard},#{cellPhone},#{password},#{name})
MyBatis--------查询参数携带集合
一、携带参数
Java的查询参数里携带了一个集合。
@Data
public class TeacherParam {
private List teacherIds;
}
这个参数作为查询Mapper的参数。
@Repository
@Mapper
public interface ClassRoomMapper {
List queryTeacher(TeacherParam teacherParam);
}
以下是调用这个内部集合的例子。
MyBatis-------时间函数处理
一、MySQL解决方案
MySQL使用date_format函数来解决。
SELECT * FROM `t_log`
WHERE
# 比19号多,但是不到21号,就是查询20号
date_format(operation_time,'%Y-%m-%d') > '2020-03-19'
AND
date_format(operation_time,'%Y-%m-%d') < '2020-03-21'
查询结果:
在MyBatis中写法实例。startDate是字符串类型,operation_time是SQL的时间类型
= #{startDate}
]]>
二、Oracle解决方案
Oracle使用to_date函数来解决。
startDate与endDate是字符串类型
从实际解决方案上来看,是利用Oracle的to_date函数,将参数转为时间与表中的记录进行对比。
【t.CREATE_DATE】 ,这个字段是Oracle的DATE类型。
【 #{startDate}】 ,传入的参数为字符串为类型
and
=
to_date(concat(#{startDate},'00:00:00'),'YYYY-MM-DD HH24:MI:SS')
]]>
and
MyBatis-------使用IN语句
一、使用foreach
注意点:Java的List需要传 List
解析后效果:
SELECT * from t_article WHERE id in ( 21 , 22 ,23 )
删除实例与插入实例大同小异。比如
delete from t_tag where id in ( 58 , 59 )
二、字符串情况
如果希望达到的效果是 in ( '2019-2020-1' , '2019-2020-2' '2020-2021-1' )
集合需要传 List
#{termCode}
MyBatis-------级联查询(一对一、一对多)
一、持有其它对象
package com.tyzhou.stu.model;
import lombok.Data;
@Data
public class ClassRoom {
/**
* 班级编号
*/
private Long id;
private String name;
/**
* 一个班级有一个班主任
*/
private Teacher teacher;
}
@Data
public class Teacher {
private Long id;
private String name;
}
核心:映射到某个对象的某个属性中
二、持有其它对象集合
一个老师会管多个班级。
@Data
public class Teacher {
private Long id;
private String name;
/**
* 一个老师管理多个班级
*/
List classRoomList;
}
@Data
public class ClassRoom {
/**
* 班级编号
*/
private Long id;
private String name;
}
拨云见日:
(1) 最先查询出老师的相关数据,并使用resultMap进行映射
(2) 在resultMap映射完老师实体后,将老师实体的主键ID作为参数传递
(3) 最后一个查询,它接收老师主键ID作为条件,查询出老师管理的多个班级集合
MyBatis--------格式化性别等字段
通用写法
SELECT
USER_ACCOUNT,
(CASE
WHEN SEX = 1 THEN '男'
WHEN SEX = 2 THEN '女'
ELSE '未知'
END )sex
FROM m_user u
Oracle特有写法
SELECT
USER_ACCOUNT,
decode(sex,1,'男',2,'女','未知') "sex"
FROM m_user u
再提供一个判断个数的示例
SELECT DECODE(COUNT(1),0,'','有调课') from 表
MyBatis-------特殊符号处理(CDATA标签)
二、CDATA标签
MyBatis--------占位符${}与#{}
一、基本区别
简单的说就是#{}传过来的参数带引号,而${}传过来的参数不带单引号。
涉及到这个问题,最初是在毕业设计项目中,进行动态排序发现的问题。看下面的SQL语句
我的目的是在文章表't_article'中,根据文章的点击量'click'来排序。但是传过来的参数带上了引号,因此排序无效
执行出来的SQL语句的效果是: SELETE * FROM t_article order by 'click'。
修改后的SQL语句如下,
很简单,就是把#{}修改成了${},这样的话,传递过来的值就不会带上引号了。
现在执行出来的SQL语句的效果是: SELETE * FROM t_article order by click。达到了预期的效果
二、#{}对SQL语句的影响
用#{}传过来的参数可以防止SQL注入,而用${}就不行了。
如何防止SQL注入:
1.对用户的输入进行校验,可以通过正则表达式,或限制长度
2.不要使用动态拼装sql,使用预编译的带占位符的SQL语句。比如使用preparedStatement
MyBatis-------获取系统当前时间
一、MySQL
insert into t_user(id,idcard,operation_time) values( #{id} , #{idCard} , now() )
二、Oracle
insert into t_user(id,idcard,operation_time) values( #{id} , #{idCard} , sysdate)
MyBatis-------返回值是对象类型需要显示指明
/**
* 获取表里所有记录的数量
*
* @param statement 要执行的SQL语句的标记,对应的是Mybatis中的id的值
* @return 表里所有记录的数量
*/
Integer getCount(String statement);
正确的Mybatis配置,要加上 resultType = 'integer'。
MyBatis-------内查询
select stuId ,teacherId from T_Table
比如上述的SQL语句中,查询出来的数据是stuId和teacherId 。stuId对应的那条数据在 T_SYS_PARAM 中,teacherId也在T_SYS_PARAM 表中,但是它们对应的是不同的记录,采用LEFT JOIN T_SYS_PARAM 的方法并不奏效。为了查询出学生的名字和老师的名字,必须分别查询。
select
t.stuId ,
t.teacherId ,
(select p.stuName from T_SYS_PARAM p where p.id= t.stuId) stuName ,
(select p.tecName from T_SYS_PARAM p where p.id= t.teacherId) tecName
from T_Table t
所以说,怪不得项目里面有那么多像 " ( ) "这样的查询
MyBatis-------批量更新
Oracle
如果操作成功,返回值为 Integer 类型的 -1
UPDATE T_TABLE SET
A= #{item.a},
B= #{item.b},
C= #{item.c},
D= sysdate
where ID = #{item.id}
二、MySQL
UPDATE m_classroom
SET name = 3,
teacherId = 3
WHERE id = 1;
UPDATE m_classroom
SET name = 3,
teacherId = 3
WHERE id = 2;
Mybatis-----Oracle关于0开头的小数,开头的0消失的问题
SELECT TO_CHAR(51,'fm99999990.099') from dual; --51.0
SELECT TO_CHAR(0.42,'fm99999990.099') from dual; --0.42
MyBatis-------解决Mysql LIMIT分页关键字offset偏移量过大
SELECT * FROM T_TABLE LIMIT 【START, SIZE 】
LIMIT 使用上述两个参数,一个是从第多少条记录开始START,另外一个是希望要查询出来得个数SIZE
随着START值的增加,那么此查询条件的速度会越来越慢。
解决方案的前提是该表的主键是自增的。那么就能先确定到从第多少条记录开始
-定位主键方法
SELECT * FROM t_user LIMIT 20883005,10
修改成
SELECT * FROM t_user WHERE id>(SELECT ID FROM t_user LIMIT 20883005,1) LIMIT 10
MyBatis--------解决Oracle的to_number函数不支持有字母的情况
需求:某表的某列是varchar类型,此列里存放的不全是数字,如何使用to_number函数呢?
解决:使用translate函数
SELECT ${primaryKey} FROM ${sourceTable}
WHERE
1=1
-- 匹配全数字类型
AND regexp_like(${sourceField}, '(^[0-9]+.[0-9]+$)|(^[0-9]$)')
= #{minValue}
]]>
MyBatis-------if test标签处理相等的情况
MyBatis--------邮箱正则、身份证正则、电话正则
@[toc]
电子邮箱
MySQL
Oracle
身份证
MySQL
Oracle
电话号码
MySQL
Oracle
MyBatis--------查询7天内的记录
MySQL
SELECT count(1) FROM ${sourceTable}
WHERE
1=1
AND ${createTime} >= DATE_SUB(NOW(),INTERVAL 7 DAY)
Oracle
SELECT count(1) FROM ${sourceTable}
WHERE
1=1
AND ${createTime} >= sysdate - 7
MyBatis--------MybatisPlus继承体系小记录
@Data
public class BaseDO {
@TableField(fill = INSERT)
@TableLogic
protected Integer isDeleted;
@TableField(fill = INSERT_UPDATE)
protected Date gmtCreate;
@TableField(fill = INSERT_UPDATE)
protected Date gmtModified;
@TableField(fill = INSERT)
protected String createId;
}
@Data
@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
@TableName(value = "t_xxx")
@Accessors(chain = true)
public class RuleDO extends BaseDO {
private String id;
private String ruleName;
private Integer type;
private String calculateTypeId;
}
@Configuration
public class MybatisPlusConfig {
@Component
static class XXX implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
// 对应实体需要标注@TableField(fill = FieldFill.INSERT) 或
// @TableField(fill = FieldFill.INSERT_UPDATE)
this.strictInsertFill(metaObject, "createId", String.class,
"1");
this.strictInsertFill(metaObject, "gmtCreate", Date.class, new Date());
this.strictInsertFill(metaObject, "gmtModified", Date.class, new Date());
this.strictInsertFill(metaObject, "isDeleted", Integer.class, 0);
}
@Override
public void updateFill(MetaObject metaObject) {
// 对应实体需要标注@TableField(fill = FieldFill.UPDATE) 或
// @TableField(fill = FieldFill.INSERT_UPDATE)
setFieldValByName("gmtModified", new Date(), metaObject);
}
}
}
MyBatis--------Oracle超过1000条的问题
select * from test_table
where 1 = 1
AND PK_ID IN
) OR PK_ID IN (
,
#{Id}
MyBatis Plus Service CRUD接口测试
Service如何集成MP
@Mapper
public interface RoleMapper extends BaseMapper {
}
public interface RoleService extends IService{
}
@Service
@RequiredArgsConstructor
public class RoleServiceImpl extends ServiceImpl implements RoleService{
}
插入
boolean save(T entity)
boolean saveBatch(Collection entityList)
boolean saveBatch(Collection entityList, int batchSize);
更新
根据ID更新,null值的属性不参与更新
Employee employee = new Employee();
employee.setId(7);
employee.setLastName("a");
employee.setAge(23);
employee.setEmail(null);
employee.setGender(null);
service.updateById(employee);
UPDATE tb_employee SET last_name='a', age=23
WHERE id=7;
根据条件进行更新,并置空其他属性(项目库的我那个头疼的置空功能就能用这个解决)。
RoleDO roleDO = new RoleDO(roleDto);
roleService.update(roleDO, new UpdateWrapper().lambda().
eq(RoleDO::getId, roleDO.getId())
//如果要置空其他的属性
.set(RoleDO::getUpdateId, null)
.set(RoleDO::getUpdateTime, null)
);
查询
T getById(Serializable id)
获取一个(如果出现多个,则报错,传统的Excepted One , Actual Two 异常)
roleService.getOne(new QueryWrapper().lambda()
.eq(RoleDO::getIsManager, role.getIsManager())
);
删除
根据主键ID
boolean removeById(Serializable id)
根据条件进行删除
RoleDO roleDO = new RoleDO(roleDto);
roleService.remove(new QueryWrapper().lambda().
eq(RoleDO::getId, roleDO.getId())
);
通过Map拼接条件删除
Map map = new HashMap<>();
map.put("id", roleDO.getId());
map.put("role_name", roleDO.getRoleName());
roleService.removeByMap(map);
where id = #{id} AND role_name = #{roleName}
批量查询
查询所有
List list()
根据ID批量查询
List listByIds(Collection idList)
根据条件批量查询
RoleDO roleDO = new RoleDO(dto);
return roleService.list(new QueryWrapper().lambda()
.eq(RoleDO::getIsManager, roleDO.getIsManager())
);
分页查询
pageIndex第几页,pageSize每页几条数据
Page page = new Page(pageIndex,pageSize);
return roleService.page(page);
根据条件分页查询
@GetMapping(value = "/pageByWrap")
@SuppressWarnings(value = "unchecked")
public Object pageByWrap() {
return roleService.page(PageContext.getPage(), new QueryWrapper().lambda()
.eq(RoleDO::getIsManager, "0")
);
}
查个数数量
int count()
根据条件,查询总记录数
return roleService.count(new QueryWrapper().lambda()
.eq(RoleDO::getIsManager, "0")
);
MyBatis Plus Mapper CRUD接口测试
插入
int insert(T entity);
删除
int deleteById(Serializable id);
int deleteBatchIds(Collection idList);
根据条件进行删除。原理为SQL语句拼接,因此Map中需要传输数据库字段列
实质为 delete from 表 where 【key1 = value1】 AND 【key2 = value2】
【key1-value1】【key2-value2】为Map的键值对
int deleteByMap(Map columnMap);
更新
根据ID更新。null值忽略。
Employee employee = new Employee();
employee.setId(7);
employee.setLastName("a");
employee.setAge(23);
employee.setEmail(null);
employee.setGender(null);
mapper.updateById(employee);
UPDATE tb_employee SET last_name='a', age=23
WHERE id=7;
根据条件更新,null值忽略。
Employee employee = new Employee();
employee.setId(7L);
employee.setLastName("c");
//这里设置的null,就是说不操作email字段
employee.setEmail(null);
employee.setGender(null);
employee.setAge(23);
mapper.update(employee, new UpdateWrapper().lambda()
.eq(Employee::getId, 7)
);
UPDATE tb_employee SET last_name='c', age=23
WHERE (id = 7);
定制置空。
Employee employee = new Employee();
employee.setId(7L);
employee.setLastName("c");
//这里设置的null,就是说不操作email字段
employee.setEmail(null);
employee.setGender(null);
employee.setAge(23);
mapper.update(employee, new UpdateWrapper().lambda()
.eq(Employee::getId, 7)
//这里可以定制置空SQL
.set(Employee::getEmail,null)
.set(Employee::getGender,"")
);
UPDATE tb_employee SET last_name='c', age=23, email=null,gender=''
WHERE (id = 7);