学习资源整理自:B站《狂神说》
扎实基础,将Mybatis全面走一遍!
Mybatis中文官网
数据库表环境
CREATE TABLE `smart_user` ( `ID` int(11) NOT NULL, `NAME` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `PASSWORD` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`ID`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of smart_user -- ---------------------------- INSERT INTO `smart_user` VALUES (1, '阿猫', '123'); INSERT INTO `smart_user` VALUES (2, '阿狗', '1234'); INSERT INTO `smart_user` VALUES (3, '阿猪', '112233'); INSERT INTO `smart_user` VALUES (4, '阿马', NULL);手写一个纯净的Mybatis项目
从官网入手:https://mybatis.org/mybatis-3/zh/getting-started.html
配置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> <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="org/mybatis/example/BlogMapper.xml"/> </mappers> </configuration> 构建数据库链接对象sql需要发送到数据库,则需要SqlSession,编写工具类
public class MybatisUtils { //链接工厂 private static SqlSessionFactory sqlSessionFactory; static{ try { //构建sqlSession String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } /** * 获取SqlSession * @return */ private static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(); } } 编写dao/mapper想要在数据库进行操作,就得通过sql语句
package com.ssx.dao; import com.ssx.entity.User; import java.util.List; public interface UserMapper { List<User> getUserList(); }实现dao接口的映射文件
<!--命名空间 绑定dao/mapper接口--> <mapper namespace="com.ssx.dao.UserMapper"> <select id="getUserList" resultType="com.ssx.entity.User"> select * from smart_user </select> </mapper> 执行sql读取数据 @Test public void test(){ //1、获取SqlSession SqlSession sqlSession = MybatisUtils.getSqlSession(); //2、执行sql //方式一,使用mapper UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> userList = mapper.getUserList(); //方式二,全限定名 不推荐 //List<User> userList = sqlSession.selectList("com.ssx.dao.UserMapper.getUserList"); userList.forEach(System.out::println); sqlSession.close(); }console
User(id=1, name=阿猫, password=123) User(id=2, name=阿狗, password=1234)id : 对应mapper接口中的方法
resultType:返回值类型
parameter:参数类型
mybatis.xml的标签是有序的。
引用官网资料
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:
configuration(配置) properties(属性)settings(设置)typeAliases(类型别名)typeHandlers(类型处理器)objectFactory(对象工厂)plugins(插件)environments(环境配置) environment(环境变量) transactionManager(事务管理器)dataSource(数据源) databaseIdProvider(数据库厂商标识)mappers(映射器)首先可以配置多个连接环境,但SqlSessionFactory实例只有一个
transactionManager
默认使用jdbc事务,还有一个managed几乎不用(EJB)
dataSource
有三种数据源类型:unpooled/pooled(默认)/jndi
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件(db.properties)中配置这些属性,也可以在 properties 元素的子元素中设置。例如:
<properties resource="org/mybatis/example/config.properties"> <property name="username" value="dev_user"/> <property name="password" value="F2Fa3!33TYyg"/> </properties>设置好的属性可以在整个配置文件中用来替换需要动态配置的属性值。比如:
<dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource>单个实体类设置别名
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如:
<typeAliases> <typeAlias alias="Author" type="domain.blog.Author"/> <typeAlias alias="Blog" type="domain.blog.Blog"/> <typeAlias alias="Comment" type="domain.blog.Comment"/> <typeAlias alias="Post" type="domain.blog.Post"/> <typeAlias alias="Section" type="domain.blog.Section"/> <typeAlias alias="Tag" type="domain.blog.Tag"/> </typeAliases>当这样配置时,Blog 可以用在任何使用 domain.blog.Blog 的地方。
扫描整个entity包*
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
<typeAliases> <package name="domain.blog"/> </typeAliases>每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;
使用注解设置别名
若有注解,则别名为其注解值。见下面的例子:
@Alias("author") public class Author { ... }这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等。
设置名描述有效值默认值cacheEnabled全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。true | falsetruelazyLoadingEnabled延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。true | falsefalselogImpl指定 MyBatis 所用日志的具体实现,未指定时将自动查找。SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING未设置既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等。例如:
<!-- 使用相对于类路径的资源引用 推荐使用 --> <mappers> <mapper resource="org/mybatis/builder/AuthorMapper.xml"/> <mapper resource="org/mybatis/builder/BlogMapper.xml"/> <mapper resource="org/mybatis/builder/PostMapper.xml"/> </mappers> <!-- 使用完全限定资源定位符(URL) --> <mappers> <mapper url="file:///var/mappers/AuthorMapper.xml"/> <mapper url="file:///var/mappers/BlogMapper.xml"/> <mapper url="file:///var/mappers/PostMapper.xml"/> </mappers> <!-- 使用映射器接口实现类的完全限定类名 --> <mappers> <mapper class="org.mybatis.builder.AuthorMapper"/> <mapper class="org.mybatis.builder.BlogMapper"/> <mapper class="org.mybatis.builder.PostMapper"/> </mappers> <!-- 将包内的映射器接口实现全部注册为映射器 --> <mappers> <package name="org.mybatis.builder"/> </mappers>这些配置会告诉 MyBatis 去哪里找映射文件,剩下的细节就应该是每个 SQL 映射文件了,也就是接下来我们要讨论的。
理解我们之前讨论过的不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。
这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。 下面的示例就是一个确保 SqlSession 关闭的标准模式:
当数据库字段名跟实体类的字段名不一致时,有专门的处理办法。(除去小学生使用的as别名)
官网资料
设置名描述有效值默认值logImpl指定 MyBatis 所用日志的具体实现,未指定时将自动查找。SLF4J 、 LOG4J 、 LOG4J2 、 JDK_LOGGING 、 COMMONS_LOGGING 、 STDOUT_LOGGING 、 NO_LOGGING未设置mybatis.xml
Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件;
我们也可以控制每一条日志的输出格式;
通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程;
可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
mybatis.xml
<settings> <!--<setting name="logImpl" value="STDOUT_LOGGING"/>--> <setting name="logImpl" value="LOG4J"/> </settings>添加log4j.properties
#将等级为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=DEBUGmapper.xml
<select id="getUserByIdOnLimit" resultMap="resultUserMap" parameterType="map"> select * from smart_user limit #{pagestart},#{pagesize} </select> <select id="getUserByIdOnRowbounds" resultMap="resultUserMap"> select * from smart_user </select>test
@Test public void getUserByIdOnLimit(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); Map<String,Integer> map = new HashMap<>(); map.put("pagestart",0); map.put("pagesize",2); List<User> userList = mapper.getUserByIdOnLimit(map); userList.forEach(System.out::println); sqlSession.close(); } @Test public void getUserByIdOnLRowbounds(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); RowBounds rowBounds = new RowBounds(0, 2); List<User> userList = sqlSession.selectList("com.ssx.dao.UserMapper.getUserByIdOnRowbounds",null,rowBounds); userList.forEach(System.out::println); sqlSession.close(); }https://pagehelper.github.io/
mybatis.xml
<mappers> <mapper class="com.ssx.dao.UserMapper"/> </mappers>mapper.java
public interface UserMapper { @Select("select * from smart_user") List<User> getUserList(); @Select("select * from smart_user where id = #{id}") List<User> getUserById(@Param("id") int id2); @Insert("insert into smart_user(id,name,password) values(#{id},#{name},#{password})") void insertUser(User user); @Update("update smart_user set name = #{name} where id = #{id}") void updateUser(User user); @Delete("delete from smart_user where id = #{id}") void deleteUser(@Param("id") int id2); }test
public class MybatisTest05 { @Test public void getUserList(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> userList = mapper.getUserList(); userList.forEach(System.out::println); sqlSession.close(); } @Test public void getUserById(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> userList = mapper.getUserById(1); userList.forEach(System.out::println); sqlSession.close(); } @Test public void insertUser() { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = new User(); user.setId(3); user.setName("阿猪"); user.setPassword("112233"); mapper.insertUser(user); List<User> userList = mapper.getUserList(); userList.forEach(System.out::println); sqlSession.close(); } @Test public void updateUser(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = new User(); user.setId(3); user.setName("阿鸡"); mapper.updateUser(user); List<User> userList = mapper.getUserList(); userList.forEach(System.out::println); sqlSession.close(); } @Test public void deleteUser(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); mapper.deleteUser(3); List<User> userList = mapper.getUserList(); userList.forEach(System.out::println); sqlSession.close(); } }@Param
基本类型或String类型需要加上,引用类型不需要加如果只有一个基本类型可以忽略,但建议加上sql语句中使用的参数名是@Param()中的设定属性名(给类型取了别名)数据库表环境
-- ---------------------------- -- Table structure for smart_teacher -- ---------------------------- CREATE TABLE `smart_teacher` ( `tid` int(11) NOT NULL, `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`tid`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of smart_teacher -- ---------------------------- INSERT INTO `smart_teacher` VALUES (1, '王老师'); INSERT INTO `smart_teacher` VALUES (2, '李老师'); -- ---------------------------- -- Table structure for smart_student -- ---------------------------- CREATE TABLE `smart_student` ( `sid` int(11) NOT NULL COMMENT ' ', `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `tid` int(11) NULL DEFAULT NULL, PRIMARY KEY (`sid`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of smart_student -- ---------------------------- INSERT INTO `smart_student` VALUES (1, '小电', 1); INSERT INTO `smart_student` VALUES (2, '小绿', 2); INSERT INTO `smart_student` VALUES (3, '小牧', 1); INSERT INTO `smart_student` VALUES (4, '小八', 1); INSERT INTO `smart_student` VALUES (5, '小闹', 2);实体类
public class Student { private int sid; private String name; private Teacher teacher; } public class Teacher { private int tid; private String name; private List<Student> students; }studentMapper.xml
<!--绑定dao/mapper接口--> <mapper namespace="com.ssx.dao.StudentMapper"> <!--内联查询语句,将会执行第一个select后,逐条再执行子查询--> <select id="getStudentList" resultMap="studentResultMap"> select * from smart_student </select> <resultMap id="studentResultMap" type="Student"> <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/> </resultMap> <select id="getTeacher" resultType="Teacher" parameterType="int"> select * from smart_teacher where tid = #{tid} </select> <!--结果定制,一条sql返回结果,然后对结果进行解析--> <select id="getStudentList2" resultMap="studentResultMap2"> select s.*,t.name tname from smart_student s left join smart_teacher t on s.tid = t.tid </select> <resultMap id="studentResultMap2" type="Student"> <result property="sid" column="sid"/> <result property="name" column="name"/> <association property="teacher" javaType="Teacher"> <result column="tid" property="tid"/> <result column="tname" property="name"/> </association> </resultMap> </mapper>标签解释:
association 关联单个记录(多对一)collection 关联集合(一对多)javaType 对应类型ofType 用在集合中的泛型环境
-- ---------------------------- -- Table structure for smart_blog -- ---------------------------- DROP TABLE IF EXISTS `smart_blog`; CREATE TABLE `smart_blog` ( `ID` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `TITLE` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `AUTHOR` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `CREATE_TIME` datetime(0) NULL DEFAULT NULL, `views` int(30) NULL DEFAULT NULL, PRIMARY KEY (`ID`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; SET FOREIGN_KEY_CHECKS = 1;if天天用就不贴代码了。
where标签其实就是trim的一种:where中的第一个如果是and或or就出掉。
<trim prefix="WHERE" prefixOverrides="AND |OR "> ... </trim>set中,最后一个如果是“,”逗号,则去掉
<trim prefix="SET" suffixOverrides=","> ... </trim>官方解释
基本上就是这样。这个简单语句的效果如下:
映射语句文件中的所有 select 语句的结果将会被缓存。映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。缓存不会定时进行刷新(也就是说,没有刷新间隔)。缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。缓存清除策略
LRU – 最近最少使用:移除最长时间不被使用的对象。FIFO – 先进先出:按对象进入缓存的顺序来移除它们。SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。显示的开启一级缓存
<settings> <setting name="cacheEnabled" value="true"/> </settings>开启二级缓存
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
除了上述自定义缓存的方式,你也可以通过实现你自己的缓存,或为其他第三方缓存方案创建适配器,来完全覆盖缓存行为。
<cache type="com.domain.something.MyCustomCache"/>可以使用mybatis-ehcache包
这块目前已经被行业遗弃了,因为有更高级的缓存可以替代了:Redis、Memcache…