目录
七、mybatis中Statement和PreparedStatement
一、搭建环境
1、引入jar包
mysql.jar
mybatis.jar
2、补全结构
com.hl.pojo
com.hl.mapper
mybatis-config.xml
sqlMapper.xml
3、测试类
加载myabtis核心配置文件,得到IO流 Resources
创建sqlSessionFactory SqlsessionFactoryBuilder
SqlSession session = sqlSessionFactory.openSession()
//获取mapper接口
调用mapper方法
//关闭流
二、核心配置文件
mybatis-config.xml
<Configuration>
//1、
<properties resource="db.properties"></properties>
//2、
<environments default="test">
<environment>
<transactionManager type="jdbc"/>
<dataSource type="POOLED">
<property name="url" value="" ></property>
username
password
driver
</dataSource>
</environment>
</environments>
//3、
<mappers>
<mapper resource="com/hl/mybatis01/mapper/AccountMapper.xml"/>
</mappers>
</Configuration>
补充完整代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<!--配置-->
<configuration>
<!--引入外部配置文件-->
<properties resource="db.properties"></properties>
<!--设置-->
<settings>
<!--下划线到驼峰式命名法自动映射-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!--类型起别名-->
<typeAliases>
<!--type="类型" alias="别名"-->
<!--针对每一个javabean分别起别名-->
<!-- <typeAlias type="com.hl.mybatis02.pojo.Student" alias="student"></typeAlias>-->
<!--统一起别名 别名默认为类名 不区分大小写-->
<package name="com.hl.mybatis02.pojo" />
</typeAliases>
<!--数据库环境-->
<environments default="dev">
<environment id="dev">
<transactionManager type="jdbc"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!--mappers映射器-->
<mappers>
<!--sqlMapper文件位置-->
<!-- <mapper resource="com/hl/mybatis02/mapper/GoodsMapper.xml"></mapper>-->
<!--指向mapper接口位置-->
<!-- <mapper class="com.hl.mybatis02.mapper.GoodsMapper"></mapper>-->
<!-- <mapper class="com.hl.mybatis02.mapper.ItemMapper"></mapper>-->
<!--指向mapper包的位置,统一扫描该包下所有接口-->
<package name="com.hl.mybatis02.mapper"/>
</mappers>
</configuration>
三、核心类 (测试类)
package com.hl.mybatis02;
import com.hl.mybatis02.mapper.StudentMapper;
import com.hl.mybatis02.pojo.Student;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
class Mybatis02ApplicationTests {
@Test
void contextLoads() throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
Student student = new Student(0,"admin",
"男","2020-1-5",1001,
null,null,null);
int num = studentMapper.insertStudent(student);
System.out.println("受影响的数据行数:"+num);
System.out.println("自增主键值为:"+student.getStudentId());
session.commit();
session.close();
}
@Test
void query() throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
List<Student> list = studentMapper.listAll();
System.out.println(list);
session.commit();
session.close();
}
}
四、缓存机制
缓存:多次执行相同dql,第一次走数据库表查询,第二次不再执行sql语句,直接从缓存区中获取数据。
一级缓存
默认已开启,可以直接使用,属于sqlSession级别的缓存。
二级缓存
默认没有开启,需要手动在mybatis配置文件中开启,属于sqlSessionFactory级别的缓存。
<!--开启当前命名空间的二级缓存-->
<cache></cache>
清理缓存
java代码清理缓存
session.clearCache();
sqlMapper文件清理缓存
useCache="false" 禁用当前sql语句的二级缓存
flushCache="true" 清空缓存区数据 导致一级缓存和二级缓存失效
<!-- //查询 下划线到驼峰式命名法的映射-->
<!-- public List<Student> listAll();
useCache="false" 禁用当前sql语句的二级缓存
flushCache="true" 清空缓存区数据 导致一级缓存和二级缓存失效
-->
<select id="listAll" resultType="student" useCache="false">
select * from student
</select>
示例:
@Test
void firstCache() throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
List<Student> list = studentMapper.listAll();
System.out.println(list);
//清理缓存
// session.clearCache();
List<Student> list2 = studentMapper.listAll();
System.out.println(list2);
session.commit();
session.close();
}
@Test
void secondCache() throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
List<Student> list = studentMapper.listAll();
System.out.println(list);
session.close();
SqlSession session2 = factory.openSession();
StudentMapper studentMapper2 = session2.getMapper(StudentMapper.class);
List<Student> list2 = studentMapper2.listAll();
System.out.println(list2);
session2.close();
SqlSession session3 = factory.openSession();
StudentMapper studentMapper3 = session3.getMapper(StudentMapper.class);
List<Student> list3 = studentMapper3.listAll();
System.out.println(list3);
session3.close();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--命名空间 唯一标识-->
<mapper namespace="com.hl.mybatis02.mapper.StudentMapper">
<!--开启当前命名空间的二级缓存-->
<cache></cache>
五、sqlMapper文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--命名空间 唯一标识-->
<mapper namespace="com.hl.mybatis02.mapper.StudentMapper">
<!-- 测试自增主键的生成
useGeneratedKeys="true" 获取自动生成的主键
keyProperty="studentId" 主键对应的属性名
-->
<!-- public int insertStudent(Student student);-->
<insert id="insertStudent" useGeneratedKeys="true" keyProperty="studentId">
insert into student(name,gender,birth_date,class_id,enrollment_date)
values (#{name},#{gender},#{birthDate},#{classId},now())
</insert>
<!-- //查询 下划线到驼峰式命名法的映射-->
<!-- public List<Student> listAll(); -->
<select id="listAll" resultType="student">
select * from student
</select>
</mapper>
六、单参数和多参数的传递
6.1取别名
Mapper接口
测试新增主键的生成,对象也可以取别名
多个参数,给参数起别名,将不再使用系统默认的arg0、arg1等
单个参数,也可以取别名,之后#{里面不能随便写了只能写别名}
//测试新增主键的生成,对象也可以取别名
public int insertStudent(@Param("stu")Student student);
// 查询 下划线到驼峰式命名法的映射
public List<Student> listAll();
// 多个参数,给参数起别名,将不再使用系统默认的arg0、arg1等
public List<Student> findStudent(@Param("name")String name, @Param("phone")String phone);
// 单个参数,也可以取别名,之后#{里面不能随便写了只能写别名}
public Student findStudentById(int id);
6.2 测试新增返回自增主键
Mapper映射文件里面
测试新增主键的生成:
useGeneratedKeys="true" 获取自动生成的主键
keyProperty="studentId" 主键对应的属性名
对象在接口中取别名之后,调用对象的属性名,应该写成:别名.属性名
<!--命名空间 唯一标识-->
<mapper namespace="com.hl.mybatis02.mapper.StudentMapper">
<!-- //测试新增主键的生成
useGeneratedKeys="true" 获取自动生成的主键
keyProperty="studentId" 主键对应的属性名
对象在接口中取别名之后,调用对象的属性名,应该写成:别名.属性名
-->
<!-- public int insertStudent(Student student);-->
<insert id="insertStudent" useGeneratedKeys="true" keyProperty="studentId">
insert into student(name,gender,birth_date,class_id,enrollment_date)
values (#{stu.name},#{stu.gender},#{stu.birthDate},#{stu.classId},now())
</insert>
<!-- // 查询 下划线到驼峰式命名法的映射-->
<!-- public List<Student> listAll();-->
<select id="listAll" resultType="student">
select * from student
</select>
<!-- // 多个参数-->
<!-- public List<Student> findStudent(String name,String phone);-->
<!-- select * from student where name = #{arg0} and phone = #{arg1}-->
<!-- <select id="findStudent" resultType="student">-->
<!-- select * from student where name = #{name} and phone = #{phone}-->
<!-- </select>-->
<!-- 模糊查询-->
<select id="findStudent" resultType="student">
select * from student where name like concat('%',#{name},'%') or phone = #{phone}
</select>
<!-- 单个参数-其实识别的不是id是里面的第一个参数-->
<!-- public List<Student> findStudentByName(int id);-->
<select id="findStudentById" resultType="student">
select * from student where student_id = #{iaa}
</select>
</mapper>
七、mybatis中Statement和PreparedStatement
statementType="PREPARED" 默认 使用预编译(防止sql注入)
statementType="STATEMENT" sql 字符串拼接
statementType="CALLABLE" 调用存储过程
<!-- //多个参数
public List<Student> findStudents(String name,String phone);
statementType="PREPARED" 默认 使用预编译
STATEMENT sql字符串拼接
CALLABLE 调用存储过程
-->
<select id="findStudents" resultType="Student">
select * from student where name like concat('%',#{name},'%') or phone=#{phone}
</select>
<!--public Student findStudentById(int id);-->
<select id="findStudentById" resultType="Student" statementType="STATEMENT">
select * from student where student_id=${id}
</select>
作业
1、掌握环境搭建
2、掌握单个参数、多个参数的增删改查
3、缓存机制 (一级、二级缓存特点)
4、mybatis核心配置文件 常见的配置项
5、#和$的区别?(重点)
-
预处理方式不同
-
#{}
:使用预编译(PreparedStatement)方式,参数会被替换为?
,能有效防止 SQL 注入 -
${}
:使用字符串拼接方式,直接将参数值替换到 SQL 语句中
-
-
安全性
-
#{}
:安全,能防止 SQL 注入 -
${}
:不安全,存在 SQL 注入风险
-
-
参数处理
-
#{}
:会对传入的参数自动添加引号(字符串类型) -
${}
:原样替换,不会添加引号
-
6、sqlMapper文件新增返回自增主键
见6.2
7、预习 动态sql和多表关联映射
动态sql
-
if
-
choose (when, otherwise)
-
trim (where, set)
-
foreach
高级结果映射
MyBatis 创建时的一个思想是:数据库不可能永远是你所想或所需的那个样子。 我们希望每个数据库都具备良好的第三范式或 BCNF 范式,可惜它们并不都是那样。 如果能有一种数据库映射模式,完美适配所有的应用程序,那就太好了,但可惜也没有。 而 ResultMap 就是 MyBatis 对这个问题的答案。
比如,我们如何映射下面这个语句?
<!-- 非常复杂的语句 -->
<select id="selectBlogDetails" resultMap="detailedBlogResultMap">
select
B.id as blog_id,
B.title as blog_title,
B.author_id as blog_author_id,
A.id as author_id,
A.username as author_username,
A.password as author_password,
A.email as author_email,
A.bio as author_bio,
A.favourite_section as author_favourite_section,
P.id as post_id,
P.blog_id as post_blog_id,
P.author_id as post_author_id,
P.created_on as post_created_on,
P.section as post_section,
P.subject as post_subject,
P.draft as draft,
P.body as post_body,
C.id as comment_id,
C.post_id as comment_post_id,
C.name as comment_name,
C.comment as comment_text,
T.id as tag_id,
T.name as tag_name
from Blog B
left outer join Author A on B.author_id = A.id
left outer join Post P on B.id = P.blog_id
left outer join Comment C on P.id = C.post_id
left outer join Post_Tag PT on PT.post_id = P.id
left outer join Tag T on PT.tag_id = T.id
where B.id = #{id}
</select>
你可能想把它映射到一个智能的对象模型,这个对象表示了一篇博客,它由某位作者所写,有很多的博文,每篇博文有零或多条的评论和标签。 我们先来看看下面这个完整的例子,它是一个非常复杂的结果映射(假设作者,博客,博文,评论和标签都是类型别名)。 不用紧张,我们会一步一步地来说明。虽然它看起来令人望而生畏,但其实非常简单。
<!-- 非常复杂的结果映射 -->
<resultMap id="detailedBlogResultMap" type="Blog">
<constructor>
<idArg column="blog_id" javaType="int"/>
</constructor>
<result property="title" column="blog_title"/>
<association property="author" javaType="Author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
<result property="password" column="author_password"/>
<result property="email" column="author_email"/>
<result property="bio" column="author_bio"/>
<result property="favouriteSection" column="author_favourite_section"/>
</association>
<collection property="posts" ofType="Post">
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
<association property="author" javaType="Author"/>
<collection property="comments" ofType="Comment">
<id property="id" column="comment_id"/>
</collection>
<collection property="tags" ofType="Tag" >
<id property="id" column="tag_id"/>
</collection>
<discriminator javaType="int" column="draft">
<case value="1" resultType="DraftPost"/>
</discriminator>
</collection>
</resultMap>