笔记一:(尚硅谷)2022 版 MyBatis 教程笔记一
mapper 接口中方法的定义:
mapper 映射文件中 sql 语句的编写,三种方法都可以实现:
select * from t_user where username like '%#{username}%'
,即将 #{}
这种占位符赋值获取参数值的放在 单引号 里面;测试类的编写:
//测试模糊查询
@Test
public void testGetUserByLike(){
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
SQLMapper mapper = sqlSession.getMapper(SQLMapper.class);
List<User> list = mapper.getUserByLike("王");
System.out.println(list);
}
结果:
mapper 接口中方法的定义:
mapper 映射文件中 sql 语句的编写,这里只能用 ${}
这种字符串拼接的方法来实现:
注意:
delete from t_user where id in (#{ids})
;#{}
这种占位符赋值的方式,会自动给参数加上单引号,从报错信息可以看出来,我想要一个 Double 类型的数据,你给我一个 ‘1,2,4’ 的字符串:测试类的编写:
//测试批量删除
@Test
public void testDeleteMore(){
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
SQLMapper mapper = sqlSession.getMapper(SQLMapper.class);
int result = mapper.deleteMore("1,2,4");
System.out.println(result);
}
结果:
mapper 接口和 mapper 映射文件的实现:
#{}
占位符赋值的方法在传入参数的时候会自动为参数加上单引号,所以这里不能用这种方法来实现设置表名的方法,因为 sql 语句不识别带单引号的表名Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''t_user'' at line 1
比如我们有下面的两个表,在新建一个班级信息的时候,我们需要获取该班级添加到数据库之后的自增主键,才能在该班级相对应的学生表中设置班级 Id 这一项。
JDBC 实现的时候有一个方法 ResultSet resultSet = ps.getGeneratedKeys();
,可以获取自增的主键,那么 MyBatis 如何实现呢?
MyBatis 的实现,通过在 mapper 映射文件中设置 useGeneratedKeys="true"
属性:
mapper 接口中方法的定义:
mapper 映射文件中 sql 语句的编写:
useGeneratedKeys
表示返回集我们要用到自增的主键这个值;keyProperty
表示我们将自增的主键赋给传回的结果集中的哪一个属性上,主键值不能作为方法的返回值(因为增删改的返回值是固定的,就是受影响的行数),只能将主键放在我们传回来的参数的某一个属性中,所以这里只能将自增 id 放在用户的 id 属性中传回来。测试类的编写:
//测试插入一条数据,是否能获取到数据库为它自动生成的主键
@Test
public void testInsertUser(){
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
SQLMapper mapper = sqlSession.getMapper(SQLMapper.class);
User user = new User(null, "张三", "123", 23, "男", "[email protected]");
mapper.insertUser(user);
System.out.println(user);
}
结果,我们插入了一个 null 值,但是返回的对象中有主键 id =13:
在 mapper 映射文件中,查询 sql 语句的时候,为字段起别名,即 select eid,emp_name empName,age,sex,email,did from t_emp
:
在 mybatis-config.xml
文件中设置全局配置,只能将自定义属性的_自动映射为驼峰
<setting name="mapUnderscoreToCamelCase" value="true"/>
javaType
:和 Emp 这个类中定义的那个 Dept 属性对应,反射获取能和当前想要的属性对应上的实现类,然后将那个类赋值给当前 Emp 的 Dept 属性。分步查询的第二步:通过部门 id 获取部门信息(有没有发现这个方法就算不作为分步查询的第二步,也可以作为根据部门 id 查询部门信息的一个方法)
那么在 Emp 的映射文件中如何将两步查询到的信息关联起来呢,只能根据部门 id :
解释:
分步查询的优点:
lazyLoadingEnabled
:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载;aggressiveLazyLoading
:当开启时,任何方法的调用都会加载该对象的所有属性。 否则,每个属性会按需加载。mybatis-config.xml
中配置
,这个时候就相当于开启了延迟加载,此时就可以实现按需加载,获取的数据是什么,就只会执行相应的 sql;fetchType
属性设置当前的分步查询是否使用延迟加载,fetchType=“lazy(延迟加载)|eager(立即加载)”;可以看出,开启延迟加载之后,不需要的 sql 语句就不会预编译和执行,节省资源。
ofType
:表示该属性所对应的集合中存储数据的类型,就是在 Dept 类中的属性集合的泛型中定义的那个属性。
目的:设置字段和属性的映射关系
通过 ofType 获取集合里存储的数据的类型,然后根据这个类型获取相应的属性(MyBatis内部操作),映射关系建立后,创建该类型的对象(此处是每一个 emp 对象),最后把每一个对象放在集合中(List)
column
:分步查询条件。分步查询的第二步:通过部门 id 去员工表中查询所有的员工信息
where ename = '?' and age = ? and sex = "?"
1、if:
2、where:
3、trim:
prefix|suffix
:将 trim 标签中内容前面或后面添加指定内容suffixOverrides|prefixOverrides
:将 trim 标签中内容前面或后面去掉指定内容判断条件中判断的是属性名(从后台预编译的 sql 语句中传过去的),语句中的应该是 字段名 = #{属性名}
,前面是放到数据库中执行的语句,后面访问的是实体类中的属性名字;
这里为什么 where 后面要加 1=1
这个条件呢?
当 where 后面的所有语句都为空的时候,多出来的 where 关键字会报错;
或者,当 where 后面 empName 条件为空字符串的时候,这个时候 where 关键字就会和 and 关键字或者 or 关键字连接在一起,造成错误。
where
字段;and/or
结尾的时候,那么就将其去掉。4、choose、when、otherwise,相当于if…else if…else
注意:
实现根据某一个条件来实现查询员工信息:
测试一下,这边所有的条件啊都设置为空:
//测试choose、when、otherwise
@Test
public void testGetEmpByChoose(){
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class);
List<Emp> list = mapper.getEmpByChoose(new Emp(null, "", null, "", ""));
System.out.println(list);
}
那么结果应该就是根据 did=1
这个条件来查询,查询出来的应该是赵六,我们没有进行表连接,所以查询出来的员工信息不包含 dept 属性:
5、foreach:
比如我们想要实现 批量删除或者批量添加 功能,那么我们的 sql 语句应该是:
where id in (1,2,3)
where id = 1 or id = 2 or id = 3
insert into t_user values(?,?,?),(?,?,?),(?,?,?)
@Param("eids")
注解来规定底层 map 容器中放置数组的键值对的 key 的名称;对象.属性
的方式提取属性值;测试类:
//测试批量删除
@Test
public void testDeleteMoreByArray(){
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class);
int result = mapper.deleteMoreByArray(new Integer[]{6, 7, 8});
System.out.println(result);
}
//测试批量插入
@Test
public void testInsertMoreByList(){
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class);
Emp emp1 = new Emp(null,"a1",23,"男","[email protected]");
Emp emp2 = new Emp(null,"a2",23,"男","[email protected]");
Emp emp3 = new Emp(null,"a3",23,"男","[email protected]");
List<Emp> emps = Arrays.asList(emp1, emp2, emp3);
System.out.println(mapper.insertMoreByList(emps));
}
上面 mapper 映射文件中三种 sql 语句编写方式对应的 sql 语句,输出的结果为:
注意:
#{}
这种占位符没事儿呢,之前不是说批量删除不能用 占位符赋值方法吗?6、sql 标签
eid,emp_name,age,sex,email
一级缓存是 SqlSession 级别的,通过同一个 SqlSession 查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问。
测试一级缓存:
先看一下对于 mapper 接口的不同的 mapper 实现对象,这个时候是同一个 SqlSession:
//测试一级缓存,默认开启的
@Test
public void testOneCache(){
SqlSession sqlSession1 = SqlSessionUtils.getSqlSession();
CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
Emp emp1 = mapper1.getEmpByEid(1);
System.out.println(emp1);
CacheMapper mapper2 = sqlSession1.getMapper(CacheMapper.class);
Emp emp2 = mapper2.getEmpByEid(1);
System.out.println(emp2);
}
//测试一级缓存,默认开启的
@Test
public void testOneCache(){
SqlSession sqlSession1 = SqlSessionUtils.getSqlSession();
CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
Emp emp1 = mapper1.getEmpByEid(1);
System.out.println(emp1);
SqlSession sqlSession2 = SqlSessionUtils.getSqlSession();
CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class);
Emp emp2 = mapper2.getEmpByEid(1);
System.out.println(emp2);
}
//测试一级缓存,默认开启的
@Test
public void testOneCache(){
SqlSession sqlSession1 = SqlSessionUtils.getSqlSession();
CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
Emp emp1 = mapper1.getEmpByEid(1);
System.out.println(emp1);
mapper1.insertEmp(new Emp(null,"abc",23,"男","[email protected]"));
// sqlSession1.clearCache();
Emp emp2 = mapper1.getEmpByEid(1);
System.out.println(emp2);
}
二级缓存是 SqlSessionFactory 级别,通过同一个 SqlSessionFactory 创建的 SqlSession 查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取。
在核心配置文件中,设置全局配置属性 cacheEnabled=“true”,默认为 true,不需要设置;
在映射文件中设置标签
;
二级缓存必须在 SqlSession 关闭或提交之后有效;
查询的数据所转换的实体类类型必须实现序列化的接口。
测试二级缓存,同一个 sqlSessionFactory ,但是不同的 sqlSession 试一下,注意需要进行 sqlSession 提交或者关闭才生效:
//测试二级缓存
@Test
public void testTwoCache(){
try {
//加载核心配置文件为字节输入流,根据输入流创建一个 sqlSessionFactory,从中获取一个 sqlSession
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession1 = sqlSessionFactory.openSession(true);//打开事务自动提交功能
CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
System.out.println(mapper1.getEmpByEid(1));
sqlSession1.close();
SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class);
System.out.println(mapper2.getEmpByEid(1));
} catch (IOException e) {
e.printStackTrace();
}
}
注意:
sqlSessionFactory.openSession(true);
是开启了事务的自动提交而不是 sqlSession 的提交。看笔记(打印信息更多了)
<dependencies>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.10version>
dependency>
<build>
<plugins>
<plugin>
<groupId>org.mybatis.generatorgroupId>
<artifactId>mybatis-generator-maven-pluginartifactId>
<version>1.3.0version>
<dependencies>
<dependency>
<groupId>org.mybatis.generatorgroupId>
<artifactId>mybatis-generator-coreartifactId>
<version>1.3.2version>
dependency>
<dependency>
<groupId>com.mchangegroupId>
<artifactId>c3p0artifactId>
<version>0.9.2version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.29version>
dependency>
dependencies>
plugin>
plugins>
build>
mybatis-config.xml
;generatorConfig.xml
(修改数据库名称和密码、修改包名、修改对应的数据库表名)
DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="DB2Tables" targetRuntime="MyBatis3">
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis"
userId="root"
password="abc123">
jdbcConnection>
<javaModelGenerator targetPackage="com.atguigu.mybatis.pojo"
targetProject=".\src\main\java">
<property name="enableSubPackages" value="true"/>
<property name="trimStrings" value="true"/>
javaModelGenerator>
<sqlMapGenerator targetPackage="com.atguigu.mybatis.mapper"
targetProject=".\src\main\resources">
<property name="enableSubPackages" value="true"/>
sqlMapGenerator>
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.atguigu.mybatis.mapper" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true"/>
javaClientGenerator>
<table tableName="t_emp" domainObjectName="Emp"/>
<table tableName="t_dept" domainObjectName="Dept"/>
context>
generatorConfiguration>
解释:
@Test
public void testMBG(){
try {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
//查询所有数据
// List list = mapper.selectByExample(null);
// list.forEach(emp -> System.out.println(emp));
//根据条件查询
// EmpExample example = new EmpExample();
// example.createCriteria().andEmpNameEqualTo("张三").andAgeGreaterThan(20);
// example.or().andDidIsNotNull();
// List list = mapper.selectByExample(example);
// list.forEach(emp -> System.out.println(emp));
//有选择性的修改,那么属性值为 null 的值对应的字段不会进行修改
mapper.updateByPrimaryKeySelective(new Emp(1,"admin",22,null,"[email protected]",3));
} catch (IOException e) {
e.printStackTrace();
}
}
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelperartifactId>
<version>5.2.0version>
dependency>
mybatis-config.xml
种配置分页插件<plugins>
<!--设置分页插件-->
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
limit index,pageSize
测试:
@Test
public void testPageHelper(){
try {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
//开启分页功能
// Page
PageHelper.startPage(2, 4);
//查询所有的数据
List<Emp> list = mapper.selectByExample(null);
//展示导航分页的页码数,一般都是奇数
PageInfo<Emp> page = new PageInfo<>(list, 5);
System.out.println(page);
} catch (IOException e) {
e.printStackTrace();
}
}
Page
,这个函数返回的 page 是包含少量信息的 page数据;PageInfo page = new PageInfo<>(list, 5);
,展示非常详细的 pageInfo 信息,里面包含 page 信息。常用数据: