环境说明:html
学习前须要掌握:java
如何得到 MyBatis?mysql
maven仓库:git
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency> 123456
Github : https://github.com/mybatis/mybatis-3/releasesgithub
数据持久化shell
为何须要须要持久化?数据库
Dao 层,Service 层,Controller 层…apache
最重要的一点:使用的人多!缓存
CREATE DATABASE `mybatis`; USE `mybatis`; DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(20) NOT NULL, `name` varchar(30) DEFAULT NULL, `pwd` varchar(30) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; insert into `user`(`id`,`name`,`pwd`) values (1,'狂神','123456'),(2,'张三','abcdef'),(3,'李四','987654');
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency>
能够根据帮助文档来进行编写
<?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.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/kuang/dao/userMapper.xml"/> </mappers> </configuration>
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 java.io.IOException; import java.io.InputStream; public class MybatisUtils { private static SqlSessionFactory sqlSessionFactory; static { try { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } //获取SqlSession链接 public static SqlSession getSession(){ return sqlSessionFactory.openSession(); } }
public class User { private int id; //id private String name; //姓名 private String pwd; //密码 //构造,有参,无参 //set/get //toString() }
import com.kuang.pojo.User; import java.util.List; public interface UserMapper { List<User> selectUser(); }
注意 namespace
不要写错!
<?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.kuang.dao.UserMapper"> <select id="selectUser" resultType="com.kuang.pojo.User"> select * from user </select> </mapper>
**junit 包测试 **
public class MyTest { @Test public void selectUser() { SqlSession session = MybatisUtils.getSession(); //方法一: //List<User> users = session.selectList("com.kuang.mapper.UserMapper.selectUser"); //方法二: UserMapper mapper = session.getMapper(UserMapper.class); List<User> users = mapper.selectUser(); for (User user: users){ System.out.println(user); } session.close(); } }
一、Maven 静态资源过滤问题
<resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources>
select标签是mybatis中最经常使用的标签之一
select语句有不少属性能够详细配置每一条SQL语句
练习 1 :根据 id 查询 用户
1.在 UserMapper 中添加对应方法
public interface UserMapper { //查询所有用户 List<User> selectUser(); //根据id查询用户 User selectUserById(int id); }
2.在UserMapper.xml中添加 select
语句
<select id="selectUserById" resultType="com.anti.pojo.User"> select * from user where id = #{id} </select>
3.在测试类中测试
@Test public void tsetSelectUserById() { SqlSession session = MybatisUtils.getSession(); //获取SqlSession链接 UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.selectUserById(1); System.out.println(user); session.close(); }
4.运行结果
练习2:根据 密码 和名字 查询用户
方法一:直接在方法中传递参数
@Param
属性。@Param
中设置的值便可,不须要到单独设置参数类型。//经过密码和名字查询用户 User selectUserByNP(@Param("username") String username,@Param("pwd") String pwd); //mapper.xml <select id="selectUserByNP" resultType="com.kuang.pojo.User"> select * from user where name = #{username} and pwd = #{pwd} </select>
方法二:万能Map
User selectUserByNP2(Map<String,Object> map);
parameterType="map"
<select id="selectUserByNP2" parameterType="map" resultType="com.kuang.pojo.User"> select * from user where name = #{username} and pwd = #{pwd} </select>
@Test public void test03(){ SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); HashMap<String, Object> map = new HashMap<String, Object>(); map.put("username","张三"); map.put("pwd","abcdef"); User user = mapper.selectUserByNP2(map); System.out.println(user); }
若是参数过多,咱们能够考虑直接使用 Map 实现,若是参数比较少,直接传递参数便可。
咱们通常使用 insert 标签进行插入操做,它的配置和 select 标签差很少.
练习1:增长一个用户
1.在 UserMapper 接口中添加对应的方法
//添加一个用户 int addUser(User user);
二、在UserMapper.xml中添加insert语句
<insert id="addUser" parameterType="com.anti.pojo.User"> insert into user (id,name,pwd) values (#{id},#{name},#{pwd}) </insert>
3.测试
@Test public void testAddUser() { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = new User(5,"王五","zxcvbn"); int i = mapper.addUser(user); System.out.println(i); session.commit(); //提交事务,重点!不写的话不会提交到数据库 session.close(); }
注意点:增、删、改操做须要提交事务!
咱们通常使用update标签进行更新操做,它的配置和select标签差很少。
练习:修改用户的信息
一、同理,编写接口方法
//修改一个用户 int updateUser(User user);
二、编写对应的配置文件SQL
<update id="updateUser" parameterType="com.kuang.pojo.User"> update user set name=#{name},pwd=#{pwd} where id = #{id} </update>
三、测试
@Test public void testUpdateUser() { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.selectUserById(1); user.setPwd("asdfgh"); int i = mapper.updateUser(user); System.out.println(i); session.commit(); //提交事务,重点!不写的话不会提交到数据库 session.close(); }
需求:根据id删除一个用户
一、同理,编写接口方法
//根据id删除用户 int deleteUser(int id);
二、编写对应的配置文件SQL
<delete id="deleteUser" parameterType="int"> delete from user where id = #{id} </delete>
三、测试
@Test public void testDeleteUser() { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); int i = mapper.deleteUser(5); System.out.println(i); session.commit(); //提交事务,重点!不写的话不会提交到数据库 session.close(); }
小结:
@Param
参数,尤为是多个参数时,必须写上!parameterType
和 resultType
都写上!第1种(推荐):在 Java代码中添加 SQL通配符。
List<User> users = mapper.selectLikeUser("%朱%");
<select id="selectLikeUser"> select * from user where name like #{name} </select>
第2种(不推荐):在 SQL 语句中拼接通配符,会引发 SQL 注入。
String name = "朱"; List<User> users = mapper.selectLikeUser(name);
<select id="selectLikeUser"> select * from user where name like "%" #{name} "%" </select>
configuration(配置) properties(属性) settings(设置) typeAliases(类型别名) typeHandlers(类型处理器) objectFactory(对象工厂) plugins(插件) environments(环境配置) environment(环境变量) transactionManager(事务管理器) dataSource(数据源) databaseIdProvider(数据库厂商标识) mappers(映射器) <!-- 注意元素节点的顺序!顺序不对会报错 -->
咱们能够阅读 mybatis-config.xml
上面的 dtd
的头文件!
<environments default="development"> <environment id="development"> <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=utf8"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments>
配置 MyBatis 的多套运行环境,将 SQL 映射到多个不一样的数据库上,必须指定其中一个为默认运行环境(经过default指定)
子元素节点:environment
dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 链接对象的资源。
数据源是必须配置的。
有三种内建的数据源类型
type="[UNPOOLED|POOLED|JNDI]")
UNPOOLED
:这个数据源的实现只是每次被请求时打开和关闭链接。
POOLED
:这种数据源的实现利用“池”的概念将 JDBC 链接对象组织起来 , 这是一种使得并发 Web 应用快速响应请求的流行处理方式。
JNDI
:这个数据源的实现是为了能在如 Spring 或应用服务器这类容器中使用,容器能够集中或在外部配置数据源,而后放置一个 JNDI 上下文的引用。
数据源也有不少第三方的实现,好比dbcp,c3p0,druid等等....
子元素节点:transactionManager
- [ 事务管理器 ]
<!-- 语法 --> <transactionManager type="[ JDBC | MANAGED ]"/>
mappers
file:///
的 URL),或类名和包名等。映射器是MyBatis中最核心的组件之一,在MyBatis 3以前,只支持xml映射器,即:全部的SQL语句都必须在xml文件中配置。而从MyBatis 3开始,还支持接口映射器,这种映射器方式容许以Java代码的方式注解定义SQL语句,很是简洁。<!-- 使用相对于类路径的资源引用 --> <mappers> <mapper resource="org/mybatis/builder/PostMapper.xml"/> </mappers>
<!-- 使用彻底限定资源定位符(URL) --> <mappers> <mapper url="file:///var/mappers/AuthorMapper.xml"/> </mappers>
<!-- 使用映射器接口实现类的彻底限定类名 须要配置文件名称和接口名称一致,而且位于同一目录下 --> <mappers> <mapper class="org.mybatis.builder.AuthorMapper"/> </mappers>
<!-- 将包内的映射器接口实现所有注册为映射器 可是须要配置文件名称和接口名称一致,而且位于同一目录下 --> <mappers> <package name="org.mybatis.builder"/> </mappers>
mapper文件:
<?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.kuang.mapper.UserMapper"> </mapper>
namespace 中文意思:命名空间,做用以下:
namespace 和子元素的 id 联合保证惟一 , 区别不一样的mapper
绑定 DAO 接口
namespace 命名规则 : 包名 + 类名
数据库这些属性都是可外部配置且可动态替换的,既能够在典型的 Java 属性文件中配置,亦可经过 properties 元素的子元素来传递。具体的请参考官方文档
咱们来优化咱们的配置文件
第一步 ; 在resources
资源目录下新建一个 db.properties
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8&serverTimezone=UTC username=root password=123456
第二步 : 将文件导入 properties
配置文件
<configuration> <!--导入properties文件--> <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> <mapper resource="mapper/UserMapper.xml"/> </mappers> </configuration>
类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减小类彻底限定名的冗余。
<!--配置别名,注意顺序--> <typeAliases> <typeAlias type="com.anti.pojo.User" alias="User"/> </typeAliases>
当这样配置时,User
能够用在任何使用 com.kuang.pojo.User
的地方。
也能够指定一个包名,MyBatis 会在包名下面搜索须要的 Java Bean,好比:
<typeAliases> <package name="com.anti.pojo"/> </typeAliases>
每个在包 com.anti.pojo
中的 Java Bean,在没有注解的状况下,会使用 Bean 的首字母小写的非限定类名来做为它的别名。
如有注解,则别名为其注解值。见下面的例子:
@Alias("user") public class User { ... }
去官网查看一下Mybatis默认的一些类型别名: https://mybatis.org/mybatis-3/zh/configuration.html#typeAliases
设置(settings)相关 => 查看帮助文档
懒加载
日志实现
缓存开启关闭
一个配置完整的 settings 元素的示例以下:
<settings> <setting name="cacheEnabled" value="true"/> <setting name="lazyLoadingEnabled" value="true"/> <setting name="multipleResultSetsEnabled" value="true"/> <setting name="useColumnLabel" value="true"/> <setting name="useGeneratedKeys" value="false"/> <setting name="autoMappingBehavior" value="PARTIAL"/> <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/> <setting name="defaultExecutorType" value="SIMPLE"/> <setting name="defaultStatementTimeout" value="25"/> <setting name="defaultFetchSize" value="100"/> <setting name="safeRowBoundsEnabled" value="false"/> <setting name="mapUnderscoreToCamelCase" value="false"/> <setting name="localCacheScope" value="SESSION"/> <setting name="jdbcTypeForNull" value="OTHER"/> <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/> </settings>
理解咱们目前已经讨论过的不一样做用域和生命周期类是相当重要的,由于错误的使用会致使很是严重的并发问题。
咱们能够先画一个流程图,分析一下 Mybatis 的执行过程!
null
能够看到查询出来的结果集中 username
属性为 null
。
MyBatis 会根据这些查询的列名(会将列名转化为小写,数据库不区分大小写) , 去对应的实体类中查找相应列名的 set方法
设值 , 因为找不到 setUsernmae()
, 因此 username 返回 null ; 【自动映射】
1.【不推荐】修改实体类的属性名,使其和数据库字段名一致。
2.【不推荐】在 SQL 语句中使用别名对应实体类中的属性名。
3.【推荐】使用在 xxMapper.xml` 中使用 ResultMap 进行结果集映射。
【多对一】的处理:
【一对多】的处理:
一、关联 - association
二、集合 - collection
三、因此 association
是用于一对一和多对一,而 collection
是用于一对多的关系
四、JavaType
和 ofType
都是用来指定对象类型的
JavaType
是用来指定 pojo
中属性的类型ofType
指定的是映射到 List
集合属性中 pojo
的类型。注意说明:
一、保证SQL的可读性,尽可能通俗易懂
二、根据实际要求,尽可能编写性能更高的SQL语句
三、注意属性名和字段不一致的问题
四、注意一对多和多对一 中:字段和属性对应的问题
五、尽可能使用Log4j,经过日志来查看本身的错误
咱们在测试 SQL 的时候,要是可以在控制台输出 SQL 的话,是否是就可以有更快的排错效率?
若是一个 数据库相关的操做出现了问题,咱们能够根据输出的 SQL 语句快速排查问题。
对于以往的开发过程,咱们会常用到debug模式来调节,跟踪咱们的代码执行过程。可是如今使用 Mybatis 是基于接口,配置文件的源代码执行过程。所以,咱们必须选择日志工具来做为咱们开发,调节程序的工具。
Mybatis内置的日志工厂提供日志功能,具体的日志实现有如下几种工具:
具体选择哪一个日志实现工具由 MyBatis 的内置日志工厂肯定。它会使用最早找到的(按上文列举的顺序查找)。若是一个都未找到,日志功能就会被禁用。
指定 MyBatis 应该使用哪一个日志记录实现。若是此设置不存在,则会自动发现日志记录实现。
<settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
测试,能够看到控制台有大量的输出!咱们能够经过这些输出来判断程序到底哪里出了Bug
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. Opening JDBC Connection Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary. Created connection 355115154. Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@152aa092] ==> Preparing: select * from teacher where id = ? ==> Parameters: 1(Integer) <== Columns: id, name <== Row: 1, 秦老师 ====> Preparing: select * from student ====> Parameters: <==== Columns: id, name, tid <==== Row: 1, 小明, 1 <==== Row: 2, 小红, 1 <==== Row: 3, 小张, 1 <==== Row: 4, 小李, 1 <==== Row: 5, 小王, 1 <==== Total: 5 <== Total: 1 Teacher(id=null, name=秦老师, students=[Student(id=1, name=小明), Student(id=2, name=小红), Student(id=3, name=小张), Student(id=4, name=小李), Student(id=5, name=小王)])
简介:
使用步骤:
一、导入log4j的包
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
二、配置文件编写
#将等级为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/kuang.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
三、setting设置日志实现
<settings> <setting name="logImpl" value="LOG4J"/> </settings>
输出结果:
[org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 71706941. [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@446293d] [com.anti.dao.TeacherMapper.getTeacherById]-==> Preparing: select * from teacher where id = ? [com.anti.dao.TeacherMapper.getTeacherById]-==> Parameters: 1(Integer) [com.anti.dao.TeacherMapper.student]-====> Preparing: select * from student [com.anti.dao.TeacherMapper.student]-====> Parameters: [com.anti.dao.TeacherMapper.student]-<==== Total: 5 [com.anti.dao.TeacherMapper.getTeacherById]-<== Total: 1 Teacher(id=null, name=秦老师, students=[Student(id=1, name=小明), Student(id=2, name=小红), Student(id=3, name=小张), Student(id=4, name=小李), Student(id=5, name=小王)])
在学习 MyBatis 等持久层框架的时候,会常常对数据进行增删改查操做,使用最多的是对数据库进行查询操做,若是查询大量数据的时候,咱们每每使用分页进行查询,也就是每次处理小部分数据,这样对数据库压力就在可控范围内。
使用Limit实现分页
#语法 SELECT * FROM table LIMIT page,pageSize page = 当前页数 = (page-1)*pageSize pageSize = 一页多少条数据
UserMapper.java
public interface UserMapper { //选择所有用户实现分页 List<User> selectUser(Map<String,Integer> map); }
UserMapper.xml
<select id="selectUser" parameterType="map" resultType="com.anti.pojo.User"> select * from user limit #{page},#{pageSize} </select>
MyTest.java
@Test public void test01(){ SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); HashMap<String, Integer> map = new HashMap<String, Integer>(); int page = 1; //第几页 int limit = 10; //每页多少数据 map.put("page",(page-1) * limit); map.put("limit",limit); List<User> users = mapper.selectUser(map); for (User user : users) { System.out.println(user); } session.close(); }
结果 :
==> Preparing: select * from user limit ?,? ==> Parameters: 0(Integer), 10(Integer) <== Columns: uid, name, gender, birthday, dept, cno, address, phone, remark, password, type <== Row: 999, 管理员, M, 2020/09/02, AAA, 0, AAA, null, null, 123, 0 <== Row: 10101, 怡香, M, 2020/02/08, Accounting, 1, Cameroon, 823-954-4217, null, 1, 1 <== Row: 10102, 惟枫, M, 2020/01/25, Accounting, 2, Palestinian Territory, 978-827-9275, null, 2, 1 <== Row: 10103, 海程, M, 2020/09/05, Human Resources, 3, Thailand, 978-712-9955, null, 3, 1 <== Row: 10104, 琪煜, M, 2020/10/07, Accounting, 4, Palestinian Territory, 730-153-0025, null, 4, 1 <== Row: 10105, 彦军, F, 2020/11/05, Services, 5, China, 504-460-1356, null, 5, 1 <== Row: 10106, 宇涵, F, 2020/11/08, Product Management, 6, Argentina, 252-143-6848, null, 6, 1 <== Row: 10107, 辰华, M, 2019/11/25, Business Development, 7, Philippines, 884-928-7856, null, 7, 1 <== Row: 10108, 晓烽, M, 2020/08/05, Engineering, 8, Philippines, 152-366-5638, null, 8, 1 <== Row: 10109, 尹智, F, 2020/01/12, Human Resources, 9, Argentina, 803-602-3704, null, 9, 1 <== Total: 10
MyBatis最初配置信息是基于 XML ,映射语句(SQL)也是定义在 XML 中的。而到MyBatis 3提供了新的基于注解的配置。不幸的是,java 注解的的表达力和灵活性十分有限。
最强大的 MyBatis 映射并不能用注解来构建
@select ()
@update ()
@Insert ()
@delete ()
注意:利用注解开发就不须要mapper.xml映射文件了 .
1.在接口中添加注解
public interface UserMapper { //查询所有用户 @Select("select * from user") List<User> getUsers(); }
2.在mybatis的核心配置文件中注入
<!--使用class绑定接口--> <mappers> <mapper class="com.anti.mapper.UserMapper"/> </mappers>
3.测试
@Test public void test01(){ UserMapper mapper = MybatisUtils.getSession().getMapper(UserMapper.class); List<User> users = mapper.getUsers(); for (User user : users) { System.out.println(user); } }
4.结果
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3c0be339] ==> Preparing: select * from user ==> Parameters: <== Columns: id, name, pwd <== Row: 1, 狂神, 123456 <== Row: 2, 张三, abcdef <== Row: 3, 李四, 987654 <== Row: 5, 王五, zxcvbn <== Total: 4 User(id=1, name=狂神, pwd=123456) User(id=2, name=张三, pwd=abcdef) User(id=3, name=李四, pwd=987654) User(id=5, name=王五, pwd=zxcvbn) Process finished with exit code 0
<!--需求1: 根据做者名字和博客名字来查询博客! 若是做者名字为空,那么只根据博客名字查询,反之,则根据做者名来查询 select * from blog where title = #{title} and author = #{author} --> <select id="queryBlogIf" parameterType="map" resultType="blog"> select * from blog where <if test="title != null"> title = #{title} </if> <if test="author != null"> and author = #{author} </if> </select>
这样写咱们能够看到,若是 author
等于 null
,那么查询语句为 select * from user where title=#{title}
,可是若是 title
为空呢?那么查询语句为 select * from user where and author=#{author}
,这是错误的 SQL 语句,如何解决呢?请看下面的 where 语句!
<select id="queryBlogIf" parameterType="map" resultType="blog"> select * from blog <where> <if test="title != null"> title = #{title} </if> <if test="author != null"> and author = #{author} </if> </where> </select>
这个“where”标签会知道若是它包含的标签中有返回值的话,它就插入一个‘where’。此外,若是标签返回的内容是以AND 或OR 开头的,则它会剔除掉。
同理,上面的对于查询 SQL 语句包含 where 关键字,若是在进行更新操做的时候,含有 set 关键词,咱们怎么处理呢?
<!--注意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>
有时候,咱们不想用到全部的查询条件,只想选择其中的一个,查询条件有一个知足便可,使用 choose 标签能够解决此类问题,相似于 Java 的 switch 语句。
choose 与 if 的区别:
<select id="queryBlogChoose" parameterType="map" resultType="blog"> select * from blog <where> <choose> <when test="title != null"> title = #{title} </when> <when test="author != null"> and author = #{author} </when> <otherwise> and views = #{views} </otherwise> </choose> </where> </select>
有时候可能某个 sql 语句咱们用的特别多,为了增长代码的重用性,简化代码,咱们须要将这些代码抽取出来,而后使用时直接调用。
提取SQL片断:
<sql id="if-title-author"> <if test="title != null"> title = #{title} </if> <if test="author != null"> and author = #{author} </if> </sql>
引用SQL片断:
<select id="queryBlogIf" parameterType="map" resultType="blog"> select * from blog <where> <!-- 引用 sql 片断,若是refid 指定的不在本文件中,那么须要在前面加上 namespace --> <include refid="if-title-author"></include> <!-- 在这里还能够引用其余的 sql 片断 --> </where> </select>
注意:
需求:咱们须要查询 blog 表中 id 分别为1,2,3的博客信息
<select id="queryBlogForeach" parameterType="map" resultType="blog"> select * from blog <where> <!-- collection:指定输入对象中的集合属性 item:每次遍历生成的对象 open:开始遍历时的拼接字符串 close:结束时拼接的字符串 separator:遍历对象之间须要拼接的字符串 select * from blog where 1=1 and (id=1 or id=2 or id=3) --> <foreach collection="ids" item="id" open="and (" close=")" separator="or"> id=#{id} </foreach> </where> </select>
小结:
其实动态 sql 语句的编写每每就是一个拼接的问题,为了保证拼接准确,咱们最好首先要写原生的 sql 语句出来,而后在经过 mybatis 动态sql 对照着改,防止出错。多在实践中使用才是熟练掌握它的技巧。
一、什么是缓存 [ Cache ]?
二、为何使用缓存?
三、什么样的数据能使用缓存?
MyBatis包含一个很是强大的查询缓存特性,它能够很是方便地定制和配置缓存。缓存能够极大的提高查询效率。
MyBatis系统中默认定义了两级缓存:一级缓存 和 二级缓存
默认状况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
二级缓存须要手动开启和配置,他是基于namespace级别的缓存。
为了提升扩展性,MyBatis定义了缓存接口Cache。咱们能够经过实现Cache接口来自定义二级缓存
一级缓存也叫本地缓存:
与数据库同一次会话期间查询到的数据会放在本地缓存中。
之后若是须要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;
1.在mybatis中加入日志,方便测试结果
2.编写接口方法
//根据id查询用户 User queryUserById(@Param("id") int id);
3.接口对应的Mapper文件
<select id="queryUserById" resultType="user"> select * from user where id = #{id} </select>
4.测试
@Test public void testQueryUserById(){ SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); User user2 = mapper.queryUserById(1); System.out.println(user2); System.out.println(user==user2); session.close(); }
5.结果分析
一级缓存是SqlSession级别的缓存,是一直开启的,咱们关闭不了它;
一级缓存失效状况:没有使用到当前的一级缓存,效果就是,还须要再向数据库中发起一次查询请求!
1.sqlSession不一样
@Test public void testQueryUserById(){ SqlSession session = MybatisUtils.getSession(); SqlSession session2 = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); UserMapper mapper2 = session2.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); User user2 = mapper2.queryUserById(1); System.out.println(user2); System.out.println(user==user2); session.close(); session2.close(); }
观察结果:发现发送了两条SQL语句!
结论:每一个sqlSession中的缓存相互独立
2.sqlSession相同,查询条件不一样
@Test public void testQueryUserById(){ SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); UserMapper mapper2 = session.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); User user2 = mapper2.queryUserById(2); System.out.println(user2); System.out.println(user==user2); session.close(); }
观察结果:发现发送了两条SQL语句!很正常的理解
结论:当前缓存中,不存在这个数据
3.sqlSession相同,两次查询之间执行了增删改操做!
@Test public void testQueryUserById(){ SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); HashMap map = new HashMap(); map.put("name","kuangshen"); map.put("id",4); mapper.updateUser(map); User user2 = mapper.queryUserById(1); System.out.println(user2); System.out.println(user==user2); session.close(); }
观察结果:查询在中间执行了增删改操做后,从新执行了
结论:由于增删改操做可能会对当前数据产生影响
4.sqlSession相同,手动清除一级缓存
@Test public void testQueryUserById(){ SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); session.clearCache();//手动清除缓存 User user2 = mapper.queryUserById(1); System.out.println(user2); System.out.println(user==user2); session.close(); }
一级缓存就是一个map
1.开启全局缓存 【mybatis-config.xml】
<setting name="cacheEnabled" value="true"/>
2.去每一个mapper.xml中配置使用二级缓存,这个配置很是简单;【xxxMapper.xml】
<cache/> 官方示例=====>查看官方文档 <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true" /> 这个更高级的配置建立了一个 FIFO 缓存,每隔 60 秒刷新,最多能够存储结果对象或列表的 512 个引用,并且返回的对象被认为是只读的,所以对它们进行修改可能会在不一样线程中的调用者产生冲突。
3.代码测试
@Test public void testQueryUserById(){ SqlSession session = MybatisUtils.getSession(); SqlSession session2 = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); UserMapper mapper2 = session2.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); session.close(); User user2 = mapper2.queryUserById(1); System.out.println(user2); System.out.println(user==user2); session2.close(); }
结论:
缓存原理图:
Ehcache是一种普遍使用的 java 分布式缓存,用于通用缓存;
1.要在应用程序中使用Ehcache,须要引入依赖的 jar 包
<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache --> <dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.1.0</version> </dependency>
2.在mapper.xml中使用对应的缓存便可
<mapper namespace = “org.acme.FooMapper” > <cache type = “org.mybatis.caches.ehcache.EhcacheCache” /> </mapper>
3.编写ehcache.xml文件,若是在加载时未找到/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:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释以下: user.home – 用户主目录 user.dir – 用户当前工做目录 java.io.tmpdir – 默认临时文件路径 --> <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"/> <!-- defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。 --> <!-- name:缓存名称。 maxElementsInMemory:缓存最大数目 maxElementsOnDisk:硬盘最大缓存个数。 eternal:对象是否永久有效,一但设置了,timeout将不起做用。 overflowToDisk:是否保存到磁盘,当系统当机时 timeToIdleSeconds:设置对象在失效前的容许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。 timeToLiveSeconds:设置对象在失效前容许存活时间(单位:秒)。最大时间介于建立时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。 diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false. diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每一个Cache都应该有本身的一个缓冲区。 diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。 memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你能够设置为FIFO(先进先出)或是LFU(较少使用)。 clearOnFlush:内存数量最大时是否清除。 memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。 FIFO,first in first out,这个是你们最熟的,先进先出。 LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。 LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又须要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。 --> </ehcache>