MyBatis
mybatis概述
mybatis是一个优秀的基于java的持久层框架,它内部封装了jdbc, 使开发者只需要关注sql语句本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。
mybatis通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射为java对象并返回。
mybatis环境搭建(基础)
-
使用maven创建项目
-
先建一个数据库的表用来测试(自己动手)
-
添加配置
<!--pom.xml--> <!--这里只是添加其中一个依赖的建议,具体可以看mybatis官网,下载来的依赖一般放在默认的.m2下的repository文件夹里面--> <!--以下版本号均为参考--> <dependencies> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.5</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <!--下面两个依赖是建议性的,一般工程都会有--> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.12</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> </dependency> </dependencies>
-
编写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="url" value="jdbc:mysql://localhost:3306/demo?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/> <property name="url" value="${url}"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> </configuration> <!--property属性填自己的就行了,放在resource包下,名字随便取。&是转义符&的意思-->
-
连接idea内置默认的数据库工具进行测试
打开idea右边的Database工具,打开的方框的左上角添加Data source选中mysql,进去后在右侧直接输入账号密码(记得开启mysql服务器)。测试连接,成功后在上面一点的Schemas选项卡里面勾上你的测试用数据库,确定后你的database工具就成了一个内置的低配数据库用户管理软件,可以看到相关数据库的信息。
-
这里测试连接的时候有可能要求下载driver文件,让他下载就行了,若遇到设置时区问题:
//这里我直接在mysql命令行里操作 //从cmd进入mysql mysql -uroot -p //查看时区 show variables like'%time_zone'; //若time_zone显示System说明没有配置 set global time_zone ='+8:00'; //设置了要重开mysql就可以看到时区设置成功。
-
mybatis连接了数据库字段和java实体类,所以创建java实体类。
public class UserBean implements Serializable{ //相应的数据 //相应的getter方法 //相应的setter方法 //相应的toString方法 }
-
注意点
-
这里的UserBean一定要有无参构造函数,要么有参无参一起写,要么都别写
-
字段名和实体类属性相同则是通过反射传值,字段名若和方法名对应则是通过setter方法传值,两个都写优先setter方法。
-
若字段名和属性名或者方法名都不匹配,则需要通过mapper的映射
<resultMap type = "com.qjl.demo.pojo.UserBean" id="myMapper"> <id property="某属性名" column="某字段名" /> <id property="某属性名" column="某字段名" /> <id property="某属性名" column="某字段名" /> </resultMap> <!--这段代码添加到所有的select标签的下面(同级),type写实体类名,id自己取一个,在需要用的select标签里面添加属性resultMap=“myMapper”--> <select resultMap="myMapper"/> <!--这样即可应用该映射-->
-
-
创建dao文件夹用于存放dao接口。
//IUserBeanDao public interface IUserBeanDao{ List<UserBean> getUserBean(); }
-
在resource下建一个mapperImpl包,再建一个UserMapper.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.qjl.demo.dao.IUserBeanDao"> <select id="getUserBean" resultType="com.qjl.demo.pojo.UserBean"> select * from resource </select> </mapper> <!--这个文件相当于一个实现了IUserBeanDao的类,namespace指定了接口,select标签里的id指定要重写的方法,resultType指定返回的类型,select里面是sql语句-->
-
要是上面这个文件没有放在Resource文件夹里面或者没有和mybatis-config.xml文件同级
则需要在mybatis-config.xml文件里面配置(上面有)
<!--configuration标签里面,放最后就行了--> <!--注意是斜杠不是点--> <mappers> <mapper resource="org/mybatis/example/BlogMapper.xml"/> </mappers> <!--可以*.xml通配-->
-
-
创建一个Utils类专门操作数据库(获得SqlSession对象)
//首先获得工厂类 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sql = new SqlSessionFactoryBuilder().build(inputStream); //获得SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession();
SqlSession sqlSession = sqlSessionFactory.openSession(ture);开启自动事务管理。
-
进行测试,获取数据库信息转化成实体类
SqlSession sqlSession = MybatisUtils.getSession(); IRes res = sqlSession.getMapper(IRes.class); List<Src> list = res.getRes(); for (Src src : list) { System.out.println(src); } sqlSession.close(); //1、2行还可以用下面的语句代替 //List<UserBean> list = sqlSession.selectList("com.qjl.demo.dao.UserBean");
-
mybatis进阶
-
数据库查询
-
增删改查
<!--parameterType="int"可以传入参数--> <select id="getId" resultType="com.qjl.demo.pojo.Src" parameterType="int"> select * from resource where src_id = #{id} </select> <!--#{id}若只有一个则会自动匹配getter方法名,随便写,parameterType只有一个基本类型,甚至可以不用写--> <insert id="insert" parameterType="com.qjl.demo.pojo.Src"> insert into resource (src_id,src_url) values (#{id},#{url}) </insert> <!--可以传入自定义的实体类,记得#{id}至少和实体类属性名或者getter方法名其中一个相对应就行--> <delete id="delete" parameterType="int"> delete from resource where src_id = #{id} </delete> <update id="update" parameterType="com.qjl.demo.pojo.Src"> update resource set src_url=#{url} where src_id=#{id}; </update>
SqlSession sqlSession = MybatisUtils.getSession(); IRes iRes = sqlSession.getMapper(IRes.class); iRes.insert(new Src(123215463,"123456")); //增删改查调用相应方法就可以了 sqlSession.commit(); sqlSession.close();
别忘了提交事务sqlSession.commit( );
-
使用map来传参
<insert id="insert" parameterType="map"> insert into resource (src_id,src_url) values (#{id},#{url}) </insert>
//dao void insert(Map<String,Object> map); //业务 SqlSession sqlSession = MybatisUtils.getSession(); IRes iRes = sqlSession.getMapper(IRes.class); Map<String,Object> map = HashMap<String,Object>(); map.input("id",123); map.input("url","information") iRes.insert(map);
作用是#{id}通过map的key来指定,并且参数灵活可变
多参数时要么传实体类要么传map,或者可以使用getList(@Param(“id”) int id,@Param(“pwd”) int pwd)
使用@Param后#{ }绑定就以它为准,同时mapper.xml文件中不用再设置parameterType属性了
-
-
mybatis-config.xml配置优化
-
使用
${ }
动态绑定 变量<!--在mybatis-config.xml同级创建jdbc.properties文件--> driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/demo?useSSL=false&useUnicode=true&characterEncoding=UTF-8 username=root password=root
<!--注意mybatis-config.xml文件有顺序,请在environment标签前面添加properties标签引入上面的变量文件--> <!--properties标签里面的property属性一样可用于设置变量,若冲突会使用外部配置文件--> <properties resource="jdbc.properties"> <property name="username" value="root"/> <property name="password" value="root"/> </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>
优先使用外部配置文件
-
设置别名替代全限定符
-
配置的方式
<!--mybatis-config.xml文件中添加别名--> <!--第一种,是自己设定别名--> <typeAliases> <typeAlias alias="demo1" type="com.qjl.demo.demo1"/> <typeAlias alias="demo2" type="com.qjl.demo.demo2"/> </typeAliases> <!--第二种,更自动化,扫描实体类的包,在没有注解的情况下,别名就是实体类名称(首字母大小写都可以)--> <typeAliases> <package name="com.qjl.demo.pojo"/> </typeAliases>
在mybatis-config.xml文件里面配置的属性都是有顺序的,这个typeAliases标签是在上面properties标签的后面,排在第三个(第二个是settings标签),没事idea里面有提示的,错了会告诉你应该放第几个。
-
注解的方式
@Alias("user") public class UserBean{ ... } //注解建议和上面扫描包的方式一起使用,有特殊需要加上注解就可以自定义别名(注解优先)。
基本类型比如像int默认映射为Integer,要使用int请在前面加 _ (例如 _int)
-
-
settings设置
<!--常用--> <settings> <setting name="logImpl" value="STDOUT_LOGGING"/><!--开启默认log记录方式--> <setting name="mapUnderscoreToCamelCase" value="true"/><!--字段属性之间驼峰命名映射--> </settings>
-
mapper映射
<!--上面讲过的使用直接映射的方法--> <mappers> <mapper resource="com.qjl.demo.XXXMapper.xml"/> </mappers> <!--指定映射器接口完成映射--> <mappers> <mapper class="com.qjl.demo.UserDao"/> </mappers> <!--包的扫描引入--> <mappers> <package name="com.qjl.demo"/> </mappers>
注意第二和第三种方法:接口和他的mapper配置文件必须同名并且在同一个包下面(UserDao改成UserMapper,UserMapper.xml放到dao文件夹下)
-
生命周期和作用域
生命周期讲的就是SqlSessionFactoryBuilder只使用一次,就是用来创建SqlSessionFactory,而SqlSessionFactory随程序运行诞生,随程序结束消亡,全程只有一个(设置为静态变量)。SqlSessionFactory获得的SqlSession不是线程安全的,每个线程需要单独一个SqlSessison为其工作,用完记得关闭。
-
resultMap结果集映射
<!--上面讲过了,这里介绍起别名的方法--> <select ...> select name,password as pwd from table </select> <!--这里的password是字段名,pwd是实体类属性名,这样就可以映射在一起-->
-
-
使用日志
-
内置日志
<settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
-
使用log4j
<settings> <setting name="logImpl" value="LOG4J"/> </settings> <!--依赖--> <dependencies> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies> <!--在resources文件夹下配置log4j.properties文件-->
#这个是log4j.properties文件,只是推荐,具体调配根据需求,可以参考文档。 ### set log levels ### log4j.rootLogger=DEBUG,stdout,info,warn,error ### console ### log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=[${projectName}] [%p] [%-d{yyyy-MM-dd HH:mm:ss}] %C.%M(%L) | %n%m%n ### info ### log4j.logger.info=info log4j.appender.info=org.apache.log4j.DailyRollingFileAppender log4j.appender.info.File=${webapp.root}/logs/${projectName}_info.log log4j.appender.info.DatePattern='_'yyyy-MM-dd'.log' log4j.appender.info.Append=true log4j.appender.info.bufferSize=1024 log4j.appender.info.Threshold=INFO log4j.appender.info.layout=org.apache.log4j.PatternLayout log4j.appender.info.layout.ConversionPattern=[${projectName}] [%p] [%-d{yyyy-MM-dd HH:mm:ss}] %C.%M(%L) | %n%m%n #warn log log4j.logger.warn=warn log4j.appender.warn=org.apache.log4j.DailyRollingFileAppender log4j.appender.warn.DatePattern='_'yyyy-MM-dd'.log' log4j.appender.warn.File=./logs/${projectName}_warn.log log4j.appender.warn.Append=true log4j.appender.warn.Threshold=WARN log4j.appender.warn.layout=org.apache.log4j.PatternLayout log4j.appender.warn.layout.ConversionPattern=[${projectName}] [%p] [%d{yyyy-MM-dd HH:mm:ss a}] [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n ### error ### log4j.logger.error=error log4j.appender.error=org.apache.log4j.DailyRollingFileAppender log4j.appender.error.File=${webapp.root}/logs/${projectName}_error.log log4j.appender.error.DatePattern='_'yyyy-MM-dd'.log' log4j.appender.error.Append=true log4j.appender.error.Threshold=ERROR log4j.appender.error.layout=org.apache.log4j.PatternLayout log4j.appender.error.layout.ConversionPattern=[${projectName}] [%p] [%-d{yyyy-MM-dd HH:mm:ss}] %C.%M(%L) | %m%n [Thread: %t][ Class:%c Method: %l ] %n ###显示SQL语句部分 log4j.logger.org.mybatis=DEBUG #log4j.logger.com.mybatis.common.jdbc.SimpleDataSource=DEBUG #log4j.logger.com.mybatis.common.jdbc.ScriptRunner=DEBUG #log4j.logger.com.mybatis.sqlmap.engine.impl.SqlMapClientDelegate=DEBUG log4j.logger.java.sql.Connection=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG
//上面是通用的方法,接下来我们使用log代替system.out static Logger logger = Logger.getLogger(test.class); //当前的类对象 logger.info("this is info"); logger.error("this is error"); //输出 [com.qjl.demo.User]-this is info
-
-
分页
-
limit实现:SQL语句里面的limit,学过sql就知道
-
RowRounds实现(了解一下就行)
//直接调用sqlSession的方法来获取实体类。 //具体到dao层接口的方法名。 RowBounds rowBounds = new RowBounds(1,2); //org.apache.ibatis.session包下面,1是offset,2是limit。 List<User> list = sqlSession.selectList("com.qjl.dao.mapper类.方法名"null,rowBounds); //这么处理以后就不用在dao层接口方法里传参数了。
-
-
使用注解开发
-
mybatis-config.xml配置以及接口注解
<mappers> <mapper class="com.qjl.dao.UserDao"/> </mappers>
//使用注解代替mapper.xml,但是只适合简单的语句 public interface UserDao{ @Select("select * from resource") List<User> getUserList(); }
-
-
#{ }和${ }的区别
#{ }会将传入的值转变成String类型,KaTeX parse error: Expected 'EOF', got '#' at position 11: { }则不会,意味着#̲{ }若是限定int只能传in…{ }没法限定int,它只能传进来String型,所以使用#{ }可以防止sql注入
注意:mybatis排序时使用order by动态参数时使用的是${ }而不是#{ }
-
lombok偷懒神器(别用)
首先添加依赖,自己去maven官网找repository依赖
@Data @AllArgsConstructor @NoArgsConstructor public class UserBean{ String name; int age; boolean sex; } //@Data注解会帮你写了getter、setter、toString、无参构造,@Getter写类上对所有属性生效,也可以单独对某个属性使用。 //其他自己研究吧,就是帮你偷懒写实体类的工具。
建议别用,什么东西嘛,注解真的是对新手超级超级超级超级不友好的东西。而且侵入性很强,拿过来的代码还得到处装包。
-
嵌套查询
-
子查询
<!--场景:一个用户拥有多辆车,用户id对应汽车的userId,下面实现子查询实现转对象--> <!-- public class Car{ int id; int color; User userId; } public class User{ int id; String name; String desc; } --> <resultMap id="carMap" type="com.qjl.pojo.Car"> <association property="userId" column="uId" javaType="com.qjl.pojo.User" select="getUser"/> </resultMap> <select id="getCar" resultMap="carMap" resultType="Car"> ... </select> <select id="getUser" resultType="User"> ... #{uId} </select>
-
关联查询
<!--关联查询其实两个表的信息都会同时查出来,只要匹配就行了--> <select id="getCar" resultMap="carMap"> select car.id, car.color,user.id from car,user where car.id = user.id </select> <!--该sql语句其实将两个表所有信息都查询出来了,只不过我们选择呈现了car.id, car.color,user.id,所以其实不用子查询,因为所有信息已经有了,只要做一个结果集映射就行了--> <resultMap id="carMap" type="com.qjl.pojo.User"> <association property="userId" javaType="com.qjl.pojo.User"> <result property="id" column="id"/> <!--这里可以看成是等于条件,property是User的id,column是前面user.id对应上了--> </association> </resultMap> <!--如果在select里面去了别名,column要用别名来映射-->
-
集合查询
<!--和关联查询相同,association改成collection就行--> <collection property="集合属性" javaType="ArrayList" ofType="泛型类"> <result property="泛型类属性" column="字段名"/> </collection> <!--子查询就自己研究了,个人认为这种比较好-->
tips:
UUID.randomUUID().toString().replaceAll("-","");
可以生成一段随机ID
-
-
mybatis缓存
-
一级缓存
一级缓存默认开启,比如两次查询获得的对象是同一个,而insert、update、delete方法会刷新缓存,并且不定时刷新,不受我们控制。
手动清理缓存:
sqlSession.clearCache( );
-
二级缓存
二级缓存也叫全局缓存,二级缓存范围更加大,不止在同一个SQLSession,而是在一个mapper.xml文件里面生效
开启的方式:<mapper>标签里面第一行添加上<cache/>就行了,具体设置如下
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/> <!--60秒刷新,最多可以有512个引用,只读属性,建议在setting里面开启设置如下所示(不写也行)--> <setting name="cacheEnabled" value="true">
afterAll,二级缓存就是两个sqlSession之间跨越调用同一个缓存,前提是前一个缓存已经关闭
-