Mybatis

——————mybatis 官方文档

==注意注意:增删改sql一定要committ(测试类)==

简单的增删改查操作CRUD

只需要改下面这三个方面就ok了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//UserMapper接口定义方法
package com.kuang.utils.dao;

import com.kuang.utils.eneity.User;
import java.util.List;

public interface UserMapper {
//先定义个查询所有用户的方法;
List<User> findAllUser();

User findUserbyid(int id);

int addUser(User user);

int updateUser(User user);

int deleteUser(int id);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<!--UserMapper.xml在这里写sql语句

复习增删改查的sql;
insert 增
delete 删(前面标签仍然是update)
update 改(没有返回值——没有resultype)
select 查
-->
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--这里mapper的空间对应创建的持久层接口;-->
<mapper namespace="com.kuang.utils.dao.UserMapper">
<!--比如说,现在要写个查询语句,id就对应接口的方法;-->
<!--resultType: 查询的结果类型-->

<select id="findAllUser" resultType="com.kuang.utils.eneity.User">
select * from day2021_9_6_studyMybatis_db.user
</select>

<select id="findUserbyid" resultType="com.kuang.utils.eneity.User">
select * from day2021_9_6_studyMybatis_db.user where id= #{id}
</select>

<!--parameterType对应参数类型从哪儿来;-->
<insert id="addUser" parameterType="com.kuang.utils.eneity.User" >
insert into day2021_9_6_studyMybatis_db.user(id,name,password)values (#{id},#{name},#{password});
</insert>

<update id="updateUser" parameterType="com.kuang.utils.eneity.User">
update day2021_9_6_studyMybatis_db.user set name=#{name},password=#{password} where id=#{id};
</update>

<update id="deleteUser" parameterType="_int">
delete from day2021_9_6_studyMybatis_db.user where id=#{id};
</update>
</mapper>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
//编写测试类(junit框架)
//一定要注意增删改要提交事务——————sqlsession.commit
package com.kuang.utils.dao;

import com.kuang.utils.eneity.User;
import com.kuang.utils.mybatisutils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;

public class UserMapperTest {
//测试查询所有用户的方法;
@Test
public void testFindAllUser(){
//首先,回去SqlSession对象;
SqlSession sqlSession = mybatisutils.getSqlSession();
//第一种方式;getmapper;
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> allUser = userMapper.findAllUser();
//遍历结果;
for (User user : allUser) {
System.out.println(user);
}
//关闭sqlSession;
sqlSession.close();
}

@Test
public void testFindUserById(){
SqlSession sqlSession = mybatisutils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User userbyid = mapper.findUserbyid(1);
System.out.println(userbyid);
//关闭sqlSession;
sqlSession.close();
}

@Test
public void tsetaddUser(){
SqlSession sqlSession = mybatisutils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.addUser(new User(6,"小刘","1456"));
sqlSession.commit();
sqlSession.close();
}
@Test
public void tsetupdateUser(){
SqlSession sqlSession = mybatisutils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.updateUser(new User(4,"小洋","22456"));
sqlSession.commit();
sqlSession.close();
}
@Test
public void tsetdeleteUser(){
SqlSession sqlSession = mybatisutils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.deleteUser(1);
sqlSession.commit();
sqlSession.close();
}
}

使用Map

上面在写sql语句的时候,由于参数或返回值要对应实体类;那么每次sql语句都要准确地对应实体类的属性;

万能的Map来了,那么每次写SQL语句的时候,就可以直接参数写map的键;(而且使用map的好处就是,键不能重复) ; 然后再实际调用的时候,再对map的键进行赋值

1
2
3
4
5
6
7
//在UserMapper中定义方法;
//万能的Map;
int allPowerfulMapToAdd(Map<String,Object> map);

//Map 查询;
User allPowerfulMapToGet(Map<String,Object> map);

1
2
3
4
5
6
7
8
9
10
11
12
<!--在UserMapper.xml配置文件的mapper标签内写添加语句;
添加用户;注意这里的参数就是 map的键
-->
<insert id="allPowerfulMapToAdd" parameterType="map">
insert into day2021_9_6_studyMybatis_db.user(id,name,password)values (#{useId},#{Name},#{password});
</insert>


<select id="allPowerfulMapToGet" parameterType="map" resultType="com.xiaozhi.eneity.User">
select * from user where id= #{id} and name=#{name};
</select>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@Test
public void testAddUserForMap(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);

//这里新建一个Map;可以存多个用户对象;然后调用添加方法;
HashMap<String, Object> map = new HashMap<String, Object>();
//向map的键赋值;
map.put("useId",7);
map.put("Name","小鸡战士");
map.put("password","55555");
int i = mapper.allPowerfulMapToAdd(map);
if(i>0){
System.out.println("添加数据条数=>"+i);
}
//提交事务;
sqlSession.commit();
//关闭资源;
sqlSession.close();
}

@Test
public void testSelUserForMap(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("id",5);
map.put("name","特工");
User user = mapper.allPowerfulMapToGet(map);
System.out.println(user);
//提交事务;
sqlSession.commit();
//关闭资源;
sqlSession.close();
}

模糊查询

1
2
3
//在UserMapper中声明方法;
//模糊查询;
List<User> findUserByName(String name);
1
2
3
4
5
在UserMapper.xml中添加文件;
<select id="findUserByName" resultType="com.xiaozhi.eneity.User" parameterType="String">
select * from user where name like #{name};
</select>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//测试类调用方法;传参时使用%通配符匹配;
@Test
public void testFindByName(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userByName = mapper.findUserByName("%鱼%");
for (User user : userByName) {
System.out.println(user);
}

//关闭资源;
sqlSession.close();
}

在这里插入图片描述

1
2
3
4
5
6
<!--还可以在sql拼接时 使用通配符
或者在SQL语句中拼接 通配符-->
<select id="findUserByName" resultType="com.xiaozhi.eneity.User" parameterType="String">
select * from user where name like "%"#{name}"%";
</select>

配置优化学习

MyBatis 可以配置成适应多种环境,尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。

比如说要写个别的环境;想要使用它,就把environments标签的 default指向那个环境.

transactionManager :事务管理器 ;
有两种类型事务管理器([JDBC和MANAGED);默认使用JDBC; 这个MANAGED不常用;

dataSource 数据源 ;用于连接数据库.

有三种数据源:

UNPOOLED不使用数据库连接池;

POOLED (默认) 使用数据库连接池;

JNDI应用服务器类使用;

属性优化(properties)

还记着之前使用连接池时,读取properties文件的配置;还是比较方便的;
数据库连接池学习—DBCP;C3P0连接池

可以在resources目录下编写db.properties文件

在核心配置文件mybatis-config.xml配置文件中(引入)数据源;
注意properties标签要写在核心配置文件的首位;
在环境标签下的dataSource下就能获取到了;就像之前那个el表达式的格式一样;${属性名} 就可读取到了;而且,由于配置文件是写在外部的,可以动态修改;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--核心配置文件-->
<configuration>

<!--引入外部配置文件-->
<properties resource="db.properties"/>


<!--environments:配置的环境,可以配置多个-->
<!--想要切换别的环境,就修改默认加载的环境-->
<environments default="development">
<environment id="development">
<!--transactionManager:事务管理;这里默认使用JDBC-->
<transactionManager type="JDBC"/>
<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>

<!--每个mapper都需要在核心配置文件中注册-->
<mappers>
<mapper resource="com/xiaozhi/dao/UserMapper.xml"/>
</mappers>
</configuration>

还可以在标签体添加属性;有时可能外部配置文件中的属性要被其他的文件读取;那么把这些属性写在外部配置文件就不合适;将这个文件需要的属性添加到properties的标签体即可;

1
2
3
4
<properties resource="db.properties">
<property name="password" value="13245"/>
<property name="df" value="deqwq"/>
</properties>

需要注意的是:当外部配置文件和properties内定义的属性出现重名的情况;默认优先使用的是引入的外部文件内容!!!

别名优化(typeAliases)

使用别名可以减少代码的冗余,看起来也比较清晰明了

注意IDEA给出的提示,标签不能乱放位置

image-20211213203721799

1
2
3
4
5
<!--配置别名
1.为实体类User配置别名-->
<typeAliases>
<typeAlias type="com.kuang.utils.eneity.User" alias="User"/>
</typeAliases>

也可以指定一个包名,mybatis会自动将这个包的实体类的类名定义为他的默认别名,首字母小写!

比如说配置扫描存放实例类User的包eneity;那么User类的别名就是user

1
2
3
4
<!--2.使用包扫描的方式 ;实体类的别名默认为首字母小写的类名-->
<typeAliases>
<package name="com.kuang.utils.eneity"/>
</typeAliases>

==实体类较少用第一种(可以DIY别名) 实体类较多用第二种==

在使用扫描包的方式(第二种);之后;如果不想用默认的小写开头的别名,
还可以直接在实体类使用注解的方式;
比如说在**实体类User上面使用注解****@Alias("别名")**
注意导包为import org.apache.ibatis.type.Alias.

1
2
3
4
5
import org.apache.ibatis.type.Alias;
//实体类User;
//使用注解的方式配置别名;
@Alias("myUser")
//在UserMapper.xml那个查询语句处使用注解写的别名;

设置(settings)–了解

需要注意的几个;

mapUnderscoreToCamelCase

1
2
是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。
默认为false关闭状态的;

比如说在数据库写了个字段名为 res_time;然后在java实体类里面用的是驼峰命名法resTime;把这个属性一设置,它就能成功映射到对应的属性了;

logImpl; 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。

1
SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING

在下半部分会用到日志;

cacheEnabled :全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。默认是开启的;
lazyLoadingEnabled 懒加载的全局开关。开启后所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。

映射器(mappers)

==注意:not known to the MapperRegistry.它说没有找到mapper映射可能是pom.xml maven依赖那里build没有配置rescourse==

之前刚开始配好mybatis后,运行测试代码;出现的mapper映射问题;

1
org.apache.ibatis.binding.BindingException: Type interface com.xiaozhi.dao.UserDao is not known to the MapperRegistry.

MapperRegistry :注册绑定mapper文件;

需要在核心配置文件mybatis-config.xml,把UserMapper.xml添加注册;

主要有三种方式配置mapper:

==(1)使用相对于类路径的资源引用;(最推荐使用 首选)==

1
2
3
<mappers>
<mapper resource="com/kuang/utils/dao/UserMapper.xml"/>
</mappers>

(2)使用映射器接口实现类的完全限定类名(了解)

但使用这种方式有时可能会出错;
错误一:如果说把Mapper.xml配置文件放在了别的包下;会出现找不到mapper的错误;
错误二:例如说有时候不注意把接口命名为UserDao;而映射文件还是UserMapper.xml,这也会出现找不到mapper的情况.
name怎么避免呢?==>
保证接口和xml配置文件在同一个包下;且是同名的.

1
2
3
<mappers>
<mapper class="com.xiaozhi.dao.UserMapper"/>
</mappers>

(3)进行包扫描;将包下的所有映射器接口实现注册;**(了解)
包扫描时也要保证接口和xml配置文件在同一个包下;且是同名的.

1
2
3
<mappers>
<package name="com.xiaozhi.dao.UserMapper"/>
</mappers>

生命周期,作用域

生命周期与作用域使用不当会导致并发问题;

img

SqlSessionFactoryBuilder在 创建sqlsessionfactory后就不需要了;

SqlSessionFactory;可以理解为一个数据库连接池; 被创建就应在应用的运行期一直存在,不需要销毁或重新创建实例。适用于应用作用域,可使用单例模式或者静态单例模式.

SqlSession 是连接到数据库的一个请求,它的实例不是线程安全的,所以无法被共享,作用于请求或方法的作用域;用完就关闭,防止资源浪费.
SqlSession可以去对应多个映射.

ResultMap 结果集映射

(如果实体类的属性和数据库的字段名不一致——–执行根据Id查询的方法;查询出的姓名为null,空值;

解决方式一:在SQL查询语句使用别名;

1
2
3
4
<!--解决方式1:在sql字段使用别名-->
<select id="findUserById" resultType="myUser" parameterType="_int">
select id,name as username,password from user where id= #{id};
</select>

解决方式二: 通过结果集映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!--将实体类属性和数据库字段对应起来
相同的可省略不写
id——名字 type——映射成谁——结果集(实体类)
-->
<resultMap id="UserMap" type="myUser">
<result property="id" column="id"/>
<result property="username" column="name"/>
<result property="password" column="password"/>
</resultMap>

<!--返回值类型的 UserMap(和上面id一样) 即可引用上面的设置-->
<select id="findUserById" resultMap="UserMap" parameterType="_int">
select * from user where id= #{id};
</select>

日志

在运行项目的时候,出现异常就需要依次排除错误,以前常用的System.out控制台输出或者debug调试显然是比较麻烦的;

使用日志无疑是一个比较好的方式.

  • SLF4J
  • Apache Commons Logging
  • Log4j 2
  • Log4j 【掌握】
  • JDK logging
  • STDOUT_LOGGING【掌握】

9.1使用标准日志工厂
==注意,setting中设置日志时,name必须为logImpl;==

比如说,使用标准日志 STDOUT_LOGGING
在mybatis-config.xml中设置开启日志;

1
2
3
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

测试执行根据Id查询的方法;

在这里插入图片描述

log4j 日志

可以控制打印的日志输出位置;控制台,文件或者GUI组件;
可控制输出格式;可定义日志的级别;可在配置文件中单独配置.

先把log4j的包导入到项目中;
pom.xml配置文件导入依赖;这里用的是log4j-1.2.17版本

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

resources目录 下创建log4j.properties资源文件;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file

#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
#输出的日志格式
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/xiaozhi.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

mybatis-config.xml 设置日志;

1
2
3
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>

测试运行;比之前的标准日志更详细;

在这里插入图片描述

使用
在需要使用log4j的类;导入包;

import org.apache.log4j.Logger;
创建日志对象;参数为当前类的class;

1
static Logger logger= Logger.getLogger(UserDaoTest.class);

尝试在测试类中创建一个方法;调用日志方法;

1
2
3
4
5
6
7
//static  Logger logger= Logger.getLogger(UserDaoTest.class);
@Test
public void testForLog(){
logger.info("info:info信息==>");
logger.debug("debug:debug调试==>");
logger.error("error:异常==>");
}

分页

1.直接在SQL语句中用limit分页,然后传参.(常用)

UserMapper 中定义方法;

1
2
//查询用户且分页;
List<User> findUserToLimit(Map<String ,Integer> map);

UserMapper.xml中写sql语句;

1
2
3
4
5
6
7
<!--还要了结果集映射-->    
<resultMap id="UserMap" type="user">
</resultMap>
<!--直接在sql语句中使用limit分页-->
<select id="findUserToLimit" parameterType="map" resultMap="UserMap">
select * from day2021_9_6_studyMybatis_db.user limit #{startpage},#{pageSize};
</select>

测试使用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//使用limit分页;
@Test
public void testLimit(){
SqlSession sqlSession=mybatisutils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String ,Integer> map=new HashMap<String ,Integer>();
//查询第二页,且每页5行数据;
map.put("startpage",0);
map.put("pageSize",5);
List<User> userToLimit = mapper.findUserToLimit(map);
for (User user : userToLimit) {
System.out.println(user);
}
//关闭sqlSession;
sqlSession.close();
}

2.使用 RowBounds分页(了解)

UserMapper定义方法;

1
2
//查询分页;
List<User> findUserRowBounds();

UserMapper.xml中写sql语句,sql语句不写分页

1
2
3
4
<!--使用RowBounds的方法分页-->
<select id="findUserRowBounds" resultMap="UserMap">
select * from uday2021_9_6_studyMybatis_db.userr
</select>

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//RowBounds ==> 了解使用;
@Test
public void testRowBounds(){
SqlSession sqlSession=MybatisUtils.getSqlSession();
//实现分页;
RowBounds rowBounds = new RowBounds(5,5);

//3个参数分别为接口路径,object,方法名;
List<User> selectList=sqlSession.selectList("com.kuang.utils.dao.UserMapper.findUserRowBounds",null,rowBounds);
for (User user : selectList) {
System.out.println(user);
}
//关闭sqlSession;
sqlSession.close();
}

3.分页插件PageHelpe(大项目可能才用)

https://pagehelper.github.io/

使用注解开发(简单的sql语句比较好用,复杂的就比较麻烦)

首先整个设置是面向接口编写程序的.

UserMapper接口的定义方法处直接使用注解;放入SQL语句;

1
2
//可使用@Param注解,标明参数;sql传入的参数就是@Param注解中的参数名;`
//`引用对象参数不用写@Param注解
1
2
3
4
//直接使用注解的方式;
@Select("select * from day2021_9_6_studyMybatis_db.user where id=#{id} and name=#{name}")
//根据Id查询用户; 可以在方法处使用@Param注解,标明参数;sql传入的参数就是@Param注解中的参数名;
User findUserById(@Param("id") int id, @Param("name") String name);

需要在mybatis-config.xml中绑定接口;

1
2
3
4
<!--绑定接口-->
<mappers>
<mapper class="com.kuang.utils.dao.UserMapper"/>
</mappers>

在测试时,其实已经通过反射得到接口的定义方法返回值

本质:反射机制实现

底层:动态代理模式

1
2
3
4
5
6
7
8
9
10
//根据Id查询用户;
@Test
public void testFindUserById(){
SqlSession sqlSession = mybatisutils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.findUserById(2, "杰哥");
System.out.println(user);
//关闭sqlSession;
sqlSession.close();
}

image-20211215173519416

Mybatis开发的流程(底层–面试前看)——了解

把之前写的工具类详细看看;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory=null;
//在调用工具类时就执行;
static {
try {
//获取SqlSessionFactory对象;

//获得配置文件;
String resource = "mybatis-config.xml";
//使用流读取资源;
InputStream inputStream = Resources.getResourceAsStream(resource);
//加载资源流;
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}

//从 SqlSessionFactory 中获取 SqlSession;
public static SqlSession getSqlSession(){
SqlSession sqlSession = sqlSessionFactory.openSession();
return sqlSession;
}
}

首先是由==Resources==获取且加载全局的配置文件,然后是实例化==SqlSessionFactoryBuilder==;
点进build方法的源码; 里面是==创建XMLConfigBuilder==来解析配置文件流的;

在这里插入图片描述

接着是==Configuration==传入所有的配置文件信息;

实例化==sqlSessionFactory==;
先通过事务管理==transaction==;
然后创建==executor器==;

创建==sqlSession== ==>实现增删改查;如果出了问题,就需要事务回滚,回到事务管理;

若执行成功,提交事务;关闭资源.


在写工具类的时候,使用方法openSession() 来获取sqlSession;

在这里插入图片描述

进入源码查看;该方法有很多重载的;注意到有个以布尔值为参数的方法;查看它的实现;

SqlSession openSession(boolean var1);

在这里插入图片描述

在这里插入图片描述

是否开启自动提交事务;

1
2
3
public SqlSession openSession(boolean autoCommit) {
return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, autoCommit);
}

然后呢,在工具类获取sqlSession的时候,给openSession()传入参数true;也就是自动提交事务;那么就不用commit手动提交事务;

1
2
3
4
5
//从 SqlSessionFactory 中获取 SqlSession;
public static SqlSession getSqlSession(){
SqlSession sqlSession = sqlSessionFactory.openSession(true);
return sqlSession;
}

用注解完成一个添加方法;
UserMapper定义方法;且用注解完成sql语句;

1
2
3
//添加用户;
@Insert("insert into day2021_9_6_studyMybatis_db.user values(#{id},#{name},#{password})")
void addUser(User user);

mybatis-config.xml上面已经绑定过UserMapper了,
测试即可,无需手动提交事务;

1
2
3
4
5
6
7
8
9
10
//添加用户;
@Test
public void testAddUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.addUser(new User(13,"蕉宝","846662"));
//无需手动提交事务;
//关闭sqlSession;
sqlSession.close();
}

==扩展小知识,在写sql语句时,#{}可防止sql注入, ${}无法防止sql注入;==

Lombok插件的使用(偷懒可用——省去写实体类的步骤)

image-20211216154128076

1
2
3
4
5
6
7
<!--  导入依赖
https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>

使用@Data注解;生成无参构造,get,set方法,equals方法,hashCode方法,toString方法.

注解@AllArgsConstructor生成有参构造方法

注解@NoArgsConstructor生成无参构造方法

注解@ToString 生成toString方法;
注解@EqualsAndHashCode生成equals方法和hashCode方法

注解@Getter ,注解@Setter 放在类上,生成所有非静态的属性get,set方法;
放在属性上,仅生成该属性的get,set方法.

多对一情况

按照类似子查询的方式, 查询语句嵌套

那么先把这个问题拆分开来;
用两段SQL语句;
先查询学生,再根据Id查询教师表;

在写sql之前;需要了解结果集Map映射
==association 是表示对象; collection表示集合.
javaType;表示指定属性的类型;一般来说,在集合中的泛型类型用ofType指定==

==property 需要映射到JavaBean 的属性名称**(就是数据库表中的字段)
column 数据表的列名或者标签别名
(就是要映射的名字或者是自己定义的别名)**。
javaType 一个完整的类名,或者是一个类型别名。如果你匹配的是一个JavaBean,那MyBatis 通常会自行检测到。然后,如果你是要映射到一个HashMap,那你需要指定javaType 要达到的目的。==

==jdbcType 数据表支持的类型列表。这个属性只在insert,update 或delete 的时候针对允许空的列有用。JDBC 需要这项,但MyBatis 不需要。如果你是直接针对JDBC 编码,且有允许空的列,而你要指定这项。
typeHandler 使用这个属性可以覆写类型处理器。这项值可以是一个完整的类名,也可以是一个类型别名。==

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!--分布思路;先查询所有学生;在根据学生,查询教师-->
<select id="finAllStudent" resultMap="studentMap">

</select>
<resultMap id="studentMap" type="student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<association property="teacher" column="tid" javaType="teacher" select="findTeacherById"/>
</resultMap>

<!--根据Id查询教师-->
<select id="findTeacherById" resultType="teacher">
select * from teacher where id=#{tid};
</select>

查询结果嵌套(推荐使用)

在查询的结果集类型中将教师属性关联;

1
2
3
4
5
6
7
8
9
10
11
12
13
<resultMap id="studentteacher" type="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
</association>
</resultMap>

<select id="finAllStudent" resultMap="studentteacher">
select s.id sid,s.name sname,t.name tname
from student s ,teacher t
where s.tid=t.id;
</select>

一对多情况

按照查询嵌套处理(了解)

——可以理解是通过子查询来实现的,需要写两个查询语句查两张表(通过查到teacher中teacherid来查student的所有id成员)

这种查询需要写javatype——对应实体类的需求类型 collection后要关系到另一个select语句就用column字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 <!--先根据 id查询教师-->
<select id="findStuByTeaId" resultMap="TeacherMap2">
select * from teacher where id=#{tid};
</select>
<resultMap id="TeacherMap2" type="teacher">
<result column="id" property="id"/>
<result column="name" property="name"/>
<!--这里的javaType表示 List集合类型,ofType 表示集合中的泛型 student类型
column关联另一个select语句-->
<collection property="students" javaType="ArrayList" ofType="student" column="id" select="findStuById"/>
</resultMap>

<!--根据教师Id查询对应的学生-->
<select id="findStuById" resultType="student">
select * from student where tid=#{tid};
</select>

按照结果嵌套查询(主要使用)

这种就是通过查出目的实体类中有什么属性然后查询并一一映射对应

然后collection或者association中展开相关实体类要查的属性查询并一一映射对应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<select id="findStuByTeaId" resultMap="TeacherMap">
select s.name sname , s.id sid ,t.id tid , t.name tname
from teacher t,student s
where
t.id=s.tid and tid=#{tid}
</select>

<resultMap id="TeacherMap" type="teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<!--这里使用 collection 集合来映射学生表的属性和数据库查询字段-->
<collection property="students" ofType="student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>

两种对应情况总结

==@Param注解的作用是给参数命名,参数命名后就能根据名字得到参数值,正确的将参数传入sql语句中(一般通过#{}的方式,${}会有sql注入的问题)==

==ofType表示的是映射到集合中的的泛型(实体类)类型——比如一对多中list集合中的类型—— ofType=”student”==

image-20211218180626158

动态sql环境搭建(看idea—untitled3)

—动态sql只是我们可以在sql层面去执行一个逻辑代码

多了一个idutils类 其他环境基本不变——resourses,pom依赖整体大部分不变

接口及其xml要变—并且在mappers中注册,接口的xml的namespace要改

==UUID 是 通用唯一识别码(Universally Unique Identifier)的缩写,是一种软件建构的标准,亦为开放软件基金会组织在分布式计算环境领域的一部分==

==UUID通用唯一标识符 randomUUID()唯一的通用唯一标识符==

1
2
3
4
5
6
7
//做个生成随机Id的工具类;
//使用UUID;
public class IdUtils {
public static String getId(){
return UUID.randomUUID().toString().replaceAll("-","");
}
}

==对于的实体类中属性createTime和数据库名的字段create_time 命名不一致,可在**mybatis的核心配置文件**中更改settings设置为开启驼峰命名自动转换.==

==mapUnderscoreToCamelCase 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。==

动态SQL –>IF标签使用

​ ——好处是直接在测试类中添加约束属性就行

现在接口定义方法

BlogMapper.xml中 编写sql语句;
注意会用到if判断(if text是必须后面加条件);前面的 1=1 防止后面的条件若都不符合就查询输出所有的数据;

只要对需要查询的输入对应参数即可进行查询;不用去手动改变SQL语句;

if语句可以多次出现,也就是可以进行多重if判断

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--查询博客-->
<select id="findBlogHaveIf" resultType="blog" parameterType="map">
select * from blog where 1=1
<if test="title!=null">
and title =#{title}
</if>
<if test="author !=null">
and author =#{author}
</if>
<if test="views != null">
and views =#{views}
</if>
</select>
1
2
3
4
5
6
7
8
9
10
11
@Test
public void testFindBlogHaveIf(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map=new HashMap();
List<Blog> blogs = mapper.findBlogHaveIf(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}

若在执行时,对title属性进行赋值;就要查询这个指定的属性对应结果;只需在测试执行时添加;查询结果 ;它会自动地调整SQL语句.

动态SQL的常用标签

where 标签;

1
2
3
/*
在上面使用 if 标签时 ,注意到当时在 where 后拼接了一个 1=1;但是实际很多使用中 加这个1=1看起来不是很规范;
那么用<where> 标签替代where,且把需要执行的语句包裹起来,如果说where标签内没用到任何一个条件,那么where就不会拼接到SQL上去; 还有就是,如果where 后的条件开头有and 或者 or,where标签都会把他们去掉.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!--修改一下,用where标签-->
<select id="findBlogHaveIf" resultType="blog" parameterType="map">
select * from blog
<where>
<if test="title!=null">
title =#{title}
</if>
<if test="author !=null">
and author =#{author}
</if>
<if test="views != null">
and views =#{views}
</if>
</where>
</select>

测试结果:

==什么条件参数也不传入,执行时,确实没有出现where==

==再通过条件浏览量 views去查询;注意看看它是否会去掉前面的 and——————确实自动去掉了and==

choose (when, otherwise)标签

==若有多个条件,可选择其中的一个,类似于java的判断switch语句.
otherwise 其他条件==

==还是查询方法;不过这次用choose进行判断过滤==

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<select id="findBlogByChoose" resultType="blog" parameterType="map">
select * from blog
<where>
<choose>
<!--执行时sql会自动过滤多余的and
自己去idea试验-->
<when test="title != null">
title=#{title}
</when>
<when test="author !=null">
and author =#{author}
</when>
<otherwise>
and views =#{views}
</otherwise>
</choose>
</where>
</select>

==如果输入title,author,views三个参数进行查询;——只会执行xml中第一个写的if参数(上面先写title是title只会查出title)作为条件==

set标签

set 标签可动态地在行首插入 SET 关键字,删掉额外的逗号

1
2
3
4
5
6
7
8
9
10
11
12
<update id="updateBlogBySet" parameterType="map">
update blog
<set>
<if test="title !=null">
title= #{title},
</if>
<if test="author !=null">
author= #{author},
</if>
</set>
where id=#{id}
</update>

注意执行时,它自动去掉了 author 后的逗号

1
2
3
4
5
6
7
8
9
10
11
@Test
public void testUpdateBlogBySet(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map=new HashMap();
map.put("title","关于转生那件事");
map.put("author","阿猫");
map.put("id","7de2fa32bb1f42649bbe3d04e2afd796");
mapper.updateBlogBySet(map);
sqlSession.close();
}

sql片段

SQL片段也就是把一些公用的SQL语句提取出来;方便使用;避免代码冗余.

可使用 标签中写入公用的SQL语句;然后用 标签去引用;
标签 的refid 对应 标签的id;

建议不要将复杂SQL作为SQL片段.

比如说,在查询方法的SQL语句中,提取出SQL片段;用inclued标签引入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<select id="findBlogHaveIf" resultType="blog" parameterType="map">
select * from blog
<where>
<include refid="isPublicCode"></include>
</where>
</select>

<!--使用sql标签取出公用的代码片段-->
<sql id="isPublicCode">
<if test="title!=null">
title =#{title}
</if>
<if test="author !=null">
and author =#{author}
</if>
<if test="views != null">
and views =#{views}
</if>
</sql>

动态SQL之foreach标签.

在这里插入图片描述

遍历时,==遍历的是一个集合,索引为index, **每次遍历取出的元素就是 item**; 用open表示开头 ;close表示结尾;使用separator作为分隔符==,将 SQL语句拼接起来.

  • 当collection是列表的时候
    item代表列表中的元素
    index代表正在迭代的下标
  • 当collection是映射的时候
    item代表的是value
    index代表key
    open代表了迭代最开始的符号
    separator代表了每一个迭代元素之间的分割符号
    close代表了结束符号
  • 当collection也可以是map
1
2
3
4
5
6
7
8
9
10

<select id="findBlogForEach" resultType="blog" parameterType="map">
select * from blog
<where>
<!--collection是测试类中集合的名称-->
<foreach collection="allId" item="id" open="and (" separator="or" close=")">
id=#{id}
</foreach>
</where>
</select>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
public void testFindBlogForEach(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map=new HashMap();
//定义存放 id的集合;
List<Integer> list=new ArrayList<Integer>();
list.add(1);
list.add(3);
//将存有id的集合,放入map;
map.put("allId",list);

List<Blog> blogForEach = mapper.findBlogForEach(map);
for (Blog blog : blogForEach) {
System.out.println(blog);
}
sqlSession.close();
}

Mybatis缓存–自行了解