Mybatis是一款优秀的持久层框架,用户简化JDBC(使用Java语言操作关系型数据库的一套API)开发
使用Mybatis查询所有用户数据:
代码演示:
UserMapper:
@Mapper //被调用时会通过动态代理自动创建实体类,并放入IOC容器中
public interface UserMapper {
@Select("select * " +
"from user")
public List<User> list();
}
单元测试中使用:
@SpringBootTest
class SpringbootMybatisQuickstartApplicationTests {
@Autowired
public UserMapper userMapper;
@Test
public void contextLoads() {
List<User> list = userMapper.list();
list.stream().forEach(user -> System.out.println(user));
}
}
JDBC
JDBC与Mybatis相比:
数据库连接池:
总结:
lombok:
Mybatis日志:
在application.properties中添加代码
#配置Mybatis的日志信息,并输出到控制台
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
数据库操作
1.通过注解操作数据库
删除操作
根据主键删除:
代码演示:
Mapper类:
@Mapper
public interface EmpMapper {
@Delete("delete from emp where id = #{id}")
public void delete(Integer id);
}
单元测试:
@SpringBootTest
class SpringbootMybatisQuickstartApplicationTests {
@Autowired
public EmpMapper empMapper;
@Test
public void deleteTest() {
empMapper.delete(17);
}
}
注意:
Mapper中的SQL语句“delete from emp where id = #{id}”,这里我们使用的#占位符,表示大括号中间的id为传递的参数,除了#占位符还有$占位符,但是它们有一些区别
★#占位符和$占位符的区别:
使用#占位符执行SQL时会将#{...}替换为?,生成预编译SQL,会自动设置参数值,使用时机:参数传递
使用$占位符执行SQL时会直接将参数拼接到SQL语句中,存在SQL注入问题.
SQL注入:
SQL注入就是通过操作输入的数据来修改事先定义好的SQL语句,以达到执行代码对服务器进行攻击的方法。
比如username='....' and password='.....',传递给password参数:' or '1' = '1 ,这样会得到username='....' and password='' or '1' = '1',总是为true,这样不知道用户名和密码也可以登录了,使用时机:对表名、列表进行动态设置时使用。
SQL预编译的好处:
1.性能更高:要想执行SQL语句,要连接上数据库,然后将SQL语句发送到数据库中,再进行SQL语法解析检查,优化SQL,编译SQL,执行SQL。但是在SQL语法解析检查,优化SQL,编译SQL过程中,为了提高效率,它会将优化编译后的SQL缓存起来,比如delete from emp where id = #{id},会缓存为delete from emp where id = ?,这样如果只有后面传递的id参数值改变,只需要在缓存中调用这一句即可,就只需要编译一次
2.更安全(防止SQL注入),通过参数赋值的方法不会更改原本的SQL语义
新增操作
新增:
代码演示:
Mapper类:
@Mapper
public interface EmpMapper {
@Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time)" +
"values(#{username},#{name},#{gender},#{image},#{job},#{entrydate},#{deptId},#{createTime},#{updateTime})")
public void insert(Emp emp);
}
单元测试:
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
void insertTest() {
Emp emp = new Emp();
emp.setUsername("han");
emp.setName("韩");
emp.setGender((short) 1);
emp.setJob((short) 1);
emp.setEntrydate(LocalDate.of(2010,1,1));
emp.setDeptId(1);
emp.setCreateTime(LocalDateTime.now());
emp.setUpdateTime(LocalDateTime.now());
empMapper.insert(emp);
}
}
新增(主键返回):
代码演示:
@Mapper
public interface EmpMapper {
@Options(useGeneratedKeys = true,keyProperty = "id")
@Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time)" +
"values(#{username},#{name},#{gender},#{image},#{job},#{entrydate},#{deptId},#{createTime},#{updateTime})")
public void insert(Emp emp);
}
更新操作:
更新:
代码演示:
Mapper:
@Mapper
public interface EmpMapper {
@Update("update emp set username= #{username},name= #{name},gender= #{gender},image= #{image}," +
"job= #{job},entrydate= #{entrydate},dept_id= #{deptId},update_time = #{updateTime} where id = #{id}")
public void update(Emp emp);
}
单元测试:
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
void updateTest() {
Emp emp = new Emp();
emp.setId(19);
emp.setUsername("han1");
emp.setName("韩1");
emp.setImage("1.jpg");
emp.setGender((short) 1);
emp.setJob((short) 1);
emp.setEntrydate(LocalDate.of(2010,1,1));
emp.setDeptId(1);
emp.setUpdateTime(LocalDateTime.now());
empMapper.update(emp);
}
}
查询操作:
根据id查询:
代码演示:
Mapper:
@Mapper
public interface EmpMapper {
@Select("select * from emp where id = #{id}")
public Emp getById(Integer id);
}
单元测试:
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
void getByIdTest() {
Emp emp = empMapper.getById(19);
System.out.println(emp);
}
}
注意:
这种编写方式得到的结果中有一部分属性值为null
这是因为mybatis的数据封装特性(如下)。
数据封装:
解决方案一:
给字段起别名,让别名与实体类属性保持一致
代码演示:
Mapper:
@Mapper
public interface EmpMapper {
/*
//与属性名不一致的字段会赋值为null
@Select("select * from emp where id = #{id}")
public Emp getById(Integer id);
*/
//解决方案一:给字段起别名,让别名与实体类属性保持一致
@Select("select id, username, password, name, gender, image, job, entrydate, " +
"dept_id deptId, create_time createTime, update_time updateTime" +
" from emp where id = #{id}")
public Emp getById(Integer id);
}
解决方案二:
通过@Results,@Result注解手动映射封装
代码演示:
Mapper:
@Mapper
public interface EmpMapper {
/*
//与属性名不一致的字段会赋值为null
@Select("select * from emp where id = #{id}")
public Emp getById(Integer id);
*/
/*
//解决方案一:给字段起别名,让别名与实体类属性保持一致
@Select("select id, username, password, name, gender, image, job, entrydate, " +
"dept_id deptId, create_time createTime, update_time updateTime" +
" from emp where id = #{id}")
public Emp getById(Integer id);
*/
//解决方案二:通过@Results,@Result注解手动映射封装
@Results({
@Result(column = "dept_id",property = "deptId"),
@Result(column = "create_time",property = "createTime"),
@Result(column = "update_time",property = "updateTime")
})
@Select("select * from emp where id = #{id}")
public Emp getById(Integer id);
}
解决方案三(推荐):
开启mybatis的驼峰命名自动映射开关
操作步骤:
只需要在mybatis配置文件application.properties中添加代码
#开启mybatis的驼峰命名自动映射开关(camel)
mybatis.configuration.map-underscore-to-camel-case=true
这样就可以使用最初的代码也能正确赋值
但是需要注意:数据库中的字段名要采用下划线命名,Java类中的属性名要采用驼峰命名法
条件查询:
根据姓名(模糊匹配)、性别、入职日期范围,来查找员工
代码演示:
Mapper:
@Mapper
public interface EmpMapper {
//条件查询
@Select("select * from emp where name like concat('%',#{name},'%') and gender = #{gender} and " +
"entrydate between #{begin} and #{end} order by update_time DESC")
public List<Emp> getList(String name, short gender, LocalDate begin, LocalDate end);
}
这里使用concat函数,是由于#{}会生成预编译SQL,编译后会转换成?,而?存在于单引号中会被认为是字符串,所以使用concat函数,来进行字符串的拼接。
单元测试:
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
void listTest() {
List<Emp> list = empMapper.getList("张", (short) 1, LocalDate.of(2010, 1, 1), LocalDate.of(2020, 1, 1));
System.out.println(list);
}
}
2.通过XML映射文件操作数据库:
除了可以使用注解来操作数据库,还可以使用XML映射文件。
代码演示:
xml文件:
<?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">
<!--上面为依赖部分,可在Mybatis中文网->入门 中的SQL部分截取-->
<mapper namespace="com.han.mapper.EmpMapper">
<!--resultType:单条记录所封装的类型-->
<select id="getList" resultType="com.han.pojo.Emp">
select * from emp where name like concat('%',#{name},'%') and gender = #{gender} and
entrydate between #{begin} and #{end} order by update_time DESC
</select>
</mapper>
Mapper:
@Mapper
public interface EmpMapper {
public List<Emp> getList(String name, short gender, LocalDate begin, LocalDate end);
}
单元测试:
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
void listTest() {
List<Emp> list = empMapper.getList("张", (short) 1, LocalDate.of(2010, 1, 1), LocalDate.of(2020, 1, 1));
System.out.println(list);
}
}
3.使用场景
使用Mybatis的注解,主要是来完成一些简单的增删改查功能/如果需要实现复杂的SQL功能,建议使用XML来配置映射语句。
动态SQL:
标签:
1.<if>(<where>、<set>)
查找(<where>):
更新(<set>):
2.<foreach>
<foreach>一般用于批量操作。
批量操作:
代码演示(批量删除):
XML文件:
<?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">
<!--上面为依赖部分,可在Mybatis中文网->入门 中的SQL部分截取-->
<mapper namespace="com.han.mapper.EmpMapper">
<!--resultType:单条记录所封装的类型-->
<delete id="deleteByIds">
delete
from emp
where id in /*(11,12,13)*/
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
</mapper>
Mapper:
@Mapper
public interface EmpMapper {
public void deleteByIds(List<Integer> ids);
}
单元测试:
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
void deleteByIdsTest() {
List<Integer> list = Arrays.asList(11, 12, 13);
empMapper.deleteByIds(list);
}
}
3.<sql>&<include>
代码演示:
如下面的XML文件代码,其中有一段动态SQL条件查询代码和一段根据id查询代码,它们中有大段的复用代码,如:
select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time
from emp
使用前代码:
<?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">
<!--上面为依赖部分,可在Mybatis中文网->入门 中的SQL部分截取-->
<mapper namespace="com.han.mapper.EmpMapper">
<!--resultType:单条记录所封装的类型-->
<select id="getById" resultType="com.han.pojo.Emp">
select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time
from emp
where id = #{id}
</select>
<!--resultType:单条记录所封装的类型-->
<select id="getList" resultType="com.han.pojo.Emp">
select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time
from emp
<where>
<if test="name != null">
name like concat('%', #{name}, '%')
</if>
<if test="gender != null">
and gender = #{gender}
</if>
<if test="begin != null and end != null">
and entrydate between #{begin} and #{end}
</if>
</where>
order by update_time DESC
</select>
</mapper>
使用<sql>和<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">
<!--上面为依赖部分,可在Mybatis中文网->入门 中的SQL部分截取-->
<mapper namespace="com.han.mapper.EmpMapper">
<sql id="commonSelect">
select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time
from emp
</sql>
<!--resultType:单条记录所封装的类型-->
<select id="getById" resultType="com.han.pojo.Emp">
<include refid="commonSelect"/>
where id = #{id}
</select>
<!--resultType:单条记录所封装的类型-->
<select id="getList" resultType="com.han.pojo.Emp">
<include refid="commonSelect"/>
<where>
<if test="name != null">
name like concat('%', #{name}, '%')
</if>
<if test="gender != null">
and gender = #{gender}
</if>
<if test="begin != null and end != null">
and entrydate between #{begin} and #{end}
</if>
</where>
order by update_time DESC
</select>
</mapper>