1,配置相关
我们上一期详细讲了一下使用注解来实现操作数据库的方式,我们今天使用xml来实现,有同学可能有疑问,使用注解挺方便呀,为啥还要注解呀,先来说一下注解我感觉挺麻烦的,但是我们后面要学动态SQL,注解就要要重写一遍xml到注解,更麻烦了,所以我们还是要学这个,xml和注解是可以共存的,所以不怕冲突;
我们先来准备工作:
根据数据库创建model字段,
@Data
public class userInfo {private Integer id;private String userName;private String password;private Integer deleteFlag;private Date createTime;private Date updateTime;
}
下一个这个插件:
写yml配置:
# 数据库连接配置datasource:url: jdbc:mysql://127.0.0.1:3306/book_test?characterEncoding=utf8&useSSL=falseusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:configuration:map-underscore-to-camel-case: true #配置驼峰⾃动转换log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印sql语句mapper-locations: classpath:mapper/**Mapper.xml
最后一行就是yml的配置了,这一行的意思是我们在resources路径下创建一个mapper包,里面放一个~~名后后缀是Mapper.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">
<mapper namespace="org.example.java_test_6_7.mapper.UserMapper"></mapper>
这个namespace对应的是自己项目名,后面.mapper.UserMapper对应的是这块
下面我们点击那个小蓝鸟,如果能跳转到小红鸟那,就说明我们跳转成功了;、
2,CRUD
1,增
@Mapper
public interface UserMapper {void insertUserInfo(UserInfo userInfo);
}
之后在mapper中写:(可以直接alt+enter生成)
<insert id="insertUserInfo"></insert>
注意这个要顶格写
<insert id="insertUserInfo">insert into user_info (user_name,password,delete_flag) values (#{userName},#{password},#{deleteFlag})</insert>
@SpringBootTest
class UserMapperTest {@Autowiredprivate UserMapper userMapper;@Testvoid insertUserInfo() {UserInfo userInfo = new UserInfo();userInfo.setUserName("yaoyu");userInfo.setPassword("12412");userInfo.setDeleteFlag(0);userMapper.insertUserInfo(userInfo);}
}
写测试类,测试通过,看看效果:
增添成功了;
2,删
把刚才添加的字段删了:
void deleteUserInfoById(Integer id);
<delete id="deleteUserInfoById">delete from user_info where id = #{id}</delete>
@Testvoid deleteUserInfoById() {userMapper.deleteUserInfoById(11);}
删除成功了。
3,查
UserInfo selectUserInfoBYId(Integer id);
<select id="selectUserInfoBYId" resultType="org.example.java_test_6_7.model.UserInfo">select * from user_info where id = #{id}</select>
@Testvoid selectUserInfoBYId() {UserInfo userInfo = userMapper.selectUserInfoBYId(1);System.out.println(userInfo);}
成功查询并拿到值了;
4,改
void updateUserInfoById(UserInfo userInfo);
<update id="updateUserInfoById">update user_info set user_name=#{userName}, password=#{password} ,delete_flag=#{deleteFlag} where id =#{id}</update>
@Testvoid updateUserInfoById() {UserInfo userInfo = new UserInfo();userInfo.setId(1);userInfo.setUserName("姚宇");userInfo.setPassword("3141");userInfo.setDeleteFlag(0);userMapper.updateUserInfoById(userInfo);}
OK了
5,java和数据库参数名对应不上
还是那三个方法
1,使用as
2,结果映射
3,驼峰自动转换
1和3跟注解是一模一样的就不讲了,这里我们讲结果映射:
UserInfo selectUserInfoBYId2(Integer id);
<resultMap id="base" type="org.example.java_test_6_7.model.UserInfo"><result column="delete_flag" property="deleteFlag"></result><result column="user_name" property="userName"></result><result column="create_time" property="createTime"></result><result column="update_time" property="updateTime"></result></resultMap>
@Testvoid selectUserInfoBYId2() {UserInfo userInfo = userMapper.selectUserInfoBYId2(1);System.out.println(userInfo);}
看结果:
OK的,这里我已经把驼峰自动转换关掉了;
3,多表查询
再来一个表,我们来弄model ;
@Data
public class BookInfo {private Integer id;private String bookName;private String author;private Integer count;private BigDecimal price;private String publish;private Integer status;private Date createTime;private Date updateTime;
}
来写一个多表查询语句
SELECT bo.id, bo.book_name, bo.author, uo.user_name, uo.`password` FROM user_info uo LEFT JOIN book_info bo ON bo.id = uo.id WHERE bo.id = 1
结果,那我们该怎么接收它呢,我们只需要再创建一个对象,对应这个字段的属性就好了;
@Data
public class Testselect {private Integer id;private String bookName;private String author;private String userName;private String password;
}
@Select("SELECT bo.id, bo.book_name, bo.author, uo.user_name, uo.`password` FROM user_info uo LEFT JOIN book_info bo ON bo.id = uo.id WHERE bo.id = #{id}")TestSelect selectUserInfoAndBookInfoById(Integer id);
@Testvoid selectUserInfoAndBookInfoById() {TestSelect testSelect = userMapper.selectUserInfoAndBookInfoById(1);System.out.println(testSelect);}
成功查到了
4,#{}和${}的区别
现在来正式讲一下$和#,
看一下#号的,这个 id后面是一个?下面表示它要填写的值,这个是预编译SQL用?来站位
看下用${}的:
@Select("select * from user_info where id = ${id}")UserInfo selectUserInfoById3(Integer id);
void selectUserInfoById3() {UserInfo userInfo = userMapper.selectUserInfoById3(1);System.out.println(userInfo);}
看到是没有任何占位符的,这个是即时SQL,
@Select("select * from user_info where user_name = ${userName}")UserInfo selectUserInfoById4(String userName);
来看看这个字符串的
@Testvoid testSelectUserInfoById4() {UserInfo userInfo = userMapper.selectUserInfoById4("姚宇");System.out.println(userInfo);}
发现报错了啊, 这说明是SQL语句出现了问题的,这个是因为是直接拼接的,没有引号,正常字符串是需要加引号的,
@Select("select * from user_info where user_name = '${userName}'")UserInfo selectUserInfoById4(String userName);
这下就通过了;
我们来具体说说区别:
主要区别就是即时SQL和预编译SQL的区别;
即时SQL呢,首先解析语法和语句,检验SQL是否正确,优化SQL语句,指定执行计划,之后执行返回结果;
1. 性能更⾼ 绝⼤多数情况下,某⼀条SQL语句可能会被反复调⽤执⾏,或者每次执⾏的时候只有个别的值不同(⽐ 如select的where⼦句值不同,update的set⼦句值不同,insert的values值不同).如果每次都需要 经过上⾯的语法解析,SQL优化、SQL编译等,则效率就明显不⾏了.
预编译SQL,编译⼀次之后会将编译后的SQL语句缓存起来,后⾯再次执⾏这条语句时,不会再次编译 (只是输⼊的参数不同),省去了解析优化等过程,以此来提⾼效率
1. 更安全(防⽌SQL注⼊) SQL注⼊:是通过操作输⼊的数据来修改事先定义好的SQL语句,以达到执⾏代码对服务器进⾏攻击的 ⽅法。
这个SQL注入是很严重的,那我们还有使用$的必要了吗,当然是有的,我们可以自己处理来预防SQ注入,还有一些功能是需要使用$而#是用不了的
1,排序
@Select("select * from book_info order by id ${sort}")List<BookInfo> selectBooInfoOrderById(String sort);
@Testvoid selectBooInfoOrderById() {userMapper.selectBooInfoOrderById("DESC");}
查到了;
为啥只能用$呢,因为sql中这个DESC是不加''号的,但是使用#的话会把我们传入的字符串自动加引号,但是sql语法是不对的;
2,like
@Select("select * from book_info where book_name like concat('%','${key}','%')")List<BookInfo> selectBooInfoLike(String key);
@Testvoid selectBooInfoLike() {userMapper.selectBooInfoLike("");}