目录
简介:MyBatis,封装JDBC,负责访问数据库,完成持久化操作
| Java概念 | 数据库概念 |
| 类 | 表名 |
| 属性 | 字段 |
| 对象 | 行 |
理解:mybatis中通过sql语句查询到的结果与类中的中的属性一一映射;形成一个对象(如果查询到的属性少于pojo对象中的属性,那么pojo对象中多出的属性为默认值)resultType或resultMap中对应的返回值类型与pojo中的类一一映射。
- <!--依赖集-->
- <dependencies>
- <!--mybatis依赖-->
- <dependency>
- <groupId>org.mybatis</groupId>
- <artifactId>mybatis</artifactId>
- <version>3.5.7</version>
- </dependency>
- <!--单元测试依赖-->
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.12</version>
- <scope>test</scope>
- </dependency>
- <!--mysql数据库依赖-->
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- <version>8.0.30</version>
- </dependency>
- <!--添加日志依赖-->
- <dependency>
- <groupId>log4j</groupId>
- <artifactId>log4j</artifactId>
- <version>1.2.17</version>
- </dependency>
- <!-- lombok依赖包 -->
- <dependency>
- <groupId>org.projectlombok</groupId>
- <artifactId>lombok</artifactId>
- <version>1.18.24</version>
- <scope>provided</scope>
- </dependency>
- </dependencies>
- #放前缀就是为了避免重名
- jdbc.driver=com.mysql.cj.jdbc.Driver
- jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
- jdbc.username=root
- jdbc.password=root
- "1.0" encoding="UTF-8" ?>
- configuration
- PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
- "http://mybatis.org/dtd/mybatis-3-config.dtd">
- <configuration>
-
-
- <properties resource="jdbc.properties"/>
- <settings>
-
- <setting name="mapUnderscoreToCamelCase" value="true"/>
-
- <setting name="lazyLoadingEnabled" value="true"/>
- <setting name="aggressiveLazyLoading" value="false"/>
- settings>
-
- <typeAliases>
-
-
-
-
- <package name="cn.tedu.mybatis.pojo"/>
- typeAliases>
-
-
- <environments default="development">
- <environment id="development">
-
-
- <transactionManager type="JDBC"/>
-
-
-
-
- <dataSource type="POOLED">
-
- <property name="driver" value="${jdbc.driver}"/>
-
- <property name="url" value="${jdbc.url}"/>
- <property name="username" value="${jdbc.username}"/>
- <property name="password" value="${jdbc.password}"/>
- dataSource>
- environment>
- environments>
-
- <mappers>
-
-
-
- <package name="cn.tedu.mybatis.mapper"/>
- mappers>
- configuration>
- "1.0" encoding="UTF-8" ?>
- mapper
- PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
- "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
- <mapper namespace="cn.tedu.mybatis.mapper.UserMapper">
-
- <insert id="insertUser">
- insert into t_user values(null ,'admin','123456',23,'男','12345@qq.com')
- insert>
- <update id="updateUser">
- update t_user set username='root',password='123' where id=3
- update>
- <delete id="deleteUser">
- delete from t_user where id=3
- delete>
-
-
-
-
- <select id="getUserById" resultType="User">
- select * from t_user where id=5
- select>
- <select id="getAllUser" resultType="cn.tedu.mybatis.pojo.User">
- select * from t_user;
- select>
- mapper>
- <?xml version="1.0" encoding="UTF-8"?>
- <!--log4j.xml文件内,名字不可改变-->
- <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
- <!--日志配置文件-->
- <log4j:configuration>
- <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
- <param name="Encoding" value="UTF-8"/>
- <layout class="org.apache.log4j.PatternLayout">
- <param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n"/>
- </layout>
- </appender>
- <logger name="java.sql">
- <level value="debug"/>
- </logger>
- <logger name="org.apache.ibatis">
- <level value="info"/>
- </logger>
- <root>
- <level value="debug"/>
- <appender-ref ref="STDOUT"/>
- </root>
- </log4j:configuration>
- @Test
- public void testInsert() throws Exception {
- //获取核心配置文件的输入流
- InputStream is = Resources.getResourceAsStream("mybatis-config-xml");
- //获取sqlsessionFactoryBuilder对象
- SqlSessionFactoryBuilder sfb = new SqlSessionFactoryBuilder();
- //获取sqlsessionFactory对象
- SqlSessionFactory sf = sfb.build(is);
- //获取sqlsession对象——mybatis提供的操作数据可得对象(这里面的true表示自动提交事务,就是不用写后面的commit方法了)
- SqlSession sqlSession = sf.openSession(true);
- //获取UserMapper的代理实现类对象(通过代理模式来创建UserMapper的代理实现类)
- //通过当前mapper接口的全类名找到当前的映射文件
- UserMapper mapper = sqlSession.getMapper(UserMapper.class);
- //通过调用方法,找到映射文件中的方法
- int i = mapper.insertUser();
- //提交事务
- //sqlSession.commit();
- //sqlSession.rollback();回滚事务
- System.out.println(i);
- //关闭会话
- sqlSession.close();
- }
debug(调试)
注意:配置日志级别,这样在打印日志的时候只会打印相同级别以及高于自己配置的日志级别日志
作用:自动生成Get/Set/toString/无参构造/全参构造/equals/hashcode等方法
- 测试:List<User> user = mapper.getUserByUsername("admin"); (mapper为接口代理对象)
-
- 接口:List<User> getUserByUsername(String username);
-
- 映射文件:select * from t_user where username='${username}'; (为字符串拼接方式)
- select * from t_user where username=#{username}; (为占位符方式)
- 测试:User user = mapper.checkLogin("admin","123456");
-
- 接口:User checkLogin(@Param("name") String username,@Param("pwd") String password);
-
- 配置文件:select * from t_user where username=#{name} and password=#{pwd}
- 测试:
- HashMap<String, Object> map = new HashMap<String, Object>();
- map.put("name", "admin");
- map.put("pwd", "123456");
- User user = mapper.checkLoginByMap(map);
-
- 接口:User checkLoginByMap(Map<String,Object> map);
-
- 配置文件:select * from t_user where username='${name}' and password=#{pwd};
- 测试:
- User user = new User(null,"root","123456",33,"女","123@qq.com");(我有全参构造)
- int i = mapper.insertUser(user);
-
- 接口:int insertUser(User user);
-
- 配置文件:insert into t_user values(null,#{username},#{password},#{age},#{gender},#{email})
- 测试:List<User> allUser = mapper.getAllUser();
-
- 接口:List<User> getAllUser();
-
- 配置文件:
- <select id="getAllUser" resultType="user">
- select * from t_user;
- </select>
注意:若sql查询的结果为多条时,一定不能以实体类型作为方法的返回值,否则会抛出异常,若查询结果为1条时,此时可以使用实体类型或list集合类型作为方法的返回值
- 测试:Integer count = mapper.getCount();
-
- 接口:Integer getCount();
-
- 配置文件:
- <select id="getCount" resultType="java.lang.Integer">
- select count(*) from t_user;
- </select>
注意:这里的resultType返回值也可以直接写Integer或int(忽略大小写——本质别名)
- 测试:Map<String, Object> userByIdToMap = mapper.getUserByIdToMap(1);
-
- 接口:Map<String,Object> getUserByIdToMap(@Param("id") Integer id);
-
- 配置文件:
- <select id="getUserByIdToMap" resultType="map">
- select * from t_user where id=#{id}
- </select>
理解:把通过id查到的数据用map集合进行封装(一条数据转化为一个map)
- 测试:List<Map<String, Object>> allUserToMap = mapper.getAllUserToMap();
- 接口:List<Map<String,Object>> getAllUserToMap();
- 配置文件:
- <select id="getAllUserToMap" resultType="map">
- select * from t_user;
- </select>
- 测试:Map<String, Object> allUserToMap = mapper.getAllUserToMap();
-
- 接口:
- @MapKey("id")——把查询到的id作为键,把查询到的对象(也就是一条一条的数据)作为值
- Map<String,Object> getAllUserToMap();
-
- 配置文件:
- <select id="getAllUserToMap" resultType="map">
- select * from t_user;
- </select>
- 测试:List<User> a = mapper.getUserByLike("a");
-
- 接口:List<User> getUserByLike(@Param("m") String m);
-
- 配置文件:
- 方式1:
- <select id="getUserByLike" resultType="user">
- select * from t_user where username like "%"#{m}"%";
- </select>
- 方式2:
- <select id="getUserByLike" resultType="user">
- select * from t_user where username like "%${m}%";
- </select>
- 测试:List<User> a = mapper.getUserByLike("%a%");
-
- 接口:List<User> getUserByLike(@Param("m") String m);
-
- 配置文件:
- <select id="getUserByLike" resultType="user">
- select * from t_user where username like #{m};
- </select>
注意:#{内容}若写在字符串里面则会被当作字符串的一部分,不会被当作占位符
- 测试:int i = mapper.deleteMoreUser("1,7");
-
- 接口:int deleteMoreUser(@Param("ids") String ids);
-
- 配置文件:
- 方法1:
- <delete id="deleteMoreUser">
- delete from t_user where id in(${ids})
- </delete>
- 测试:List<User> t_user = mapper.getUserList("T_user");
-
- 接口:List<User> getUserList(@Param("tableName") String tableName);
-
- 配置文件:
- <select id="getUserList" resultType="user">
- select * from ${tableName}
- </select>
主要原因:表名是不能加单引号的,所以不能用#{},因为它默认会为值添加单引号
- 测试:
- User user = new User(null, "lili", "123456", 23, "男", "12@qq.com");
- mapper.insertUser(user);——这里我没为id赋值,但是输出却有id值
-
- 接口:void insertUser(User user);
-
- 配置文件:
- <!--获取自增的主键,keyProperty:把获取到自增的主键存储到我们传输过来的实体类对象的对应属性中-->
- <insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
- insert into t_user values (null ,#{username},#{password},#{age},#{gender},#{email})
- </insert>
- 测试:Emp empByEmpId = mapper.getEmpByEmpId(1);
-
- 接口:Emp getEmpByEmpId(@Param("empId") Integer empId);
-
- 配置文件:
- <select id="getEmpByEmpId" resultType="emp">
- select emp_id empId,emp_name empName,age,gender from t_emp where emp_id=#{empId}
- </select>
注意:这里起的别名是对应实体类(Emp)中属性的名称
在mybatis-config.xml核心配置文件中设置如下(注意设置标签的顺序问题)
- <settings>
-
- <setting name="mapUnderscoreToCamelCase" value="true"/>
- settings>
- 测试:Emp empByEmpId = mapper.getEmpByEmpId(1);
-
- 接口:Emp getEmpByEmpId(@Param("empId") Integer empId);
-
- 配置文件:
- <select id="getEmpByEmpId" resultMap="empResultMap">
- select * from t_emp where emp_id=#{empId}
- </select>
- <!--id:resultMap配置的id(随便写都行)
- type:返回值的类型-->
- <resultMap id="empResultMap" type="emp">
- <!--处理主键映射-->
- <!--将字段里的emp_id与对象中的empId映射-->
- <id column="emp_id" property="empId"></id>
- <!--普通字段的映射-->
- <result column="emp_name" property="empName"></result>
- </resultMap>
- //所用到的实体类
- public class Emp {
- private Integer empId;
- private String empName;
- private Integer age;
- private String gender;
- private Dept dept;
- }
- <select id="getEmpAnddeptByEmpId" resultMap="empAndDeptResultMap">
- select t_emp.*,t_dept.* from t_emp left join t_dept on t_emp.dept_id=t_dept.dept_id where t_emp.emp_id=#{empId}
- </select>
- <resultMap id="empAndDeptResultMap" type="emp">
- <id column="emp_id" property="empId"></id>
- <result column="emp_name" property="empName"></result>
- <result column="dept_id" property="dept.deptId"></result>
- <result column="dept_name" property="dept.deptName"></result>
- </resultMap>
- 注意:dept表为emp表的内部属性
- <select id="getEmpAnddeptByEmpId" resultMap="empAndDeptResultMap">
- select t_emp.*,t_dept.* from t_emp left join t_dept on t_emp.dept_id=t_dept.dept_id where t_emp.emp_id=#{empId}
- </select>
- <resultMap id="empAndDeptResultMap" type="emp">
- <id column="emp_id" property="empId"></id>
- <result column="emp_name" property="empName"></result>
- <!--处理多对一映射关系,必须是处理实体类型中的属性-->
- <!--javaType用来设置当前属性的类型-->
- <association property="dept" javaType="Dept">
- <id column="dept_id" property="deptId"></id>
- <result column="dept_name" property="deptName"></result>
- </association>
- </resultMap>
注意:association标签里还可以有autoMapping属性,若此属性设置为true,则可以使被封装的属性与和属性名称相同的字段进行自动映射(autoMapping设置为true时,若映射的字段和对象的属性一致时则可以省略不写)
- 一次查询:
- <select id="getEmpAndDeptByStep" resultMap="empAndDeptByStepResultMap">
- select * from t_emp where emp_id=#{empId}
- </select>
- <resultMap id="empAndDeptByStepResultMap" type="emp" autoMapping="true">
- <id column="emp_id" property="empId"></id>
- <result column="emp_name" property="empName"></result>
- <!--select属性表示二次查询sql的id,column表示从第一次查询结果中返回的某个字段作为二次查询的条件-->
- <!--fetchType属性在全局开启了延迟加载情况下设置某一条语句立即加载-->
- <association property="dept" select="cn.tedu.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo"
- column="dept_id" autoMapping="true" fetchType="eager">
- </association>
- </resultMap>
-
- 二次查询:
- <select id="getEmpAndDeptByStepTwo" resultType="dept">
- select * from t_dept where dept_id = #{deptId}
- </select>
- public class Dept {
- private Integer deptId;
- private String deptName;
- private List<Emp> emps;
- }
- <select id="getDeptAndEmpByDeptId" resultMap="deptAndEmpResultMap">
- select * from t_dept left join t_emp on t_dept.dept_id=t_emp.dept_id where t_dept.dept_id=#{deptId}
- </select>
- <resultMap id="deptAndEmpResultMap" type="dept" autoMapping="true">
- <id column="dept_id" property="deptId"></id>
- <!--ofType:设置当前集合中的类型-->
- <collection property="emps" ofType="emp" autoMapping="true">
- <id column="emp_id" property="empId"></id>
- <result column="emp_name" property="empName"></result>
- </collection>
- </resultMap>
注意:不管映射不映射,主键一定要写上
- 一次查询:
- <select id="getDeptAndEmpByStepOne" resultMap="deptAndEmpResultMapByStep">
- select * from t_dept where dept_id=#{deptId}
- </select>
- <resultMap id="deptAndEmpResultMapByStep" type="dept" autoMapping="true">
- <id column="dept_id" property="deptId"></id>
- <collection property="emps" column="dept_id"
- select="cn.tedu.mybatis.mapper.EmpMapper.getDeptAndEmpByStepTwo" autoMapping="true">
- <id column="emp_id" property="empId"></id>
- </collection>
- </resultMap>
-
- 二次查询:
- <select id="getDeptAndEmpByStepTwo" resultType="emp">
- select * from t_emp where dept_id=#{deptId}
- </select>
Mybatis框架的动态sql技术是一种根据特定条件动态拼接sql语句的功能,它存在的意义就是为了解决拼凑sql语句字符串时的痛点问题
- 测试:
- Emp emp = new Emp(null, "张三", 20, "男");
- List<Emp> empByCondition = mapper.getEmpByCondition(emp);
-
- 接口:List<Emp> getEmpByCondition(Emp emp);
-
- 配置:
- 方法1:
- <select id="getEmpByCondition" resultType="emp">
- select * from t_emp <where>
- <if test="empName!=null and empName!=''">
- emp_name=#{empName}
- </if>
- <if test="age!=null and age!=''">
- and age=#{age}
- </if>
- <if test="gender!=null and gender!=''">
- and gender=#{gender}
- </if>
- </where>
- </select>
-
- 方法2:
- <select id="getEmpByCondition" resultType="emp">
- select * from t_emp where 1=1
- <if test="empName!=null and empName!=''">
- and emp_name=#{empName}
- </if>
- <if test="age!=null and age!=''">
- and age=#{age}
- </if>
- <if test="gender!=null and gender!=''">
- and gender=#{gender}
- </if>
- </select>
-
- 方法3:
- <select id="getEmpByCondition" resultType="emp">
- select * from t_emp
- <trim prefix="where" suffixOverrides="and">
- <if test="empName!=null and empName!=''">
- emp_name=#{empName} and
- </if>
- <if test="age!=null and age!=''">
- age=#{age} and
- </if>
- <if test="gender!=null and gender!=''">
- gender=#{gender}
- </if>
- </trim>
- </select>
注意:
- 测试:
- Emp w = new Emp(2, "王五", 23, null);
- int i = mapper.updateEmp(w);
-
- 接口:int updateEmp(Emp emp);
-
- 配置文件:
- <update id="updateEmp">
- update t_emp
- <set>
- <if test="empName!=null and empName!=''">emp_name=#{empName},</if>
- <if test="age!=null and age!=''">age=#{age},</if>
- <if test="gender!=null and gender!=''">gender=#{gender}</if>
- </set>
- where emp_id=#{empId}
- </update>
注意:set标签主要处理多余的逗号问题,也会生成set关键字
- 测试:
- Emp emp = new Emp(null, "张三", 20, "男");
- List<Emp> empByChoose = mapper.getEmpByChoose(emp);
-
- 接口:List<Emp> getEmpByChoose(Emp emp);
-
- 配置:
- <select id="getEmpByChoose" resultType="emp">
- select * from t_emp
- <where>
- <choose>
- <when test="empName!=null and empName!=''">emp_name=#{empName}</when>
- <when test="age!=null and age!=''">age=#{age}</when>
- <otherwise>gender=#{gender}</otherwise>
- </choose>
- </where>
- </select>
注意:
- 测试:
- Emp emp = new Emp(null, "张三", 20, "男");
- Emp emp1 = new Emp(null, "张四", 21, "男");
- Emp emp2 = new Emp(null, "张五", 22, "男");
- List<Emp> emps = Arrays.asList(emp, emp1, emp2);
- mapper.insertMoreEmp(emps);
-
- 接口:void insertMoreEmp(@Param("emps") List<Emp> emps);
-
- 配置文件:
- <!--collection里面传@Param里面对应的键,表示要循环的集合或数组;item里面的属性表示用什么表示集合里面的数据;separator表示循环所用分隔符-->
- <insert id="insertMoreEmp">
- insert into t_emp values
- <foreach collection="emps" item="emp" separator=",">
- (null,#{emp.empName},#{emp.age},#{emp.gender},null)
- </foreach>
- </insert>
注意:如果不写@Param注解,那么如果你的参数是一个list集合,则mybatis会把他以list为键,后以list集合为值放入map集合中;若你的参数是一个数组的话,则mybatis也会把它放入map中,以Array为键,以其参数值为值
- 测试:
- Integer[] integers = {5,6,7};
- mapper.deleteMoreEmp(integers);
-
- 接口:void deleteMoreEmp(@Param("empIds") Integer[] empIds);
-
- 配置文件:
- 方式1:
- <delete id="deleteMoreEmp">
- delete from t_emp where emp_id in
- (
- <foreach collection="empIds" item="empId" separator=",">
- #{empId}
- </foreach>
- )
- </delete>
- 注意:foreach里面还有2个属性,分别为open和close,分别表示以什么符号开始和以什么符号结束(不写括号时使用)
-
- 方式2:
- <delete id="deleteMoreEmp">
- delete from t_emp where
- <foreach collection="empIds" item="empId" separator="or">
- emp_id=#{empId}
- </foreach>
- </delete>
- 测试:List<Emp> emps = mapper.selectAll();
-
- 接口:List<Emp> selectAll();
-
- 配置文件:
- <!--sql中的id表示sql片段的标识,include标签表示引用sql,refid与sql中id对应-->
- <sql id="empColumn">
- emp_id,emp_name,age,gender,dept_id
- </sql>
- <select id="selectAll" resultType="emp">
- select <include refid="empColumn"></include> from t_emp;
- </select>
- 测试:Emp emp = mapper.getById(1);
-
- 接口:
- @Select("select * from t_emp where emp_id=#{id}")
- Emp getById(@Param("id") Integer id);
注意:
一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据会从缓存中直接获取,不会从数据库重新访问
注意:一级缓存默认开启
- @Test
- public void testGetEmpById(){
- SqlSession session = SqlSessionUtils.getSession();
- CacheMapper mapper = session.getMapper(CacheMapper.class);
- Emp empById = mapper.getEmpById(1);
- //清空一级缓存
- session.clearCache();
- Emp empById1 = mapper.getEmpById(1);
- System.out.println(empById+"\n"+empById1);
- }
注意:这里我清空一级缓存了,所以会查2次
二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSessionFactory创建的SqlSession,默认开启,但是需要标识
两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效
- @Data
- @AllArgsConstructor
- @NoArgsConstructor
- public class Emp implements Serializable {
- private Integer empId;
- private String empName;
- private Integer age;
- private String gender;
- }
接口方法:Emp getEmpById(Integer empId);
- <mapper namespace="cn.tedu.mybatis.mapper.CacheMapper">
- <cache/>
- <select id="getEmpById" resultType="emp">
- select * from t_emp where emp_id = #{empId}
- </select>
- </mapper>
- @Test
- public void testCache() throws Exception {
- InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");
- //创建第一个sqlsessionFactory
- SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(stream);
- SqlSession sqlSession = sqlSessionFactory.openSession(true);
- SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
- CacheMapper mapper = sqlSession.getMapper(CacheMapper.class);
- Emp empById = mapper.getEmpById(1);
- System.out.println(empById);
- //只有将sqlsession关闭之后保存在一级缓存中的数据才会保存在二级缓存中,二级缓存才能生效
- sqlSession.close();
- CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
- Emp empById1 = mapper1.getEmpById(1);
- System.out.println(empById1);
- sqlSession1.close();
- }
注意:只有将sqlsession关闭之后保存在一级缓存中的数据才会保存在二级缓存中,二级缓存才能够生效
前言:这些属性可以在cache标签里面或注解@CacheNamespace()里面设置
eviction属性:缓存回收策略,默认为LRU
- LRU:最近最少使用的(移除最长时间不被使用的对象)
- FIFO:先进先出(按对象进入缓存的顺序来移除他们)
- SOFT:软引用(移除基于垃圾回收器状态和软引用规则的对象)
- WEAK:弱引用(更积极地移除基于垃圾收集器状态和弱引用规则的对象)
flushInterval属性:刷新间隔,单位毫秒(不然只有关闭一级缓存才能刷新)
默认情况下是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新
size属性:引用数目,正整数
代表缓存最多可以存储多少对象,太大容易导致内存溢出
readOnly属性:只读,true/false
- true:只读缓存,会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势
- false:读写缓存,会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false