MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
需要使用到的jar包:
注:如果使用maven创建项目的化,可以取maven仓库找到对应的mybatis包,再导入xml就好。
数据持久化:
持久层:
例如:DAO层、Service层、Controller层
在此之前需要新建一个数据库数据;
CREATE DATABASE `mybatis`; USE `mybatis`; CREATE TABLE `user`( `id` INT(20) NOT NULL PRIMARY KEY, `name` VARCHAR(30) DEFAULT NULL, `pwd` VARCHAR(30) DEFAULT NULL )ENGINE=INNODB DEFAULT CHARSET=utf8; --自行插入数据到数据表中;
接下来创建一个项目,导入所需要的所有Jar包,然后就可以编写代码了;
创建一个database.properties,用来定义JDBC数据连接数据库:
driver=com.mysql.jdbc.Driver url=jdbc:mysql:/本机地址:3306/数据库?useUnicode=true&characterEncoding=utf-8&useSSL=true username=数据库账号 password=密码
最重要的一点,编写mybaits-config.xml核心配置:
(可以查看Mybatis开发文档:https://mybatis.org/mybatis-3/zh/index.html)
<?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> <!-- 获取配置文件database.properties --> <properties resource="database.properties"></properties> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"></transactionManager> <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> <!-- 每一个Mapper.xml都需要在Mybatis核心配置中注册! --> <mappers> <mapper resource="com/vxzx/dao/UserMapper.xml"></mapper> </mappers> </configuration>
可以再编写一个获取Mybatis的Sqlsession的工具类:
package com.vxzx.utils; 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 { //获取SqlSessionFactory对象: public static SqlSession getsqlSession(){ SqlSession session = null; String resource = "mybaits-config.xml"; try { InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream); session = sessionFactory.openSession(); } catch (IOException e) { e.printStackTrace(); } return session; } }
package com.vxzx.pojo; public class User { private Integer id; private String name; private String pwd; public User() { } public User(Integer id, String name, String pwd) { this.id = id; this.name = name; this.pwd = pwd; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", pwd='" + pwd + '\'' + '}'; } }
package com.vxzx.dao; import com.vxzx.pojo.User; import java.util.List; import java.util.Map; public interface UserMapper { //获取所有用户信息: List<User> getUserList(); //添加一个用户: int addUser(User user); //使用map修改用户: int updateUser2(Map<String,Object> map); //模糊查询: List<User> getUserByLike(String value); }
<?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命名空间=绑定的一个对应的Mapper接口 --> <mapper namespace="com.vxzx.dao.UserMapper"> <!-- select:查询语句; id:对应引用的方法名;resultType:返回结果集 --> <select id="getUserList" resultType="com.vxzx.pojo.User"> select * from user </select> -- 只能修改pwd的数据,不能修改其他的数据: <insert id="addUser" parameterType="com.vxzx.pojo.User"> insert into user(id,name,pwd) values(#{id},#{name},#{pwd}) </insert> <update id="updateUser2" parameterType="map"> update user set name=#{username},pwd=#{password} where id=#{id} </update> <select id="getUserByLike" resultType="com.vxzx.pojo.User"> select * from user where name like #{value} </select> </mapper>
//查询所有用户信息: public void getUserList(){ //1、获取SqlSession对象: SqlSession session = MybatisUtils.getsqlSession(); //2、执行SQL: ----方式一:推荐使用 UserMapper mapper = session.getMapper(UserMapper.class); List<User> userList = mapper.getUserList(); //2、执行SQL: ----方式二: // List<User> userList = session.selectList("com.vxzx.dao.UserMapper.getUserList"); for (User user : userList) { System.out.println(user); } session.close(); } //添加用户: public void addUser(){ SqlSession session = MybatisUtils.getsqlSession(); UserMapper mapper = session.getMapper(UserMapper.class); int user1 = mapper.addUser(new User(5, "vxzx", "123")); if (user1 > 0){ System.out.println("添加成功!"); } //提交事务:必须执行,不然添加不进去 session.commit(); session.close(); } //使用map修改用户: public void updateUser(){ SqlSession session = MybatisUtils.getsqlSession(); UserMapper mapper = session.getMapper(UserMapper.class); Map<String,Object> map = new HashMap<String, Object>(); //格式:(sql语句中的#{}的值,新的值);可以插入一个或多个,不用全部插入 map.put("username","vx"); map.put("password","111111"); map.put("id",4); mapper.updateUser2(map); session.commit(); session.close(); } //模糊查询: public void getUserByLike(){ SqlSession session = MybatisUtils.getsqlSession(); UserMapper mapper = session.getMapper(UserMapper.class); List<User> userList = mapper.getUserByLike("%x%"); for (User user : userList) { System.out.println(user); } session.close(); }
如果出现配置文件无法导出或者生效的问题,可以在项目对应的pom.xml插入以下配置代码:
<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>
接下来,我们可以对上面代码进行详细的解析:
namespace中的包名要和 Dao/mapper 接口的包名一致!
选择,查询语句;
//根据ID查询用户 User getUserById(int id);
<select id="getUserById" parameterType="int" resultType="com.kuang.pojo.User"> select * from mybatis.user where id = #{id} </select>
public void getUserById() { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.getUserById(1); System.out.println(user); sqlSession.close(); }
<!--对象中的属性,可以直接取出来--> <insert id="addUser" parameterType="com.kuang.pojo.User"> insert into mybatis.user (id, name, pwd) values (#{id},#{name},#{pwd}); </insert>
<update id="updateUser" parameterType="com.kuang.pojo.User"> update mybatis.user set name=#{name},pwd=#{pwd} where id = #{id} ; </update>
<delete id="deleteUser" parameterType="int"> delete from mybatis.user where id = #{id}; </delete>
注意:增删改需要使用commit()
提交事务;
假设,我们的实体类,或者数据库中的表,字段或者参数过多,我们应当考虑使用Map!
//万能的Map int updateUser(Map<String,Object> map);
<update id="updateUser2" parameterType="map"> update user set name=#{username},pwd=#{password} where id=#{id} </update>
public void updateUser(){ SqlSession session = MybatisUtils.getsqlSession(); UserMapper mapper = session.getMapper(UserMapper.class); Map<String,Object> map = new HashMap<String, Object>(); //格式:(sql语句中的#{}的值,新的值);可以插入一个或多个,不用全部插入 map.put("username","vx"); map.put("password","111111"); map.put("id",4); mapper.updateUser2(map); session.commit(); session.close(); }
Map传递参数,直接在sql中取出key即可! 【parameterType="map"】
对象传递参数,直接在sql中取对象的属性即可!【parameterType="Object"】
只有一个基本类型参数的情况下,可以直接在sql中取到!
多个参数用Map,或者注解!
对应的mapper的sql语句:
<select id="getUserByLike" resultType="com.vxzx.pojo.User"> select * from user where name like #{value} </select>
测试类中获取数据:
List<User> userList = mapper.getUserLike("%李%"); //加上通配符 % %
一:对应的mapper的sql语句:
<select id="getUserByLike" resultType="com.vxzx.pojo.User"> select * from user where name like "%"#{value}"%" </select>
测试类中获取数据:
List<User> userList = mapper.getUserLike("李"); //在mapper中已加通配符,这里不用加了
核心配置文件:
注意:各种配置必须严格按照其固定的顺序来使用。
使用properties来引用外部数据源;例如:引入JBDC数据库连接;【database.properties】
这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。
database.properties:
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=true username=root password=123456
在核心配置中直接引入:
<properties resource="database.properties"/>
在核心配置中添加一些配置:
<properties resource="db.properties"> <property name="username" value="root"/> <property name="pwd" value="11111"/> </properties>
注意:如果两个文件有同一个字段,那么会优先使用外部的配置信息。
Mybatis可以配置多种环境来使用;但是每个SqlSessionFactory实例只能使用一种配置。
其中,Mybatis默认的transactionManager一般为JDBC,dataSource为POOLED连接池。
<environments default="environment"> <environment id="environment"> <transactionManager type="JDBC"></transactionManager> <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 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。
例如:配置日志文件实现;
<settings> <setting name="logImpl" value="log4j"/> </settings>
使用
<typeAliases> <package name="com.vxzx.pojo"/> </typeAliases>
使用
其中type为需要设置的类,alias为设置的别名。
<typeAlias type="com.vxzx.pojo.User" alias="user"></typeAlias>
在
@Alias("user") pubic class user(){}
我们有三种方法可以使用:
一:常用(无其他注意点)
<mappers> <mapper resource="com/kuang/dao/UserMapper.xml"/> </mappers>
二:使用class文件绑定注册
<mappers> <mapper class="com.kuang.dao.UserMapper"/> </mappers>
注意:
三:使用扫描包的方式注册绑定
<mappers> <package name="com.kuang.dao"/> </mappers>
注意:
它的过程可以简单的理解为:创建sql工厂,打开sqlSession,使用Mapper来操作CRUD;
SqlSessionFactoryBuilder
SqlSessionFactory
SqlSession
实体类的属性名:
private Integer id; private String name; private String password;
数据库的字段名:
id int(3) ; name varchar(10) ; pwd varchar(20) ;
其中,password与pwd就有区别。这样查询数据会查询出null。
解决方法一:直接在查询语句中设置别名as
<select id="getUserList" resultMap="resultmap"> select id,name,pwd as password from user </select>
解决方法二:使用resultMap结果映射
<resultMap id="resultmap" type="user"> <!-- property:属性名; column:数据库字段名; --> <id property="id" column="id"/> <result property="name" column="name"/> <result property="password" column="pwd"/> </resultMap> <select id="getUserList" resultMap="resultmap"> select * from user </select>
对于简单的语句根本不需要配置显式的结果映射,而对于复杂一点的语句只需要描述它们的关系就行了。
你已经对它相当了解了,就不需要显式地用到他们。
使用limit分页跟在sql里面的使用步骤一样,不同的是,需要在xml中填加该语句。
<!-- 将查询出来的结果从1开始,每页展示三个 --> <select id="getUserlistLmit" resultType="user"> select * from mybatis.user limit 0,3; </select>
Mybatis提供了一个简单的逻辑分页使用类RowBounds。
首先,第一步先实例化一个RowBounds对象,里面输入的值为数据(起始值,显示页面值)
public RowBounds(int offset, int limit)
然后使用SQLsession中的selectList方法:
List<User> userList = sqlSession.selectList("com.vxzx.dao.UserMapper.getUserList", null, rs); //传入的值:(需要查询的类的接口--及里面的方法,Object,RowBounds)
public void getUserListRow(){ //实现从第一开始,每页显示2条数据的显示; RowBounds rs = new RowBounds(0,2); SqlSession sqlSession = MybatisUtils.getSqlSession(); //注意,要使用selectList的方法; List<User> userList = sqlSession.selectList("com.vxzx.dao.UserMapper.getUserList", null, rs); for (User user : userList) { System.out.println(user); } sqlSession.close(); }
了解一些分页插件: PageHelper
数据库操作出现问题,就可以使用日志文件来进行排错。
Mybatis中日志的具体实现,可以在settings(设置)中设置。
在Mybatis核心配置文件中配置:
<settings> <setting name="logImpl" value="log4j"/> </settings>
还可以自己设置日志数据输出格式和路径:(log4j.properties)
Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件;过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程;通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
具体使用步骤:
(1)导入log4j的jar包:
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
(2)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/vx.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
(3)配置log4j的实现:
<settings> <setting name="logImpl" value=""/> </settings>
实际使用操作:
import org.apache.log4j.Logger; static Logger logger = Logger.getLogger(UserMapperTest.class);
logger.info("输出的logger--->info"); logger.debug("输出的logger--->debug"); logger.error("输出的logger--->error");
sql数据库的CURD我们都可以使用注解来进行简单的操作;
@Select("select * from user where id=#{uid}") User getId(@Param("uid")int id);
@Insert("insert into user(id,name,pwd) values(#{id},#{name},#{password})") int insertUser(User user);
@Update("update user set name=#{name} where id=#{id}") int updateUser(User user);
@Delete("delete from user where id=#{uid}") int deleteUser(@Param("uid")int id);
值得注意的一点是,需要在核心配置文件中绑定接口!
<!-- 使用注解方法=====绑定接口: --> <mappers> <mapper class="com.vxzx.dao.UserMapper"></mapper> </mappers>
Lombok能通过注解的方式,在编译时自动为属性生成构造器、getter/setter、equals、hashcode、toString方法。出现的神奇就是在源码中没有getter和setter方法,但是在编译生成的字节码文件中有getter和setter方法。这样就省去了手动重建这些代码的麻烦,使代码看起来更简洁些。
使用步骤:
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </dependency>
@Data
//导入lombok的jar包,使用Lombok注解,完成getter、setter @Data //etter、setter @AllArgsConstructor //有参构造 @NoArgsConstructor //无参构造 @EqualsAndHashCode //hashcode比较 @ToString public class User { private Integer id; private String name; private String password; }
例如:多个学生对应一个老师的例子。
多对一查询:使用<resultMap>中的association; 具体实现一:根据结果集映射查询 先使用sql连表查询语句===>映射为resultMap; 再使用resultMap,配置查询的数据的别名的问题,其中需要使用association, 再在association里面配置数据库列信息; 具体实现二:嵌套查询: 先使用sql语句查询第一个表中的数据===>映射为resultMap; 再使用resultMap,配置查询的第一个表;其中需要使用到association, 注意的是,association里面不必在写查询语句了,只需要在标签处配置 需要查询的第二个表的信息; 接着,再查询第二表的信息,要与上面的resultMap进行配对。
对于这种问题,我们有两种方法:
实体类:
public class Student { private Integer id; private String name; private Teacher teacher; }
接口类:
public List<Student> getStudent(); public List<Student> getStudent2();
第一种:根据查询嵌套处理
<!-- 多对一 方式一:--> <select id="getStudent" resultMap="st"> select * from student </select> <!-- 复杂的属性需要:association->对象; collection->集合 --> <resultMap id="st" type="student"> <result property="id" column="id"/> <result property="name" column="name"/> <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/> </resultMap> <select id="getTeacher" resultType="teacher"> select * from teacher where id=#{tid} </select>
第二种:按结果查询嵌套
<select id="getStudent2" resultMap="st2"> select student.id as sid,student.name as sname,teacher.name as tname from student,teacher where student.tid = teacher.id </select> <resultMap id="st2" type="student"> <result property="id" column="sid"/> <result property="name" column="sname"/> <association property="teacher" javaType="Teacher"> <result property="name" column="tname"/> </association> </resultMap>
例如:一个老师对应多个学生;
一对多查询:使用<resultMap>中的collection; 具体实现一:根据结果集映射查询 先使用sql连表查询语句===>映射为resultMapp; 在使用resultMap,配置查询语句的数据的别名问题,其中需要使用到collection; 再在collection中配置查询数据库列的信息; 注意的是: 相较于association,还需要多配置实体类中对应的 ofType(集合中获取泛型信息); 具体实现二:嵌套查询: 先使用sql语句查询第一个表中的数据===>映射为resultMap; 在使用resultMap,配置查询的第一个表;其中是要到的collection; 注意的是,collection里面不必在写查询语句了,只需要在标签处配置; 注意的是: 相较于association,还需要多配置实体类中对应的 javaType(指定属性的类型),ofType(集合中获取泛型信息); 最后,再进行查询第二个表,要与上面的resultMap进行配对;
对于这种问题,我们有两种方法:
实体类:
public class Student { private Integer id; private String name; private Integer tid; }
public class Teacher { private Integer id; private String name; List<Student> students; }
接口类:
List<Teacher> getTeacher(@Param("tid") int id); List<Teacher> getTeacher2(@Param("tid") int id);
第一种:根据查询嵌套处理
<!-- collection 方式一: --> <select id="getTeacher" resultMap="ts"> select * from teacher where id = #{tid} </select> <resultMap id="ts" type="teacher"> <collection property="students" javaType="ArrayList" ofType="Student" column="id" select="getstudent"> </collection> </resultMap> <select id="getstudent" resultType="student"> select * from mybatis.student where tid = #{tid}; </select>
第二种:按结果查询嵌套
<!-- collection 方式二: --> <select id="getTeacher2" resultMap="ts2"> select s.id as sid,s.name as sname,t.name as tname,t.id as tid from student as s,teacher as t where s.tid = t.id and t.id = #{tid}; </select> <resultMap id="ts2" type="teacher"> <result property="id" column="tid"/> <result property="name" column="tname"/> <!-- javaType="" 指定属性的类型! 集合中获取泛型信息,需要使用ofType获取 --> <collection property="students" ofType="Student"> <result property="id" column="sid"/> <result property="name" column="sname"/> <result property="tid" column="tid"/> </collection> </resultMap>
动态 SQL 是 MyBatis 的强大特性之一。借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。
简单来说,动态SQL就是指根据不同的条件生成不同的SQL语句。
if
接口编写:
//动态sql:if public List<Blog> getBlogm(Map<String,String> map);
mapper配置编写:
<!-- 动态 SQL·if: --> <select id="getBlogm" resultType="blog" parameterType="map"> select * from mybatis.blog <where> <if test="title != null"> and title = "blog1" </if> <if test="author != null"> and author = "zx" </if> </where> </select>
测试编写:
public void getBlogm(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap<String,String> map = new HashMap(); map.put("title","blog1"); List<Blog> blogm = mapper.getBlogm(map); System.out.println(blogm); sqlSession.close(); }
choose (when, otherwise)
其中接口类,测试类的配置基本都是一样的。
<!-- choose (when, otherwise): --> <select id="getBlogmChoose" resultType="blog" parameterType="map"> select * from mybatis.blog <where> <choose> <when test=" title != null"> and title = "title1" </when> <otherwise> and id = 2 </otherwise> </choose> </where> </select>
trim (where, set)
<update id="updateTrim" parameterType="map"> update mybatis.blog <set> title=#{title} </set> where id=#{id} </update>
foreach
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。
<!-- foreach: --> <select id="getBlogmFe" parameterType="map" resultType="blog"> select * from mybatis.blog where id in <foreach collection="list" item="item" index="1" open="(" separator="," close=")"> #{item} </foreach> </select>
注意:
collection:指你需要迭代的对象;
item:集合项,在下面需要使用;
index:索引值;
open:迭代值开始的符号;
separator:迭代值中间的分隔符;
close:迭代值结尾的符号;
有的时候,我们可能会将一些功能的部分抽取出来,方便复用!这时候,我们就需要用到SQL片段了。
在代码中使用<sql>
标签抽取公共代码,然后使用<include>
标签在需要的地方引用公共代码片段。
<!-- SQL片段: --> <sql id="psql"> <if test="title != null"> and title = #{title} </if> <if test="author != null"> and author = #{author} </if> </sql>
<select id="getBlogm" resultType="blog" parameterType="map"> select * from mybatis.blog <where> <include refid="psql"></include> </where> </select>
一级缓存:存在于每一个测试类中,即一脱离了这个测试类,缓存就会被自动删除。默认开启
二级缓存:存在于每一个对应的Mapper中,即脱离了这个Mapper,缓存就会删除。需要手动开启
其中,开启二级缓存:
1、开启全局缓存
<!--显示的开启全局缓存--> <setting name="cacheEnabled" value="true"/>
2、在需要使用的二级缓存的Mapper中使用:
<!--在当前Mapper.xml中使用二级缓存--> <cache/>
也可以自定义缓存参数:
<!--在当前Mapper.xml中使用二级缓存--> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
注意:开启二级缓存的时候,我们需要将实体类序列换,不然会出错。