Mybatis学习笔记
1、入门
-
搭建环境(Maven)
-
导入依赖(
mybatis
、mysql-connector-java
)<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.3</version> </dependency>
-
配置mybatis(
mybatis-config.xml
)<?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> <!--环境,可以配置多个环境,这里设置了默认的环境为development--> <environments default="development"> <!--第一个环境时id为development的环境--> <environment id="development"> <!--事务管理是JDBC--> <transactionManager type="JDBC"/> <!--数据池配置--> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="123456w"/> </dataSource> </environment> </environments> <!--每一Mapper.XML都需要在Mybatis核心配置文件中注册--> <mappers> <mapper resource="com/th/dao/UserMapper.xml"/> </mappers> </configuration>
-
构建工具类,获取SqlSessionFactory和SqlSession
public class MybatisUtils { // 从 XML 中构建 SqlSessionFactory[也可以通过Java 代码构建SqlSessionFactory] private static SqlSessionFactory sqlSessionFactory; static { //第一步:获取SqlSessionFactory对象 try { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } /*既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。SqlSession 提供了在 数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。*/ //第二步:通过SqlSessionFactory获取SqlSession对象 public static SqlSession getSqlSession(){ //通过静态代码块中的sqlSessionFactory对象调用openSession()方法得到一个SqlSession 对象 //将这个SqlSession 对象返回出去,作为工具提供给需要实现数据库操作的对象使用。 return sqlSessionFactory.openSession(); } }
-
编写实体类(可使用
lombok
)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; } …… @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", pwd='" + pwd + '\'' + '}'; } }
-
Dao / Mapper接口
public interface UserMapper { List<User> getUserList();
-
接口实现类Impl【XXXMapper.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"> <!--namespace=绑定一个对应的Dao/Mapper接口,XXXMapper.xml相当于UserDaoImpl,实现Dao接口--> <mapper namespace="com.th.dao.UserMapper"> <!--select查询语句--> <!--id为Dao/Mapper接口的一个方法,resultType为返回的一个类型--> <select id="getUserList" resultType="com.th.pojo.User"> select * from mybatis.user </select> </mapper>
-
在Mybatis核心配置文件中注册每一个Mapper.xml
-
测试
public void testGetUserList() { //第一步:通过工具类获得SqlSession对象 SqlSession sqlSession = MybatisUtils.getSqlSession(); //第二步:获取结果集 try { //方式一:getMapper(推荐使用) //不仅更清晰,更加类型安全,还不用担心可能出错的字符串字面值以及强制类型转换。 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<User> userList = userMapper.getUserList(); //方式二: //List<User> userList = sqlSession.selectList("com.th.dao.UserDao.getUserList"); for (User user : userList) { System.out.println(user); } } catch (Exception e) { e.printStackTrace(); } finally { //第三步:关闭SqlSession sqlSession.close(); } }
-
注意点
- 报错
org.apache.ibatis.binding.BindingException: Type interface com.th.dao.UserMapper is not known to the MapperRegistry.
每一个XXXMapper.xml文件都必须在mybatis.xml文件中进行注册。
<mappers> <mapper resource="com/th/dao/UserMapper.xml"/> </mappers>
- 报错
Caused by: java.io.IOException: Could not find resource com/th/dao/UserMapper.xml
Maven约定大于配置,出现这个错误的原因是Maven默认导出
resources
下的配置文件,而src目录下的不会被导出,解决方案是配置使其导出src下的配置文件。<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>
- 报错
2、三大对象作用域
- SqlSessionFactoryBuilder
SqlSessionFactoryBuilder用于创建SqlSessionFactory对象,一旦创建了 SqlSessionFactory,就不再需要SqlSessionFactoryBuilder。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量) - SqlSessionFactory
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。 - SqlSession
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例是线程不安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。
3、CRUD
-
id: 对应接口中定义的方法名
-
resultType: SQL执行后的返回值
-
parameterType:参数类型
-
注意:增删改需要提交事务
-
1. select
- 编写接口
User getUserById(int id);
- 编写对应的mapper中的sql语句
<select id="getUserById" parameterType="int" resultType="com.th.pojo.User"> select * from mybatis.user where id=#{id} </select>
- 测试/实现
@Test public void testGetUserById() { SqlSession sqlSession = MybatisUtils.getSqlSession(); try { UserMapper mapper = sqlSession.getMapper(UserMapper.class); User userById = mapper.getUserById(1); System.out.println(userById); } catch (Exception e) { e.printStackTrace(); } finally { sqlSession.close(); } }
2. insert
-
编写接口
int insertUser(User user);
-
编写对应的mapper中的sql语句
<insert id="insertUser" parameterType="com.th.pojo.User"> insert into mybatis.user(id,name,pwd) values (#{id},#{name},#{pwd}); </insert> <!--mybaits中可以自动识别对象中的属性,所以这里直接(#{id},#{name},#{pwd})即可-->
-
测试/实现
@Test public void testInsertUser() { SqlSession sqlSession = MybatisUtils.getSqlSession(); try { UserMapper mapper = sqlSession.getMapper(UserMapper.class); int i = mapper.insertUser(new User(4, "王五", "123456")); if (i > 0) { System.out.println("insert successful"); } //提交事物,这是必须的,否者无法真正的插入数据。 sqlSession.commit(); } catch (Exception e) { e.printStackTrace(); } finally { sqlSession.close(); } }
3. update
-
sql语句
<update id="updateUser" parameterType="com.th.pojo.User"> update mybatis.user set name =#{name},pwd=#{pwd} where id=#{id}; </update>
4. delete
-
sql语句
<delete id="deleteUser" parameterType="int"> delete from mybatis.user where id=#{id}; </delete>
万能的Map
避免形如在修改某个对象属性时需要传递完整的对象,使用Map则只需要传递有需求的参数即可。以更新数据为例。
-
编写接口
int updateUser2(Map<String,Object> map);
-
编写对应的mapper中的sql语句
<update id="updateUser2" parameterType="map"> <!--参数名于传入map中的key一致即可--> update mybatis.user set name =#{userName} where id=#{userid}; </update>
-
测试/实现
public void testUpdateUser2(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); try { UserMapper mapper = sqlSession.getMapper(UserMapper.class); Map<String, Object> map = new HashMap<>(); map.put("userid", 4); map.put("userName", "燕g"); int i = mapper.updateUser2(map); if (i>0){ System.out.println("Update Successful"); } sqlSession.commit(); } catch (Exception e) { e.printStackTrace(); } finally { sqlSession.close(); } }
-
总结
- Map传递参数,直接在sql中取出key即可。
- 对象传递参数,直接在sql中取出对象属性即可。
- 只有一个基本参数情况下,可以直接在sql中取到。
- 多个参数用Map或者注解!
4、Mybatis配置解析
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
注意:在配置时必须按照一下顺序(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)
1. 属性(properties)
-
作用:实现引用配置文件
-
使用步骤
-
编写一个配置文件
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
-
在核心配置文件中进行引入
<properties resource="db.properties"> <property name="uname" value="root"/> <property name="uword" value="mysqlpw"/> </properties>
-
核心配置文件中配置environments时直接使用
<environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <!--之所以使用${driver}是因为在<properties>标签内引入了值来替换--> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${uname}"/> <property name="password" value="${uword}"/> </dataSource> </environment> </environments>
3. 可以在引入后添加property标签对外部配置文件进行补充,如果有两个相同属性,优先使用外部配置文件!
-
2. 设置(settings)
设置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true | false | true |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 |
true | false | false |
mapUnderscoreToCamelCase | 是否开启驼峰命名自动映射,即从经典数据库列名A_COLUMN 映射到经典 Java 属性名aColumn。 | true | false | False |
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J | LOG4J(deprecated since 3.5.9) | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | 未设置 |
日志工厂
<settings>
<!--标准日志工厂实现-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
标准日志工厂实现不需要额外导包,注意name和value的值必须严格要求按照标准书写,前后不允许出现空格。
LOG4J
是什么?
- Log4是 Apache的一个开源项目,通过使用Log4,我们可以控制日志信息输送的目的地是控制台、文件、GU组件
- 我们也可以控制每一条日志的输出格式;
- 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程
- 通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
如何用?
-
导入依赖
<!-- https://mvnrepository.com/artifact/log4j/log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
-
创建
log4j.properties
配置文件(网上有详细配置)log4j.rootLogger=DEBUG,console,file #控制台输出的相关设置 #输出出现乱码时在配置文件配置编码格式后还应该设置idea的编码格式。 log4j.appender.stdout.Encoding=UTF-8 log4j.appender.console = org.apache.log4j.ConsoleAppender # 设置控制台输出的格式为utf-8. log4j.appender.console.Encoding=UTF-8 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 # 设置文件输出的格式为utf-8. log4j.appender.file.Encoding=UTF-8 log4j.appender.file.File=./log/log4j.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
-
项目中配置LOG4J日志工厂实现
<settings> <setting name="logImpl" value="LOG4J"/> </settings>
-
简单使用
在相应的控制台和文件中将有日志输出。
-
其他使用
-
在要使用LOG4J的类中创建
Logger
对象(import org.apache.log4j.Logger;)//UserMapperTest为需要使用LOG4J的类 static Logger logger = Logger.getLogger(UserMapperTest.class);
-
使用日志级别
logger.info("进入了testGetUserById"); logger.debug("进入了testGetUserById"); logger.error("进入了testGetUserById");
-
3. 类型别名(typeAliases)
作用:可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。
-
自定义别名 (适合Java Bean少的情况下)
<typeAliases> <!--<typeAlias type="com.th.pojo.User" alias="user"/>--> </typeAliases>
-
包扫描别名
<typeAliases> <package name="com.th.pojo"/> </typeAliases>
每一个在包
com.th.pojo
中的 Java Bean,在没有注解的情况下,会使用Bean 的首字母小写的非限定类名来作为它的别名。 比如com.th.pojo.User
的别名为user
;若有注解,则别名为其注解值。@Alias("user") public class User { ... }
4. 环境配置(environments)
要求:配置多套运行环境
MyBatis可以适配多种环境,但是每一个SqlSessionFactory实例只能选择一种环境。
MyBatis默认的事物管理器是JDBC,有连接池POOLED;
5. 映射器(mappers)
作用:注册绑定Mapper文件
-
使用相对于类路径的资源引用
<mappers> <mapper resource="com/th/dao/UserMapper.xml"/> </mappers>
推荐使用,不容易出错。
-
使用映射器接口实现类的完全限定类名
<mappers> <mapper class="com.th.dao.UserMapper"/> </mappers>
注意点:接口和它的 Mapper配置文件必须同名且在同一个包下!
-
将包内的映射器接口实现全部注册为映射器
<mappers> <package name="com.th.dao"/> </mappers>
注意点:接口和它的 Mapper配置文件必须同名且在同一个包下!
-
使用完全限定资源定位符(URL)
<mappers> <mapper url="file:///var/mappers/AuthorMapper.xml"/> </mappers>
不使用。
5、resultMap
问题:数据库中的字段名与实体类中属性名不一致。导致这个字段查出数据为null。这是因为mybatis类型处理器无法找到这个属性。
作用:解决属性名与字段名不一致的问题。将数据库中字段yi
解决:
1. 使用mysql语法as取别名
2. 使用resultMap
使用resultMap
-
Mapper.xml中配置结果集映射,其中id为这个结果集映射的名字,type为返回值的类型。
<resultMap id="UserMap" type="user"> <!--column就是对应数据库中的字段,property就是对应实体类的属性--> <result column="pwd" property="password"/> </resultMap>
-
使用结果集映射
<!-resultMap中的值为以上结果集映射中定义的id值--> <select id="getUserById" parameterType="int" resultMap="UserMap"> select * from mybatis.user where id=#{id} </select>
6、分页
分页是为了减少数据的处理量
SELECT * from table limit startIndex, pagesize;
SELECT * from table limit 3; #[O, n]
使用Mybatis实现分页,核心SQL
-
编写接口
//使用万能的Map获取起始下标以及页面大小 List<User> getUserByLimit(Map<String,Integer> map);
-
Mapper.xml
<!--分页实现查询用户列表--> <select id="getUserByLimit" parameterType="map" resultMap="UserMap"> select * from mybatis.user limit #{startIndex},#{pageSize}; </select>
-
测试
public void testGetUserByLimit(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); try { UserMapper mapper = sqlSession.getMapper(UserMapper.class); HashMap<String, Integer> map = new HashMap<>(); map.put("startIndex", 1); map.put("pageSize", 2); List<User> userList = mapper.getUserByLimit(map); for (User user : userList) { System.out.println(user); } } catch (Exception e) { e.printStackTrace(); } finally { sqlSession.close(); } }
其他分页实现:RowBounds、分页插件(PageHelper)。
7、使用注解开发
-
注解的使用直接在接口中实现
@Select("select * from user") List<User> getUserList();
-
在核心配置文件中绑定接口
<mappers> <mapper class="com.th.dao.UserMapper"/> </mappers>
-
测试使用
本质:底层还是使用反射
CRUD
可以在工具类创建的时候设置自动提交事物:
public static SqlSession getSqlSession(){
//true表示自动提交事物
return sqlSessionFactory.openSession(true);
}
- 编写接口
@Select("select * from user")
List<User> getUserList();
/**当有多个参数时,所有的参数前必须加上@Param("id") 注解*/
@Select("select * from user where id=#{id}")
User getUserById(@Param("id") int ids);
@Insert("insert into user(id,name,pwd) values(#{id},#{name},#{pwd})")
int addUser(User user);
@Update("update user set name=#{name},pwd=#{pwd} where id = #{id}")
int updateUser(User user);
@Delete("delete from user where id=#{id}")
int deleteUser(@Param("id") int uid);
- 测试类
public void testT() {
//第一步:获得SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
try {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//List<User> userList = userMapper.getUserList();
//for (User user : userList) {
// System.out.println(user);
//}
//User user = userMapper.getUserById(1);
//System.out.println(user);
//UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//userMapper.addUser(new User(5, "sfgu", "abyh"));
//userMapper.updateUser(new User(5, "nihao", "nihao"));
//userMapper.deleteUser(5);
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭SqlSession
sqlSession.close();
}
}
关于@Param()注解
- 基本类型的参数或者String类型,需要加上
- 引用类型不需要加
- 如果只有一个基本类型的话,可以忽略,但是建议加上。
- 在SQL语句中的引用就是@Param("id")中设定的属性名。
关于#{}和${}的区别
-
{}是安全的,可以防止SQL注入
- 建议使用#{}
8、复杂查询
解决形如一个老师对应多个学生,多个学生对应一个老师的情况;
处理实体类
//学生实体类
@Data
public class Student {
private int id;
private String name;
//老师对象
private Teacher teacher;
}
//老师实体类
@Data
public class Teacher {
private int id;
private String name;
}
1. 多对一
子查询
<!--思路:
1. 查询出所有的学生
2. 根据查询出的学生的tid,寻求对应的老师 子查询-->
<select id="getStudentList" resultMap="StudentTeacher">
select * from student
</select>
<resultMap id="StudentTeacher" type="Student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<!--复杂的属性,我们需要单独处理 对象:association 集合:collection-->
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="Teacher">
select * from teacher where id = #{id}
</select>
javaType
:Student实体类中teacher属性是Teacher类型。
select
:子查询语句的id。
联表查询----按照结果查询
<!--==============方式二:按照结果进行嵌套==============-->
<select id="getStudentList2" resultMap="StudentTeacher2">
select s.id sid,s.name sname,t.name tname,t.id tid
from student s,teacher t
where s.tid=t.id;
</select>
<resultMap id="StudentTeacher2" type="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
<result property="id" column="tid" />
</association>
</resultMap>
查询结果:
Student(id=1, name=小明, teacher=Teacher(id=1, name=秦老师))
Student(id=2, name=小红, teacher=Teacher(id=1, name=秦老师))
Student(id=3, name=小张, teacher=Teacher(id=1, name=秦老师))
Student(id=4, name=小李, teacher=Teacher(id=2, name=黄老师))
Student(id=5, name=小王, teacher=Teacher(id=1, name=秦老师))
2. 一对多
获取指定老师的信息以及所有学生信息。
处理实体类
//学生实体类
@Data
public class Student {
private int id;
private String name;
private int tid;
}
//老师实体类
@Data
public class Teacher {
private int id;
private String name;
//老师对应的学生集合
private List<Student> students;
}
联表查询
<!--=============== 方式一 ===============-->
<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
javaType:指定属性的类型
ofType: 集合中的泛型信息
-->
<collection property="students" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
子查询
<!--===============方式二===============-->
<select id="getTeacher2" resultMap="TeacherStudent2">
select * from mybatis.teacher where id=#{tid}
</select>
<resultMap id="TeacherStudent2" type="Teacher">
<collection property="students" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId" column="id"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="Student">
select * from mybatis.student where tid=#{tid}
</select>
建议使用联表查询。
9、动态SQL
什么是动态SQL:动态SQL指的是根据不同的条件生成不同的SQL语句。
搭建环境
CREATE TABLE `blog`(
`id` VARCHAR(50) NOT NULL COMMENT '博客id',
`title` VARCHAR(100) NOT NULL COMMENT '博客标题',
`author` VARCHAR(30) NOT NULL COMMENT '博客作者',
`create_time` DATETIME NOT NULL COMMENT '创建时间',
`views` INT(30) NOT NULL COMMENT '浏览量'
)ENGINE=INNODB DEFAULT CHARSET=utf8
实体类
@Data
public class Blog {
private String id;
private String title;
private String author;
/**属性名和数据库字段名不一致,可以在配置文件中设置mapUnderscoreToCamelCase值
<setting name="mapUnderscoreToCamelCase" value="true"/> */
private Date createTime;
private int views;
}
使用UUID类创建ID
public class IDutils {
public static String getId(){
return UUID.randomUUID().toString().replace("-", "");
}
@Test
public void test(){
System.out.println(IDutils.getId());
}
}
1. if
Mapper.xml
<select id="queryBlogIf" parameterType="map" resultType="blog">
select * from mybatis.blog where 1=1
<if test="titleaa != null">
and title = #{titleaa}
</if>
<if test="authoraa != null">
and author = #{authoraa}
</if>
</select>
if
标签内test
表示条件。
但是这里还是会出现一个问题就是多余了 where 1=1
,这是不正规,但是如果没有这个条件在拼接SQL时出现AND
和OR
,导致语法错误。
改造,使用where
标签。
<select id="queryBlogIf" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<if test="titleaa != null">
and title = #{titleaa}
</if>
<if test="authoraa != null">
and author = #{authoraa}
</if>
</where>
</select>
测试
public void testqueryBlogIf(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
try {
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
// map.put("titleaa", "Java");
map.put("authoraa", "狂神说");
List<Blog> blogs = mapper.queryBlogIf(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
2. choose (when, otherwise)
choose
:MyBatis 提供了的choose 元素像 Java 中的 switch 语句。when
:当以第一满足条件时直接拼接后续即便满足也不会再拼接。otherwise
:当when条件都满足时拼接。
Mapper.xml
<select id="queryBlogChoose" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<choose>
<when test="titleaa!=null">
and title=#{titleaa}
</when>
<when test="authoraa!=null">
and author=#{authoraa}
</when>
<otherwise>
and views=#{viewaa}
</otherwise>
</choose>
</where>
</select>
测试
public void testqueryBlogChoose(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
try {
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
// map.put("titleaa", "Java");
// map.put("authoraa", "狂神说");
map.put("viewaa", 9999);
List<Blog> blogs = mapper.queryBlogChoose(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
3. trim (where, set)
Mapper.xml
<update id="updateBlog" parameterType="map">
update mybatis.blog
<set>
<if test="title!=null">
title=#{title},
</if>
<if test="author!=null">
author=#{author}
</if>
</set>
where id=#{id}
</update>
set
元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。
可以自定义 trim
元素来定制where
元素的功能。
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
测试
public void testUpdateBlog(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
try {
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
map.put("title", "JavaWeb");
map.put("author", "狂神说Java");
map.put("id", "89fde7b2e83e4ae3b2505b3fec27967f");
mapper.updateBlog(map);
} catch (Exception e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
4. foreach
对集合进行遍历(尤其是在构建 IN 条件语句的时候)。
Mapper.xml
<select id="queryBlogForeach" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<foreach collection="ids" item="id" open="(" close=")" separator="or">
id=#{id}
</foreach>
</where>
</select>
测试
public void testQueryBlogForeach(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
try {
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap hashMap = new HashMap();
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(3);
list.add(4);
hashMap.put("ids", list);
List<Blog> blogs = mapper.queryBlogForeach(hashMap);
for (Blog blog : blogs) {
System.out.println(blog);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
}
sqlSession.close();
}
拼接的SQL
==> Preparing: select * from mybatis.blog WHERE ( id=? or id=? or id=? )
==> Parameters: 1(Integer), 3(Integer), 4(Integer)
5、SQL片段
将某些公共SQL提取出来,方便复用。
-
使用
sql
标签抽取出公共部分。<sql id="queryBlogIf-sql"> <if test="titleaa!=null"> title=#{titleaa} </if> <if test="authoraa!=null"> and author=#{authoraa} </if> </sql>
-
在需要使用的时候使用
include
标签引用即可。<select id="queryBlogIf" parameterType="map" resultType="blog"> select * from mybatis.blog <where> <include refid="queryBlogIf-sql"></include> </where> </select>
-
注意
- 最好基于单表定义SQL片段,多张表时不适用。
- 不要存在
where
标签。
10、缓存
1. 一级缓存
一级缓存也叫本地缓存: SqlSession
- 与数据库同一次会话期间查询的数据会放在本地缓存中。
- 以后如果需要获取相同数据,直接从缓存中取,不在查询数据库。
- 一级缓存默认开启,也无法关闭。
测试
-
开启日志
-
测试在一个SqlSession中查询两次相同记录
public void testQueryUserById(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); try { UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.queryUserById(2); System.out.println(user); System.out.println("==========一级缓存在sqlSession中有效,默认开启============"); User user2 = mapper.queryUserById(2); System.out.println(user2); System.out.println("===============user == user2======="); System.out.println(user == user2); } catch (Exception e) { e.printStackTrace(); } finally { sqlSession.close(); } }
-
查看日志输出
- 缓存失效
- 查询不同记录
- 增删改改变原来的记录,导致刷新缓存
- 查询不同的Mapper.xml
- 手动清理缓存
sqlSession.clearCache();
2. 二级缓存
二级缓存也叫全局缓存,解决一级缓存作用域低的问题
- 基于namespace级别的缓存,一个名称空间,对应一个二级缓存。
- 工作机制:
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中。
- 如果会话关闭,这个对话的一级缓存就消失了,但是一级缓存中的可以保存到二级缓存。
- 新的会话查询信息,就可以从二级缓存中获取内容。
- 不同的mapper查询出的数据会放在自己对应的缓存(map)中。
测试
-
显示开启全局缓存
<settings> <!--显示的开启全局缓存,既二级缓存,虽然这个默认是开启的--> <setting name="cacheEnabled" value="true"/> </settings>
-
在当前Mapper中开启二级缓存
<cache/>
<cache/>
当然也可以自定义参数
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
查询顺序: 先查看二级缓存中是否存在===》一级缓存中是否存在 ===》 数据库
3. 自定义缓存——Ehcache
Ehcache是一种广泛使用的开源Java分布式缓存,主要面向通用缓存。
-
导入依赖
<dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.1.0</version> </dependency>
-
在当前Mapper.xml文件中使用自定义缓存
<cache type="org.mybatis.caches.ehcache.EhcacheCache" />
-
添加配置文件
ehcache.xml
。<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false"> <diskStore path="./tmpdir/Tmp_EhCache"/> <defaultCache eternal="false" maxElementsInMemory="10000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="1800" timeToLiveSeconds="259200" memoryStoreEvictionPolicy="LRU"/> <cache name="cloud_user" eternal="false" maxElementsInMemory="5000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="1800" timeToLiveSeconds="1800" memoryStoreEvictionPolicy="LRU"/> </ehcache>