文章目录
1.mybatis
是一个持久层框架,通过XML配置或者注解配置,省去了所有的JDBC代码(除了sql语句),比如获取连接,设置参数,返回结果集。
开源在githup上
1.1mybatis使用
-
maven仓库
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency>
-
GitHub
-
中文文档
1.2 为什么使用mybatis
自动化配置,实现sql和java代码分离,提高维护性。
2.第一个mybatis程序
搭建环境–》导入mybatis–》编写代码–》测试
2.1 搭建环境
-
创建maven项目
-
删除src目录,就可以当成maven的父目录,父目录中的依赖可以不用导入
-
导入maven依赖
<!--导入依赖--> <dependencies> <!-- mybatis--> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency> <!-- mysql--> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.12</version> </dependency> <!-- junit--> <!-- https://mvnrepository.com/artifact/junit/junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies>
- dependencies使用
2.2创建子模块
- resources目录下配置mybatis核心配置文件,连接数据库
<?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> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis_study?useSSL=false&useUnicode=true&serverTimezone=UTC&characterEncoding=utf8"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <mapper resource="dao/UserMapper.xml"/> </mappers> </configuration>
<mappers>
<mapper resource="dao/UserMapper.xml"/>
</mappers>
不要忘记mapper.xml的注入会报错
org.apache.ibatis.binding.BindingException: Type interface com.kuang.dao.UserDao is not known to the MapperRegistry.
<?xml version="1.0" encoding="UTF-8" ?>
//中文问题,mysql数据库中需要用utf8,将utf-8改为utf8
<?xml version="1.0" encoding="UTF8" ?>
2.编写mybatis工具类,获取sqlSession相当于connection
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
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(){
return sqlSessionFactory.openSession();
}
}
3.编写代码
实体类
public class User {
private int id;
private String name;
private String pwd;
public User() {
}
public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
2.接口类
public interface UserDao {
List<User> getUsers();
}
3.接口实现类
<?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="dao.UserDao">
<select id="getUsers" resultType="pojo.User">
select * from users where id = 1;
</select>
</mapper>
4.测试
public class UserDaoTest {
@Test
public void test(){
//获取sqlSession
SqlSession sqlSession = MybatisUtils.getSqlSession();
//获取接口UserDao,调用方法
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> users = mapper.getUsers();
//输出
for(User user:users){
System.out.println(user);
}
}
}
细节
*static静态代码块其实完全可以看做是一个没有名字、没有参数、没有返回值的静态方法*
SqlSession sqlSession = sqlSessionFactory.openSession();
return sqlSession;
代码优化
return sqlSessionFactory.openSession();
常见错误
-
sql查询语句标签名
-
jdbc数据库连接url
-
编码,mapper.xml和mybatis-config.xml中中文乱码问题
-
mybatis中mapper.xml注册资源路径
-
mapper.xml文件导出的问题,maven中pom.xml中需要扫描导出
<!--在build中配置resources,来防止我们资源导出失败的问题--> <build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> </resources> </build>
3.CURD
只需要修改接口,接口实现类,测试类
1.实现接口,UserMapper
public interface UserMapper {
List<User> list();
User getUserById(int id);
int addUser(User user);
int updateUser(User user);
int deleteUser(int id);
}
- 参数
- 返回值
- 方法名规范
2.接口实现类 mapper.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="com.qiang.dao.UserMapper">
<select id="list" resultType="com.qiang.pojo.User">
select * from users
</select>
<select id="getUserById" resultType="com.qiang.pojo.User" parameterType="int">
select * from users where id=#{id}
</select>
<insert id="addUser" parameterType="com.qiang.pojo.User">
insert into users (`id`,`name`,`pwd`) values (#{id},#{name},#{pwd})
</insert>
<update id="updateUser" parameterType="com.qiang.pojo.User">
update users set `name`=#{name},`pwd`=#{pwd} where id=#{id}
</update>
<delete id="deleteUser" parameterType="com.qiang.pojo.User">
delete from users where id=#{id}
</delete>
</mapper>
- nameplace 空间命名对应接口,相当于实现接口
- id对应方法名,相当于实现方法
- 返回类型和参数
- {id}可以获取对象中的属性,且要和对象属性一一对应
- 表名前加数据库名,idea连接数据库后,没连不用写,直接写表名
3.测试
import com.qiang.dao.UserMapper;
import com.qiang.pojo.User;
import com.qiang.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;
import java.util.List;
public class test {
@Test
public void test(){
//获取sqlSession
SqlSession sqlSession = MybatisUtils.getSqlSession();
//获取接口UserDao,调用方法
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.list();
User user1 = mapper.getUserById(1);
//输出
for(User user:users){
System.out.println(user);
}
System.out.println(user1);
sqlSession.close();
}
@Test
public void addUserTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int i = mapper.addUser(new User(4,"lixin","123456"));
if(i>0){
System.out.println("插入成功!");
}
sqlSession.commit();
sqlSession.close();
}
@Test
public void updateUserTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int i = mapper.updateUser(new User(4, "程咬金", "123456"));
if(i>0){
System.out.println("更改成功!");
}
sqlSession.commit();
sqlSession.close();
}
@Test
public void deleteUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int i = mapper.deleteUser(3);
if(i>0){
System.out.println("删除成功!");
}
sqlSession.commit();
sqlSession.close();
}
}
- 增删改要提交事务
- 记得释放资源sqlSession
- @Test
- 传值 new User(4,“fewhi”,“5113”) 双引号
map
修改某一字段,如果字段过多,使用User传值,需要写出全部的字段,使用map最多传个id,随意简便。
使用User
mapper.updateUser1(new User(1,"王昭君","123456"));
使用map 键值对 key
int updateUser(Map<String,Object> map);
<update id="updateUser" parameterType="Map">
update users set `name`=#{onename} where id=#{oneid}
</update>
@Test
public void updateTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
HashMap<String, Object> map = new HashMap<>();
map.put("oneid", 1);
map.put("onename", "宋尚香");
mapper.updateUser(map);
sqlSession.commit();
sqlSession.close();
}
- 简便,省去过多字段
- 不用像User一样,需要一一对应属性名,可以自定义
map传参数,sql中用key 【 parameterType=“Map”】
对象传参数,sql中用属性 【parameterType=“pojo.User”】
只用一个基本属性,比如int 【parameterType=“int”】省略不写
多个属性使用map或者注解
模糊查询
List<User> getUserLike(String value);
select * from users where `name` like #{value}
List<User> users = mapper.getUserLike("李%");
- 不安全,用户传入id为1&1=1,字符串拼接,存在注入问题
解决:可以把值固定,只能传value值
select * from users where `name` like "%"#{value}"%"
List<User> users = mapper.getUserLike("李");
4.配置解析
不是是会简单的curd,学会配置,学会看官方文档
4.1 配置环境
可以配置多种环境,但是一个实例中只能选择一种环境,要通过default指定要使用的环境
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--配置文件-->
<configuration>
<!-- 引入外部配置文件-->
<properties resource="db.properties"/>
<environments default="development">
<environment id="development">
<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>
<mappers>
4.2 配置文件
1.在resources文件中写db.properties文件
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis_study?useSSL=false&useEncoding=true&characterEncoding=utf8&serverTimezone=UTC
username=root
password=123456
2.引入配置文件
//第一种方式 自闭合
<properties resource="db.properties"/>
//第二种方式 可以设置属性 优先级低于properties中
<properties resource="db.properties">
<property name="username" value="name"/>
<property name="password" value="123456"/>
</properties>
注意位置,应最前面,xml中的配置有顺序
properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?
别名
解决mapper.xml配置文件中类名复杂的问题
1.指定具体,适用于别名较少的
<!-- 可以起别名 -->
<typeAliases>
<typeAlias type="com.qiang.pojo.User" alias="User"></typeAlias>
</typeAliases>
2.通过扫描包下所有的JavaBean类,默认为类名小写,首字母大写好像也行,适用于类多的情况
<typeAliases>
<package name="com.qiang.pojo"/>
</typeAliases>
3.注解,可以在类中通过注解指定别名
@Alias("hello")
4.3 设置
4.4 映射器
注册绑定mapper文件
1.使用resources
<mappers>
<mapper resource="com/qiang/dao/UserMapper.xml"/>
</mappers>
2.使用class类名
<mappers>
<mapper class="com.qiang.dao.UserMapper"/>
</mappers>
- 类名和配置名要相同,且在同一包下
3.使用package
<mappers>
<package name="com.qiang.dao"/>
</mappers>
4.5 生命周期和作用域
错误的使用作用域和生命周期,会引发并发问题
SqlSessionFactoryBuild
-
创建SQLSessionFactory,创建玩就不需要了
-
局部变量
-
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSessionFactory
-
理解为数据库连接池
-
创建sqlSession,需要一直存在,应用启动就一直存在
-
全局变量,应用作用域
-
private static SqlSessionFactory sqlSessionFactory; return sqlSessionFactory.openSession();
sqlSession
-
理解为一个请求
-
调用方法,接口,实现业务
-
在方法体类
-
public void getUsers(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> users = mapper.getUsers(); for(User user :users ){ System.out.println(user); }
每一个mapper相当于业务。实现SQL语句
5.ResultMap结果集
解决;属性名和字段名不一致问题,查询不到数据
解决方法一:给字段起别名
select `id`,`name`,`pwd` as `password` from users
解决方法二:resultMap结果集映射
<!-- 将结果集UserMap映射为User类 id 是引用 type映射的类-->
<resultMap id="UserMap" type="User">
<!-- property 类中的属性 column 数据库的字段-->
<result property="id" column="id"/>
<result property="name" column="name"/>
<result property="password" column="pwd"/>
</resultMap>
resultMap结果集
- 简单语句可以做到0配置,复杂语句只要说明关系
6.日志
6.1 日志工厂
解决SQL语句报错,方便找到bug
配置日志
<!-- 设置-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
- 类名和值要严格,大小写,不能有空格
- 默认为STDOUT_LOGGING 适用其他日志需要导入包
6.2 log4j
定义
log4j是Apache下的一个开源项目,可以通过修改配置文件,控制日志文件的输出方式,控制台,文件,和控制日志的格式,同时能通过Logger对象来定义日志信息的级别,输出,debug,error,更加清除生成过程
1.导入log4j包
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2.log4j配置文件
#将等级为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/qiang.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
3.设置log4j
<!-- 设置-->
<settings>
<!-- log4j-->
<setting name="logImpl" value="LOG4J"/>
</settings>
4.使用
使用
尽量只用log4j,配置文件可以想自定义修改,可以在log文件中通过前缀看到信息方式
1.在需要使用log4j的类中导入import org.apache.log4j.Logger;,一定是apache下的
2.日志对象,参数为当前类中的class
static Logger logger = Logger.getLogger(UserMapperTest.class);
3.实现级别
logger.info("输出");
logger.debug("debug");
logger.error("错误");
7.分页
减少数据库的压力,提高用户体验
7.1 使用limit分页
SELECT * FROM users LIMIT 0,2
本质是通过mybaties对SQL语句的实现
1.Usermapper
//分页
List<User> getUserByLimit(Map<String,Integer> map);
2.UserMapper.xml
<select id="getUserByLimit" resultMap="UserMap" parameterType="map">
select * from users limit #{startIndex},#{pageSize}
</select>
3.test
@Test
public void getUserByLimitTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Integer> map = new HashMap<>();
map.put("startIndex", 0);
map.put("pageSize", 2);
List<User> users = mapper.getUserByLimit(map);
for(User user:users){
System.out.println(user);
}
}
- 通过map实现,Map的别名
7.2 RowBounds
1.接口
List<User> getUsersByRowBounds();
2.接口实现类
<select id="limit" resultType="pojo.User" parameterType="map">
select * from users limit #{startIndex},#{pageSize}
</select>
3.test
@Test
public void getUsersByRowBoundsTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
RowBounds rowBounds = new RowBounds(0, 2);
List<User> users = sqlSession.selectList("com.qiang.dao.UserMapper.getUsersByRowBounds",null,rowBounds);
for(User user :users){
System.out.println(user);
}
sqlSession.close();
}
List<User>
和List<Object>
sqlSession.close();
不要忘记,最好一开始就写好
7.3 分页插件
分页的实现本质上是通过limit
1.sql语句加map传参的方法
2.RowBounds实现,通过直接调用方法,全限定名实现,不推荐使用,快被淘汰
8.使用注解开发
8.1 面向接口
面向接口编程:解耦,每一个接口相当于程序的骨架,只需要实现每个接口就行,注解的使用符合面向接口编程
mybatis中可以在接口上使用注解,可以简化开发,省写mapper.xml文件,但是只支持简单的sql,复杂的sql不行,比如字段和属性不匹配的话,使用注解查询不到,所以简单的可以使用注解开发,但还是推荐使用xml
底层原理:反射,通过UserMapper mapper = sqlSession.getMapper(UserMapper.class);
反射获取接口的一切东西,接口的SQL实现,和方法自然就可以实现了
1.接口加注解 只要写接口,不用xml
public interface UserMapper {
@Select("select * from users")
List<User> getUsers();
}
2.配置文件mybatis-config.xml中注册mapper接口
<mappers>
<mapper class="com.qiang.dao.UserMapper"/>
</mappers>
3.测试
public class UserMapperTest {
@Test
public void getUsersTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.getUsers();
for(User user:users){
System.out.println(user);
}
sqlSession.close();
}
}
8.2.mybatis执行流程
通过debug分析
8.3 CRUD注解开发
开启自动提交
return sqlSessionFactory.openSession(true);
可以省略sqlSession.commit;
但不建议使用,会错误提交信息
CRUD注解开发
接口
public interface UserMapper {
@Select("select * from users")
List<User> getUsers();
//多个参数时,一定要加上@Param指定sql语句#{id}获取的值是,引用对象user不需要写
@Select("select * from users where id = #{id}")
User getUserById(@Param("id") int id);
@Insert("insert into users (`id`,`name`,`pwd`) values(#{id},#{name},#{password})")
int add(User user);
@Update("update users set `name`=#{name},`pwd`=#{password} where `id`=#{id}")
int update(User user);
@Delete("delete from users where id=#{id}")
int delete(@Param("id") int id);
}
- @Param的使用,有多个基本参数时,一定要使用@Param,一个参数可以不用,引用参数可以不用比如Userdeng
- @Param(“id1”)对应的sql中的#{id1},是一一对应的
注意在配置文件中绑定注册
<mappers>
<mapper class="com.qiang.dao.UserMapper"/>
</mappers>
8.4Lombok
简化操作,工具
@Data //无参,getset方法,tostring
@AllArgsConstructor //有参,写了后没有无参需要再导入
@NoArgsConstructor //无参
public class User {
private int id;
private String name;
private String password;
}
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
9.多对一处理
多个学生关联一个老师
9.1 嵌套子查询处理
1.先查询所有学生
2.根据tid查老师,相当于子查询,先查出学生有哪些,然后根据s.tid=t.id,查老师
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qiang.dao.StudentMapper">
<select id="getStudents" resultMap="StudentTeacher">
select * from student
</select>
<resultMap id="StudentTeacher" type="student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="Teacher">
select * from teacher where id=#{id}
</select>
</mapper>
- 用resultMap解决属性teacher和字段tid不对应的问题,但是返回的还是Student类
- 遇到复杂属性需要单独处理,对象用association,集合用collection
9.2 结果集查询处理
相当于连表查询,只要对应上字段即可
<!-- 结果集查询-->
<select id="getStudents2" resultMap="StudentTeacher1">
select s.id sid,s.name sname,t.name tname
from student s,teacher t
where s.tid=t.id
</select>
<resultMap id="StudentTeacher1" type="student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
</association>
</resultMap>
10.一对多处理
一个老师关联多个学生
10.1嵌套子查询
<!-- 子查询-->
<select id="getTeacher2" resultMap="TeacherStudent1">
select * from teacher where id=#{tid}
</select>
<resultMap id="TeacherStudent1" type="teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="students" column="id" ofType="Student" select="getStudent" javaType="ArrayList"></collection>
</resultMap>
<select id="getStudents" resultType="student">
select `id`,`name`,`tid` from student where tid=#{tid}
</select>
- 两个条件
10.2 结果集查询
<!-- 结果集-->
<select id="getTeacher" resultMap="TeacherStudent">
select s.id sid,s.name sname,t.name tname,t.id tid
from student s,teacher t
where s.tid=t.id and t.id = #{tid}
</select>
<resultMap id="TeacherStudent" type="teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="students" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
</collection>
</resultMap>
- 按照结果进行映射
总结
- association对象 collection集合
- resultMap 实体中属性对应的类型 比如ArrayList,oftype 映射到集合或者List的pojo类型比如User
- 注意不同表和实体的属性和字段的对应
11动态sql
11.1自动生成id
//自动生成id
public class IdUtils {
public static String getId(){
return UUID.randomUUID().toString().replaceAll("-", "");
}
@Test
public void test(){
System.out.println(IdUtils.getId());
System.out.println(IdUtils.getId());
System.out.println(IdUtils.getId());
System.out.println(IdUtils.getId());
}
}
11.2动态sql
if
List<Blog> queryBlog(Map map);
<mapper namespace="com.qiang.dao.BlogMapper">
<select id="queryBlog" parameterType="map" resultType="Blog">
select * from blog
<where>
<!-- <if test="title != null">-->
<!-- and title=#{title}-->
<!-- </if>-->
<if test="author != null">
and author=#{author}
</if>
</where>
</select>
</mapper>
public class BlogTest {
@Test
public void queryBlogTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap<String, Object> map = new HashMap<>();
map.put("title", "python");
map.put("author", "后羿");
List<Blog> blogs = mapper.queryBlog(map);
for(Blog blog:blogs){
System.out.println(blog);
}
}
}
choose
<select id="queryBlogChoose" parameterType="map" resultType="Blog">
select * from blog
<where>
<choose>
<when test="title!=null">
title=#{title}
</when>
<when test="author!=null">
author=#{author}
</when>
<otherwise>
view=#{view}
</otherwise>
</choose>
</where>
</select>
- 只会满足一个条件,后面的即使满足也不会执行,相当于Switch case
set
<update id="updateBlog" parameterType="map">
update blog
<set>
<if test="title!=null">
title=#{title},
</if>
<if test="author!=null">
author=#{author}
</if>
</set>
where id=#{id}
</update>
- set标签会自动加set关键词,去掉多余的逗号
动态SQL本质上还是sql,就是通过if判断是否为空,去增加sql条件,在SQL层面增加逻辑
sql片段
抽取重复片段,实现代码复用
<sql id="updateSql">
<set>
<if test="title!=null">
title=#{title},
</if>
<if test="author!=null">
author=#{author}
</if>
</set>
</sql>
<update id="updateBlog" parameterType="map">
update blog
<include refid="updateSql"></include>
where id=#{id}
</update>
- sql标签抽取,include标签进行引用
- 尽量在单表中使用,不要包含where和set标签
for(Blog blog:blogs){
System.out.println(blog);
}
- 可以通过
blog.for
实现
forEach
实现动态SQLselect * from blog where 1=1 and (id=1 or id=2 or id=3)
List<Blog> getBlogsForEach(Map map);
<!-- select * from blog where 1=1 and (id=1 or id=2 or id=3)-->
<select id="getBlogsForEach" parameterType="map" resultType="Blog">
select * from blog
<where>
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
id=#{id}
</foreach>
</where>
</select>
@Test
public void getBlogsForEach(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap<String, Object> map = new HashMap<>();
ArrayList<Object> ids = new ArrayList<>();
ids.add(1);
ids.add(2);
map.put("ids",ids);
List<Blog> blogs = mapper.getBlogsForEach(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
}
-
在mysql中实现SQL保证SQL的正确性,再用动态SQL实现,本质上还是sql语句
-
通过map,List给sql传值
-
元素主要用在构建 in 条件中,它可以在 SQL 语句中迭代一个集合。
元素的属性主要有 item、index、collection、open、separator、close。
- item 表示集合中每一个元素进行迭代时的别名。
- index 指定一个名字,用于表示在迭代过程中每次迭代到的位置。
- open 表示该语句以什么开始。
- separator 表示在每次进行迭代之间以什么符号作为分隔符。
- close 表示以什么结束。
在使用 元素时,最关键、最容易出错的是 collection 属性,该属性是必选的,但在不同情况下该属性的值是不一样的,主要有以下 3 种情况:
- 如果传入的是单参数且参数类型是一个 List,collection 属性值为 list。
- 如果传入的是单参数且参数类型是一个 array 数组,collection 的属性值为 array。
- 如果传入的参数是多个,需要把它们封装成一个 Map,当然单参数也可以封装成 Map。Map 的 key 是参数名,collection 属性值是传入的 List 或 array 对象在自己封装的 Map 中的 key。
实例
select * from blog where id in (1,2,3)
List<Blog> getBlogsForEachIn(List<Integer> list);
<!-- select * from blog where id in (1,2,3)-->
<select id="getBlogsForEachIn" resultType="blog" parameterType="List">
select * from blog
<where>
<foreach collection="list" open="id in (" close=")" separator="," item="id">
#{id}
</foreach>
</where>
</select>
@Test
public void getBlogsForEachIn(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
ArrayList<Integer> ids = new ArrayList<>();
ids.add(1);
ids.add(2);
// ids.add(3);
List<Blog> blogs = mapper.getBlogsForEachIn(ids);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
}
- 也可以直接在写成
select * from blog where id in
后面接(1,2,3)
12 缓存
在内存中对数据进行备份,当数据没有改变的情况下,可以 直接在缓存中取数据,而不用去数据库读数据,减少读写次数,提高效率。
主要应用于查询量大的操作
常见的数据库缓存技术
12.1 一级缓存
mybatis会默认开启,sqlSession级别的,会在sqlSession创建和清除间隔中存在,相当于一个map
缓存失效
1.执行一个新的查询
2.增删改
3.不同的mapper.xml
4.手动关闭缓存
sqlSession.clearCache();
没有太多用处,只有重复查询一个东西时有用
12.2 二级缓存
在同一个Mapper中有效,是nameSpace级别的缓存,数据会暂时保存在一级缓存中,但是sqlSession提交或者关闭,就会把数据存到二级缓存中。
开启
在mapper.xml中可以通过cache标签开启
1.mybatis-config.xml中开启全局缓存
<!-- 开启全局缓存-->
<setting name="cacheEnabled" value="true"/>
2.在要使用到的mappe.xmlr中开启
- 不定义参数
<cache/>
- 会出现序列化bug
Error serializing object. Cause: java.io.NotSerializableException: com.qiang.pojo.User
-
解决
-
加参数readOnly="true" 或者将实体类序列化 类后面加public class User implements Serializable
2.自定义参数
<cache readOnly="true" eviction="FIFO" flushInterval="60000" size="512"/>
-
二级缓存是只有sqlSession关闭或者提交,才将数据存入二级缓存,所以是针对整个mapper来说的
-
通过查看控制台输出,没有重新执行SQL,就直接取,就是缓存生效了
12.3 缓存原理
- 先查二级缓存,再查一级缓存 最后查数据执行sql
- 一级缓存是针对sqlSession来说的,SQLSession没有关闭,数据还在一级缓存中,所以先查二级缓存再查一级缓存
12.4 自定义缓存ehcache
,可以 直接在缓存中取数据,而不用去数据库读数据,减少读写次数,提高效率。
主要应用于查询量大的操作
常见的数据库缓存技术
12.1 一级缓存
mybatis会默认开启,sqlSession级别的,会在sqlSession创建和清除间隔中存在,相当于一个map
缓存失效
1.执行一个新的查询
2.增删改
3.不同的mapper.xml
4.手动关闭缓存
sqlSession.clearCache();
没有太多用处,只有重复查询一个东西时有用
12.2 二级缓存
在同一个Mapper中有效,是nameSpace级别的缓存,数据会暂时保存在一级缓存中,但是sqlSession提交或者关闭,就会把数据存到二级缓存中。
开启
在mapper.xml中可以通过cache标签开启
1.mybatis-config.xml中开启全局缓存
<!-- 开启全局缓存-->
<setting name="cacheEnabled" value="true"/>
2.在要使用到的mappe.xmlr中开启
- 不定义参数
<cache/>
- 会出现序列化bug
Error serializing object. Cause: java.io.NotSerializableException: com.qiang.pojo.User
-
解决
-
加参数readOnly="true" 或者将实体类序列化 类后面加public class User implements Serializable
2.自定义参数
<cache readOnly="true" eviction="FIFO" flushInterval="60000" size="512"/>
-
二级缓存是只有sqlSession关闭或者提交,才将数据存入二级缓存,所以是针对整个mapper来说的
-
通过查看控制台输出,没有重新执行SQL,就直接取,就是缓存生效了
[外链图片转存中…(img-sYilveRb-1636376570322)]
12.3 缓存原理
[外链图片转存中…(img-1M4fkprQ-1636376570325)]
- 先查二级缓存,再查一级缓存 最后查数据执行sql
- 一级缓存是针对sqlSession来说的,SQLSession没有关闭,数据还在一级缓存中,所以先查二级缓存再查一级缓存