-
花了一个星期的时间,算是初步入门了Mybatis这个Dao层框架
-
简而言之,Mybatis简化了传统JDBC的操作。MyBatis 是一个半自动化的ORM框架 (Object Relationship Mapping) -->对象关系映射
-
Mybatis环境搭建:JDK1.8 MySQL 5.7.19 maven-3.6.1 IDEA2019.2
第一个Mybatis程序
-
创建数据库(自己用SQLyog创建)
-
导入Mybatis相关包(还有测试包junit以及后面偷懒用的lombok)
<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>
-
编写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核心配置文件--> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <!--驱动--> <property name="driver" value="com.mysql.jdbc.Driver"/> <!--useSSL配置为false--> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=utf8"/> <property name="username" value="root"/> <property name="password" value="12345"/> </dataSource> </environment> </environments> <!--每一个Mapper.xml都需要在核心配置文件中注册--> <!--这里resource中的路径只能用斜杠分隔--> <mappers> <mapper resource="com/shihongli/dao/UserMapper.xml"/> </mappers> </configuration>
-
编写MybatisUtils工具类
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; //工具类 //获取sqlSessionFactory对象 //sqlSessionFactory是用于构建sqlSession的 public class MybatisUtils { private static SqlSessionFactory sqlSessionFactory; static { try { //官方文档 使用Mybatis第一步 //获取sqlSessionFactory对象 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } //sqlSession包括了面向数据库执行sql命令的所有方法 //获取SqlSession连接 public static SqlSession getSession(){ return sqlSessionFactory.openSession(); } }
-
编写实体类(对照数据库 有几个表就创建几个实体类 放在pojo包下)
-
之后编写与实体类对应的Mapper接口类(之前都叫dao接口)
-
对应Mapper接口,编写对应的Mapper.xml文件
-
注意namespace,它是绑定Mapper接口的标志
-
namespace的命名必须跟某个接口同名
-
接口中的方法与映射文件中sql语句id应该一一对应
-
namespace和子元素的id联合保证唯一 , 区别不同的mapper
-
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.shihongli.dao.UserMapper">
-
-
编写测试类
@Test public void test() { //获取sqlSession对象 SqlSession sqlSession = MybatisUtils.getSession(); //执行sql //获得接口的对象,用于调用方法 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<User> userList = userMapper.getUserList(); for (User user : userList) { System.out.println(user); } //关闭sqlSession sqlSession.close(); }
最好在创建module时把下面代码放到pom.xml中 防止maven读取资源出问题
<build> <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> </build>
-
-
注意:Mapper.xml配置文件中namespace中的名称为对应Mapper接口或者Dao接口的完整包名,必须一致
-
CRUD步骤:(使用xml开发)
- 在Mapper接口中定义需求接口
- 在Mapper.xml中添加CRUD标签
- 在测试类中进行测试
-
常见标签
-
传入的参数一律都用#{}括起来
-
select标签(查找)
<select id="getUserById" parameterType="Integer" resultType="com.shihongli.pojo.User"> select * from user where id = #{id} </select>
- resultTypeSQL语句返回值类型。【完整的类名或者别名】
- resultMap:如果实体类属性名与数据库字段名不一致时或出现一对多,多对一,多对多的情况时,使用。
- parameterType 传入SQL语句的参数类型 。【万能的Map】
- id:命名空间中唯一的标识符
-
模糊查询:直接在Java代码中加通配符
string wildcardname = “%smi%”; list<name> names = mapper.selectlike(wildcardname); <select id=”selectlike”> select * from foo where bar like #{value} </select>
-
接口传入参数为map时
User selectUserByNP2(Map<String,Object> map); <select id="selectUserByNP2" parameterType="map" resultType="com.kuang.pojo.User">sql略 Map<String, Object> map = new HashMap<String, Object>(); map.put("username","小明"); map.put("pwd","123456"); User user = mapper.selectUserByNP2(map);
如果参数过多,可以考虑直接使用Map实现,如果参数比较少,直接传递参数
-
insert标签(增加)
-
insert没有resultType
-
增删改必须要提交事务,否则不提交到数据库
sqlSession.commit();
或者 在Mybatis工具类openSession加入参数true,自动提交事务
-
-
update标签(更新)
-
delete标签(删除)
-
所有的增删改操作都需要提交事务!
-
接口所有的普通参数,尽量都写上@Param参数,尤其是多个参数时,必须写上!
-
有时候根据业务的需求,可以考虑使用map传递参数!
-
为了规范操作,在SQL的配置文件中,尽量将Parameter参数和resultType都写上!
-
-
配置解析
-
mybatis-config.xml 系统核心配置文件(设置mybatis行为设置和属性信息)
-
配置内容
configuration(配置) properties(属性) settings(设置) typeAliases(类型别名) typeHandlers(类型处理器) objectFactory(对象工厂) plugins(插件) environments(环境配置) environment(环境变量) transactionManager(事务管理器) dataSource(数据源) databaseIdProvider(数据库厂商标识) mappers(映射器) <!-- 注意元素节点的顺序!顺序不对会报错 -->
常用 configuration properties settings typeAliases environments mappers
-
environments元素
-
<environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <!--驱动--> <property name="driver" value="${driver}"/> <!--useSSL配置为false,amp的作用是转义--> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments>
-
-
Mappers元素
- mapper作为映射器,用于定义映射SQL语句文件
- 映射就是 告诉Mybatis去哪找这些语句
引入资源的方式:(三种都需要掌握)
<!--每一个Mapper.xml都需要在核心配置文件中注册--> <!--这里resource中的路径只能用斜杠分隔-->
<!-- 使用相对于类路径的资源引用 resource只能用/分隔 这种引用方式不要求在同一个包下 只要找得到即可 --> <mappers> <mapper resource="org/mybatis/builder/PostMapper.xml"/> </mappers>
<!-- 使用映射器接口实现类的完全限定类名 需要配置文件名称和接口名称一致,并且位于同一目录下 --> <mappers> <mapper class="org.mybatis.builder.AuthorMapper"/> </mappers>
<!-- 将包内的映射器接口实现全部注册为映射器 但是需要配置文件名称和接口名称一致,并且位于同一目录下 --> <mappers> <package name="org.mybatis.builder"/> </mappers>
-
Properties元素(在JDBC中也使用过)
-
1 在资源目录下新建db.properties(这是我本地的)
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=utf8 username=root password=12345
-
文件导入properties配置文件
<configuration> <!--导入properties文件--> <properties resource="db.properties"/> ....... </configuration>
-
-
typeAliases别名优化:为 Java 类型设置一个短的名字,用于减少包名+类名的冗余
-
为单独的实体类设置别名
-
<typeAliases> <!--给实体类起别名--> <typeAlias type="com.shihongli.pojo.User" alias="User"/> </typeAliases>
-
为整个包设置别名,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。
-
<typeAliases> <!--给实体类起别名--> <!-- <typeAlias type="com.shihongli.pojo.User" alias="User"/>--> <package name="com.shihongli.pojo"/> </typeAliases>
-
通过注解设置别名
-
@Alias("user") public class User { ... }
-
-
settings设置(常用cacheEnabled lazyLoadingEnabled mapUnderscoreToCamelCase logImpl)
-
<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>
-
-
各个对象作用域
- SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。
- SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。
- SqlSession 的最佳的作用域是请求或方法作用域。
日志工厂
-
在核心配置文件的settings中配置日志输出
-
STDOUT_LOGGING(Mybatis自带标准日志输出)
-
<settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
-
log4j(log for Java)
-
导入log4j的包
-
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
-
编写配置文件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=DEBUG
-
setting实现日志
-
<settings> <setting name="logImpl" value="LOG4J"/> </settings>
-
测试:Logger.getLogger(MyTest.class);返回一个测试Logger类
-
-
-
一对多&多对一(需要多练习)这里只写我熟悉的一种写法
-
多对一
-
数据库设计
-
搭建测试环境(核心配置文件 工具类 pojo dao(Mapper Mapper.xml))
-
lombok插件(导包)
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.10</version> </dependency>
-
使用resultMap并且多对一表示关联,使用association标签
这种方法 不用分开sql语句 映射关系相对于另一种麻烦了一点
<select id="getStudentInfo1" resultMap="StudentTeacher1"> 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="StudentTeacher1" type="Student"> <!-- 对结果进行处理--> <!-- 这里相当于是对student中的Student类的对象进行 分别的属性处理--> <result property="id" column="sid"/> <result property="name" column="sname"/> <!-- 对其中的复杂类型teacher单独处理--> <!-- 这里实际上是对teacher这个对象对应的Teacher类 进行了一次嵌套,里面是Teacher类的属性--> <association property="teacher" javaType="Teacher"> <!-- 再次进行嵌套--> <result property="name" column="tname"/> <result property="id" column="tid"/> </association> </resultMap>
-
注意记得在核心配置文件中注入mapper/或者直接注入包(前提是名字一致且在同一包下)
-
一对多
-
之前配置同上
-
Mapper.xml语句
<select id="getTeacherById" 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=#{id} </select> <resultMap id="TeacherStudent" type="Teacher"> <!-- 在数据库的表中别名也算column的名字--> <result property="id" column="tid"/> <result property="name" column="tname"/> <!-- 复杂的对象需要单独处理 对象(多对一)association 集合(一对多)--> <!-- 这里不需要写javaType的原因是 这里已经对应取的是这个集合中的各个元素的属性了--> <collection property="students" ofType="Student"> <result property="id" column="sid"/> <result property="name" column="sname"/> <result property="tid" column="tid"/> </collection> </resultMap>
-
-
总结:
1、关联-association
2、集合-collection
3、所以association是用于一对一和多对一,而collection是用于一对多的关系
4、JavaType和ofType都是用来指定对象类型的
-
JavaType是用来指定pojo中属性的类型
-
ofType指定的是映射到list集合属性中pojo的类型。(可以理解为泛型)
JavaType是用来指定pojo中属性的类型 ofType指定的是映射到list集合属性中pojo的类型
-
-
动态SQL:指的是根据不同的查询条件 , 生成不同的Sql语句
-
会用几个常用的标签
-
if标签(最经常使用的 相当于JDBC中sql语句拼接)
<if test="title != null"> title = #{title} </if> <if test="author != null"> and author = #{author} </if>
-
where标签
<where> <if test="title != null"> title = #{title} </if> <if test="author != null"> and author = #{author} </if> </where>
-
“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个‘where’。此外,如果标签返回的内容是以AND 或OR 开头的,则它会剔除掉。(反正写上就完事了)
-
Set标签(用于Update标签)
<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标签与where标签用法基本一致,用于去除多余的逗号
-
choose(when otherwise)标签
-
类似java中switch-case语句
<select id="queryBlogChoose" parameterType="map" resultType="Blog"> select * from mybatis.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片段不要包括where
-
1.提高复用性 提取sql片段
<sql id=“xxx” xxx </sql>
-
2.引用sql片段
-
<include refid="if-title-author"></include>
-
-
foreach
-
传入一个map
-
<!-- select * from mybatis.blog where 1=1 and id in (1,2,3);--> <!-- foreach标签中的 collection可以根据map的key的值获取集合--> collection:指定输入对象中的集合属性key值 item:每次遍历生成的对象 open:开始遍历时的拼接字符串 close:结束时拼接的字符串 separator:遍历对象之间需要拼接的字符串 <select id="queryBlogForeach" parameterType="map" resultType="Blog"> select * from mybatis.blog <where> <foreach collection="ids" item="id" open="and (" close=")" separator="or"> id = #{id} </foreach> </where> </select>
-
缓存
-
MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
- 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
- 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
- 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
-
一级缓存也叫本地缓存:
- 与数据库同一次会话期间查询到的数据会放在本地缓存中。
- 以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库
- 一级缓存自动开启
-
二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
工作机制
-
-
-
一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
-
如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
-
新的会话查询信息,就可以从二级缓存中获取内容;
-
不同的mapper查出的数据会放在自己对应的缓存(map)中;
-
-
使用步骤
在核心配置文件中设置开启
-
<setting name="cacheEnabled" value="true"/>
在mapper.xml中配置使用二级缓存
<cache/> 或者 <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/> 这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
-
-
注解开发(Mybatis中注解只能进行比较简单的sql语句,复杂的语句还是用xml开发)
-
映射并不能用注解来构建
-
常见注解:
-
- @select ()
- @update ()
- @Insert ()
- @delete ()
-
-
使用注解之后只需改变两个地方:mapper和测试类,不需要在mapper.xml中写SQL语句了
-
1.添加注解
-
//查询全部用户 @Select("select id,name,pwd password from user") public List<User> getAllUser();
-
-
-
2.核心配置文件注入(绑定接口)
<!--使用class绑定接口--> <mappers> <mapper class="com.kuang.mapper.UserMapper"/> </mappers>
-
-
-
面向接口编程:根本原因 : 解耦 , 可拓展 , 提高复用 , 分层开发中 , 上层不用管具体的实现 , 大家都遵守共同的标准 , 使得开发变得容易 , 规范性更好
-
其他注解实例
-
//添加一个用户 @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);
-
//根据id删除用 @Delete("delete from user where id = #{id}") int deleteUser(@Param("id")int id);
@Param注解用于给方法参数起一个名字。以下是总结的使用原则:
- 在方法只接受一个参数的情况下,可以不使用@Param。
- 在方法接受多个参数的情况下,建议一定要使用@Param注解给参数命名。
- 如果参数是 JavaBean(参数为引用类不用加,如果是基本数据类型和string一定要加上) , 则不能使用@Param。
- 不使用@Param注解时,参数只能有一个,并且是Javabean。
#{} 的作用主要是替换预编译语句(PrepareStatement)中的占位符? 【推荐使用】
${} 的作用是直接进行字符串替换
-
-
使用注解和配置文件协同开发,才能最大利用Mybatis!
后续:
用了一个星期的碎片时间,把mybatis入门了,之后一段时间先看spring再看springmvc,最后ssm整合,希望能在7.10号之前完成!-2020.06.05晚22.22