技术先进的网站建设,婚庆公司多少钱,wordpress好看的页面跳转,自己如何开网站MyBatis优点
轻量级#xff0c;性能出色 SQL 和 Java 编码分开#xff0c;功能边界清晰。Java代码专注业务、SQL语句专注数据 开发效率稍逊于HIbernate#xff0c;但是完全能够接受
补充#xff1a;POJO
一#xff1a;什么是POJO POJO的名称有多种#xff0c;pure old…MyBatis优点
轻量级性能出色 SQL 和 Java 编码分开功能边界清晰。Java代码专注业务、SQL语句专注数据 开发效率稍逊于HIbernate但是完全能够接受
补充POJO
一什么是POJO POJO的名称有多种pure old java object 、plain ordinary java object 等。 按照Martin Fowler的解释是“Plain Old Java Object”从字面上翻译为“纯洁老式的java对象”但大家都使用“简单java对象”来称呼它。 POJO的内在含义是指那些没有从任何类继承、也没有实现任何接口更没有被其它框架侵入的java对象。
二为什么会有POJO 主要是Java的开发者被EJB的繁杂搞怕了大家经过反思又回归“纯洁老式”的JavaBean即有无参构造函数每个字段都有getter和setter的java类。 无参构造器必须要有 Mapper接口
MyBatis中的mapper接口相当于以前的dao。但是区别在于mapper仅仅是接口我们不需要 提供实现类。 DAO层叫数据访问层全称为data access object某个DAO一定是和数据库的某一张表一一对应的其中封装了CRUD增加Create、检索Retrieve、更新Update和删除Delete基本操作DAO只做原子操作。无论多么复杂的查询dao只是封装增删改查。至于增删查改如何去实现一个功能dao是不管的 起名规则POJO的名字 Mapper
创建MyBatis的映射文件
MyBatis映射文件用于编写SQL访问以及操作表中的数据
MyBatis映射文件存放的位置是src/main/resources/mappers目录下
相关概念ORMObject Relationship Mapping对象关系映射。 对象Java的实体类对象 关系关系型数据库 映射二者之间的对应关系
命名规则表所对应的实体类的类名Mapper.xml最好是一个mapper接口及其对应一个配置文件的名字相同即有一个Java文件时UserMapper.java对应一个UserMapper.xml 因此一个映射文件对应一个实体类对应一张表的操作对应一个mapper接口 mapper接口中的一个方法对应映射文件中的一个SQL语句
MyBatis中可以面向接口操作数据要保证两个一致 1、mapper接口的全类名和映射文件的命名空间namespace保持一致
2、mapper接口中方法的方法名和映射文件中编写SQL的标签的id属性保持一致 Mybatis有两个重要文件核心文件和映射文件 核心文件用于连接数据库 映射文件用于操作数据库 映射文件中写的是SQL语句 public interface UserMapper {/*** 添加用户信息*/int insertUser();
}?xml version1.0 encodingUTF-8 ?
!DOCTYPE mapperPUBLIC -//mybatis.org//DTD Mapper 3.0//ENhttp://mybatis.org/dtd/mybatis-3-mapper.dtd
mapper namespacecom.atguigu.mybatis.mapper.UserMapper!--int insertUser();--!--这个SQL语句对应mapper接口中的insertUser--nsert idinsertUser!--就是这部分--insert into t_user values(null,admin,123456,23,男,12345qq.com)/insert
/mapper通过junit测试功能
package com.atguigu.mybatis;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 org.junit.Test;
import com.atguigu.mybatis.mapper.UserMapper;import java.io.IOException;
import java.io.InputStream;public class MyBatisTest {Testpublic void testInsert() throws IOException {// 获取核心配置文件的输入流核心配置文件中又注入了映射文件所以获取核心配置文件即可InputStream resourceAsStream Resources.getResourceAsStream(mybatis-config.xml);// 获取SqlSessionFactoryBuilder 对象 - 工厂构建器SqlSessionFactoryBuilder sqlSessionFactoryBuilder new SqlSessionFactoryBuilder();// 创建 SqlSessionFactory - 创建会话SqlSessionFactory sqlSessionFactory sqlSessionFactoryBuilder.build(resourceAsStream);// 获取sqlSession - MyBatis 提供的操作数据库的对象不会自动提交事务SqlSession sqlSession sqlSessionFactory.openSession();//获取sqlSession - MyBatis 提供的操作数据库的对象可以自动提交事务//SqlSession sqlSession sqlSessionFactory.openSession(true);// 获得Mapper接口的代理类 - 操纵Mapper类执行数据库操作UserMapper是一个接口不能直接实例化所以通过这种方式创建其对象UserMapper userMapper sqlSession.getMapper(UserMapper.class);// 执行SQL操作Integer rows userMapper.insertUser();System.out.println(rows rows);// 提交事务 - 事务是默认开启的不然数据库中并没有这条记录sqlSession.commit();// 关闭资源sqlSession.close();}
}SqlSession代表Java程序和数据库之间的会话。HttpSession是Java程序和浏览器之间的会话
SqlSessionFactory是“生产”SqlSession的“工厂”。
工厂模式如果创建某一个对象使用的过程基本固定那么我们就可以把创建这个对象的
日志级别
日志的xml文件名字必须是——log4j.xml environments defaultdevelopmentenvironment iddevelopment!-- 事务管理器 --transactionManager typeJDBC/transactionManager!-- 数据源 即连接池--dataSource typePOOLEDproperty namedriver valuecom.mysql.cj.jdbc.Driver/property nameurl valuejdbc:mysql://192.168.23.128:3306/ssm?useSSLfalseamp;serverTimezoneAsia/Shanghaiamp;characterEncodingutf-8amp;autoReconnecttrue/property nameusername valueroot/property namepassword value123456//dataSource/environmentenvironment idtest!-- 事务管理器 --transactionManager typeJDBC/transactionManager!-- 数据源 即连接池--dataSource typePOOLEDproperty namedriver valuecom.mysql.cj.jdbc.Driver/property nameurl valuejdbc:mysql://192.168.23.128:3306/ssm?useSSLfalseamp;serverTimezoneAsia/Shanghaiamp;characterEncodingutf-8amp;autoReconnecttrue/property nameusername valueroot/property namepassword value123456//dataSource/environment/environmentsFATAL(致命)ERROR(错误)WARN(警告)INFO(信息)DEBUG(调试)
从左到右打印的内容越来越详细级别越来越低
核心配置文件详解
核心配置文件中的标签必须按照固定的顺序
properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,refl
ectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?
typeAlias
typeAlias设置某个类型的别名 属性 type设置需要设置别名的类型 alias设置某个类型的别名若不设置该属性那么该类型拥有默认的别名即类名且不区分大小写
environments
environments配置多个连接数据库的环境 可以配置多个环境比如测试环境和开发环境 ; 使用id区分不能重复。
属性 default设置默认使用的环境的id environments defaultdevelopmentenvironment iddevelopment!-- 事务管理器 --transactionManager typeJDBC/transactionManager!-- 数据源 即连接池--dataSource typePOOLEDproperty namedriver valuecom.mysql.cj.jdbc.Driver/!--驱动这里用的是mysql8--property nameurl valuejdbc:mysql://192.168.23.128:3306/ssm?useSSLfalseamp;serverTimezoneAsia/Shanghaiamp;characterEncodingutf-8amp;autoReconnecttrue/property nameusername valueroot/property namepassword value123456//dataSource/environmentenvironment idtest!-- 事务管理器 --transactionManager typeJDBC/transactionManager!-- 数据源 即连接池--dataSource typePOOLEDproperty namedriver valuecom.mysql.cj.jdbc.Driver/property nameurl valuejdbc:mysql://192.168.23.128:3306/ssm?useSSLfalseamp;serverTimezoneAsia/Shanghaiamp;characterEncodingutf-8amp;autoReconnecttrue/property nameusername valueroot/property namepassword value123456//dataSource/environment/environmentstransactionManager
transactionManager设置事务管理方式 属性 type“JDBC|MANAGED” JDBC表示当前环境中执行SQL时使用的是JDBC中原生的事务管理方式事务的提交或回滚需要手动处理 MANAGED被管理例如Spring
dataSource
dataSource配置数据源 属性 type设置数据源的类型 type“POOLED|UNPOOLED|JNDI” POOLED表示使用数据库连接池缓存数据库连接 UNPOOLED表示不使用数据库连接池 JNDI表示使用上下文中的数据源 例子在前面的代码中 总的配置文件
?xml version1.0 encodingUTF-8 ?
!DOCTYPE configurationPUBLIC -//mybatis.org//DTD Config 3.0//ENhttp://mybatis.org/dtd/mybatis-3-config.dtd
configuration!--MyBatis核心配置文件中标签的顺序properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?--!--引入properties文件--properties resourcejdbc.properties /!--设置类型别名--typeAliases!--typeAlias设置某个类型的别名属性type设置需要设置别名的类型alias设置某个类型的别名若不设置该属性那么该类型拥有默认的别名即类名且不区分大小写--!--typeAlias typecom.atguigu.mybatis.pojo.User/typeAlias--!--以包为单位将包下所有的类型设置默认的类型别名即类名且不区分大小写--package namecom.atguigu.mybatis.pojo//typeAliases!--environments配置多个连接数据库的环境属性default设置默认使用的环境的id--environments defaultdevelopment!--environment配置某个具体的环境属性id表示连接数据库的环境的唯一标识不能重复--environment iddevelopment!--transactionManager设置事务管理方式属性typeJDBC|MANAGEDJDBC表示当前环境中执行SQL时使用的是JDBC中原生的事务管理方式事务的提交或回滚需要手动处理MANAGED被管理例如Spring--transactionManager typeJDBC/!--dataSource配置数据源属性type设置数据源的类型typePOOLED|UNPOOLED|JNDIPOOLED表示使用数据库连接池缓存数据库连接UNPOOLED表示不使用数据库连接池JNDI表示使用上下文中的数据源--dataSource typePOOLED!--设置连接数据库的驱动--property namedriver value${jdbc.driver}/!--设置连接数据库的连接地址--property nameurl value${jdbc.url}/!--设置连接数据库的用户名--property nameusername value${jdbc.username}/!--设置连接数据库的密码--property namepassword value${jdbc.password}//dataSource/environmentenvironment idtesttransactionManager typeJDBC/dataSource typePOOLEDproperty namedriver valuecom.mysql.cj.jdbc.Driver/property nameurlvaluejdbc:mysql://localhost:3306/ssmserverTimezoneUTC/property nameusername valueroot/property namepassword value123456//dataSource/environment/environments!--引入mybatis的映射文件--mappers!--mapper resourcemappers/UserMapper.xml/--!--以包为单位引入映射文件要求1、mapper接口所在的包要和映射文件所在的包一致2、mapper接口要和映射文件的名字一致--package namecom.atguigu.mybatis.mapper//mappers
/configurationMyBatis的增删改查
在 Mapper.xml 文件中使用 添加
!--int insertUser();--
insert idinsertUserinsert into t_user values(null,admin,123456,23,男)
/insert删除
!--int deleteUser();--
delete iddeleteUserdelete from t_user where id 7
/delete修改
!--int updateUser();--
update idupdateUserupdate t_user set usernameybc,password123 where id 6
/update查询
!--查询一个实体--
!--User getUserById();--
select idgetUserById resultTypecom.atguigu.mybatis.bean.Userselect * from t_user where id 2
/select
!--查询实体集合--
!--ListUser getUserList();--
select idgetUserList resultTypecom.atguigu.mybatis.bean.Userselect * from t_user
/select注意 查询的标签select必须设置属性resultType或resultMap用于设置实体类和数据库表的映射 关系 resultType自动映射用于属性名和表中字段名一致的情况,resultType可以直接返回给出的返回值类型意思是返回值类型 resultMap自定义映射用于一对多或多对一或字段名和属性名不一致的情况返回值是自定义实体类的情况意思是自定义映射 MyBatis获取参数值的两种方式
MyBatis获取参数值的两种方式${}和#{}
${}的本质就是字符串拼接#{}的本质就是占位符赋值
${}使用字符串拼接的方式拼接sql若为字符串类型或日期类型的字段进行赋值时需要手动加单引
号但是#{}使用占位符赋值的方式拼接sql此时为字符串类型或日期类型的字段进行赋值时
可以自动添加单引号
单个字面量类型的参数
若mapper接口中的方法参数为单个的字面量类型
此时可以使用{}和#{}以任意的名称获取参数的值注意{}需要手动加单引号
#{}方式 ${}方式 这样会报错因为admin没有加单引号会被认为是sql’数据表中的一个字段改成 其实大括号中的名字不一定非得是变量名任意的名字都可以成功获取不过建议使用参数名让其变得有意义 多个字面量类型的参数
若mapper接口中的方法参数为多个时
此时MyBatis会自动将这些参数放在一个map集合中以arg0,arg1…为键以参数为值
以param1,param2…为键以参数为值
因此只需要通过${}和#{}访问map集合的键就可以获取相对应的值注意 ${}需要手动加单引号
使用param方式使用arg0……就是将大括号中的param1改成arg0param2改成arg1……即可
map集合类型的参数
若mapper接口中的方法需要的参数为多个时此时可以手动创建map集合将这些数据放在
map中
只需要通过{}和#{}访问map集合的键就可以获取相对应的值注意${}需要手动加单引号 在select中设置的键值是username和password因为在test方法中在put时制定了map集合中两个键值的名字username和password和上一种情况相比就是这次我们是通过put对map进行手动添加而且自定义了键值的名称上一种情况是机器自动将我们的参数放到map中所以只能使用机器定义的键值名称arg0……param1等 实体类类型的参数
若mapper接口中的方法参数为实体类对象如下的User时此时可以使用{}和#{}通过访问实体类对象中的属性名获取属性值注意{}需要手动加单引号 注意属性和我们在java中编写的private int age 等内容没有关系而是和get、set方法有关系如果一个java类中没有设置相应的age等成员变量但是有其对应的get、set方法不会阻碍我们获取相应的成员变量。属性就是将这两个方法的get、set去掉剩余部分变成小写。 总结属性只和类中的get、set方法有关取值找get赋值找set与成员变量无关 User类的结构 id设置为null是想让他自增 使用Param标识参数
可以通过Param注解标识mapper接口中的方法参数此时会将这些参数放在map集合中以Param注解的value属性值为键以参数为值以param1,param2…为键以参数为值只需要通过${}和#{}访问map集合的键就可以获取相对应的值注意 ${}需要手动加单引号
与第二种情况比较可以自定义键值与第三种情况比较可以不需要手动向map中添加 想要访问参数的值需要先访问他对应的键所以#{}中的内容是键也就是Param括号中的内容 MyBatis的各种查询功能
查询一个实体类对象
/**
* 根据用户id查询用户信息
* param id
* return
*/
User getUserById(Param(id) int id);这里使用Param是因为字面量在查询的时候可以通过任意的内容来查询所以这里制定用id查询
!--User getUserById(Param(id) int id);--
select idgetUserById resultTypeUserselect * from t_user where id #{id}
/select查询一个list集合 当查询的数据为多条时不能使用实体类作为返回值否则会抛出异常TooManyResultsException 但是若查询的数据只有一条可以使用实体类或集合作为返回值 /**
* 查询所有用户信息
* return
*/
ListUser getUserList();!--ListUser getUserList();--
select idgetUserList resultTypeUserselect * from t_user
/select查询单行单列数据 /**
* 查询用户的总记录数
* return
* 在MyBatis中对于Java中常用的类型都设置了类型别名
* 例如 java.lang.Integer的别名int|integer
* 例如 int的别名_int|_integer
* 例如 String的别名string
* 例如 Map的别名map,List的别名list
*/
int getCount();!--int getCount();--
select idgetCount resultType_integerselect count(id) from t_user//4返回的结果是有多少个id
/select查询一条数据为map集合
举例加入我们想要查询员工数据表的最高工资、最低工资和平均工资这时没有其对应的实体类那应该将返回的数据存放成什么类型呢
其实实体类和Map都是通过键值对两者的最大区别就是实体类中的键值对是固定的只有设置好的那些比如name、age、email等查询为一个实体类必须要字段名和属性名一致但是Map中没有固定的键想查啥都行
/**
* 根据用户id查询用户信息为map集合
* param id
* return
*/
MapString, Object getUserToMap(Param(id) int id);!--MapString, Object getUserToMap(Param(id) int id);--
!--结果 {password123456, sex男 , id1, age23, usernameadmin}--
select idgetUserToMap resultTypemapselect * from t_user where id #{id}
/select注意如果查询的内容中有字段的值为空即null该字段的内容是不会放在map中的。 查询多条数据为map集合
注意一条数据对应一个map若有多条数据就会产生多个map集合不能只用一个map接受否则报错
方式一
/**
* 查询所有用户信息为map集合
* return
* 将表中的数据以map集合的方式查询一条数据对应一个map若有多条数据就会产生多个map集合此
时可以将这些map放在一个list集合中获取
*/
ListMapString, Object getAllUserToMap();方式二 通过MapKey注解设置map集合的键值是每条数据所对应的map集合 通过MapKey将查询某个字段的值作为大的Map的键
/**
* 查询所有用户信息为map集合
* return
* 将表中的数据以map集合的方式查询一条数据对应一个map若有多条数据就会产生多个map集合并
且最终要以一个map的方式返回数据此时需要通过MapKey注解设置map集合的键值是每条数据所对应的
map集合
*/
MapKey(id)
MapString, Object getAllUserToMap();!--MapString, Object getAllUserToMap();--
!--输出效果
{1{password123456, sex男, id1, age23, usernameadmin},2{password123456, sex男, id2, age23, username张三},3{password123456, sex男, id3, age23, username张三}
}
--
select idgetAllUserToMap resultTypemapselect * from t_user
/select特殊SQL的执行
模糊查询
/**
* 测试模糊查询
* param mohu
* return
*/
ListUser testMohu(Param(mohu) String mohu);!--ListUser testMohu(Param(mohu) String mohu);--
select idtestMohu resultTypeUser!--select * from t_user where username like %${mohu}%--!--select * from t_user where username like concat(%,#{mohu},%)--select * from t_user where username like %#{mohu}%
/select1、like %${mohu}%中 ${mohu}由于 ${}会解释称占位符但是因为是在单引号中所以并不会将它作为占位符使用而是看作字符串的一部分会报错 2、concat是拼接字符串的函数 3、用双引号的方式使用最多 批量删除
/**
* 批量删除
* param ids
* return
*/
int deleteMore(Param(ids) String ids);!--int deleteMore(Param(ids) String ids);--
delete iddeleteMoredelete from t_user where id in (${ids})
/delete这里用到 ${}是因为如果用#{}时会自动添加单引号的但是在sql语句中delete from t_user where id in (‘9,10’)是错误的正确的写法是delete from t_user where id in (9,10)——删除id为9和10的数据。 动态设置表名
/**
* 动态设置表名查询所有的用户信息
* param tableName
* return
*/
ListUser getAllUser(Param(tableName) String tableName);!--ListUser getAllUser(Param(tableName) String tableName);--
select idgetAllUser resultTypeUserselect * from ${tableName}
/select同样表明也是不能加单引号的 添加功能获取自增的主键
场景模拟
t_clazz(clazz_id,clazz_name)——班级表
t_student(student_id,student_name,clazz_id)——学生表
1、添加班级信息
2、获取新添加的班级的id
3、为班级分配学生即将某学的班级id修改为新添加的班级的id
/**
* 添加用户信息
* param user
* return
* useGeneratedKeys设置使用自增的主键
* keyProperty因为增删改有统一的返回值是受影响的行数因此只能将获取的自增的主键放在传输的参数user对象的某个属性中将添加的自增的主键为实体类对象的某个属性赋值
*/
int insertUser(User user);!--int insertUser(User user);--
insert idinsertUser useGeneratedKeystrue keyPropertyidinsert into t_user values(null,#{username},#{password},#{age},#{sex})
/insert自定义映射resultMap
resultMap处理字段和属性的映射关系
若字段名和实体类中的属性名不一致则可以通过resultMap设置自定义映射比如数据表中的字段名的命名是是用下划线而Java中的属性名用的是驼峰的方式命名两者不能对应上
处理方法 1、为表中的每个字段起别名 2、当字段名符合mysql中的命名规则使用下划线而属性名符合Java的命名规则使用驼峰时可以在mybatis的配置文件中统一设置一个全局配置将下划线映射为驼峰。 3、使用ResultMap自定义映射处理
!--resultMap设置自定义映射属性id表示自定义映射的唯一标识type处理映射关系的实体类的类型子标签id设置主键的映射关系处理主键和属性的映射关系result设置普通字段的映射关系association设置多对一的映射关系collection设置一对多的映射关系属性property设置映射关系中实体类中的属性名column设置映射关系中表中的字段名
--
resultMap iduserMap typeUserid propertyid columnid/idresult propertyuserName columnuser_name/resultresult propertypassword columnpassword/resultresult propertyage columnage/resultresult propertysex columnsex/result
/resultMap
!--ListUser testMohu(Param(mohu) String mohu);--
select idtestMohu resultMapuserMap!--这里的resultMap和前面的resultMap id后面的值要一致--!--select * from t_user where username like %${mohu}%--
select id,user_name,password,age,sex from t_user where user_name likeconcat(%,#{mohu},%)
/select多对一映射处理
场景模拟
查询员工信息以及员工所对应的部门信息 对一对应的就是一个对象 对多对应的就是一个集合 级联方式处理映射关系
Emp实体类中有类型为Dept的属性dept但是我们想要返回的dept.*里有dept_id和dept_name这两个字段名并不能和Emp的dept属性进行映射他们应该和Emp中的dept属性的dept_id和dept_name映射
resultMap idempDeptMap typeEmpid columneid propertyeid/idresult columnename propertyename/resultresult columnage propertyage/resultresult columnsex propertysex/resultresult columndid propertydept.did/result!--和dept属性的dept_id和dept_name映射--result columndname propertydept.dname/result
/resultMap
!--Emp getEmpAndDeptByEid(Param(eid) int eid);--
select idgetEmpAndDeptByEid resultMapempDeptMapselect emp.*,dept.* from t_emp emp left join t_dept dept on emp.did dept.did where emp.eid #{eid}
/select使用association处理映射关系
resultMap idempDeptMap typeEmpid columneid propertyeid/idresult columnename propertyename/resultresult columnage propertyage/resultresult columnsex propertysex/resultassociation propertydept javaTypeDept!--对应的是前面的property中的属性对应的类型这里是别名--id columndid propertydid/idresult columndname propertydname/result/association
/resultMap
!--Emp getEmpAndDeptByEid(Param(eid) int eid);--
select idgetEmpAndDeptByEid resultMapempDeptMapselect emp.*,dept.* from t_emp emp left join t_dept dept on emp.did dept.did where emp.eid #{eid}
/select分步查询
①查询员工信息
/**
* 通过分步查询查询员工信息
* param eid
* return
*/
Emp getEmpByStep(Param(eid) int eid);resultMap idempDeptStepMap typeEmpid columneid propertyeid/idresult columnename propertyename/resultresult columnage propertyage/resultresult columnsex propertysex/result!--select设置分步查询查询某个属性的值的sql的标识namespace.sqlIdcolumn将sql以及查询结果中的某个字段设置为分步查询的条件--association propertydeptselectcom.atguigu.MyBatis.mapper.DeptMapper.getEmpDeptByStep columndid
/association
/resultMap
!--Emp getEmpByStep(Param(eid) int eid);--
select idgetEmpByStep resultMapempDeptStepMapselect * from t_emp where eid #{eid}
/select1、这里的association 后面的select跟的是调用的select语句所在的方法的全类名方法名可以通过——找到方法——点击右键——Copy Reference就可以获得全类名方法名 2、column中写的是条件因为在第二部查询时用到了一个参数而这个参数是通过第一部查询获得column中写的就是这个作为第二部查询的条件的字段 ②根据员工所对应的部门id查询部门信息
/**
* 分步查询的第二步 根据员工所对应的did查询部门信息
* param did
* return
*/
Dept getEmpDeptByStep(Param(did) int did);!--Dept getEmpDeptByStep(Param(did) int did);--
select idgetEmpDeptByStep resultTypeDept!--因为字段名和属性名一致--select * from t_dept where did #{did}
/select分步查询的优点可以实现延迟加载
但是必须在核心配置文件中设置全局配置信息
lazyLoadingEnabled延迟加载的全局开关。当开启时所有关联对象都会延迟加载
aggressiveLazyLoading当开启时任何方法的调用都会加载该对象的所有属性。否则每个属性会按需加载
此时就可以实现按需加载获取的数据是什么就只会执行相应的sql。此时可通过association和collection中的fetchType属性设置当前的分步查询是否使用延迟加载 fetchType“lazy(延迟加 载)|eager(立即加载)” fetchType在开启了延迟加载的环境中通过设置该属性设置当前分布查询是否使用延迟加载
一对多映射处理
比如在Dept这个类中就是多个员工对应一个部门
collection
collection设置一对多的映射关系
不再使用javaType因为前面有collection说明处理的是集合类型而要用ofType指明的是集合类型的属性中存储的数据的类型
/**
* 根据部门id查新部门以及部门中的员工信息
* param did
* return
*/
Dept getDeptEmpByDid(Param(did) int did);resultMap iddeptEmpMap typeDeptid propertydid columndid/idresult propertydname columndname/result!--ofType设置collection标签所处理的集合属性中存储数据的类型--collection propertyemps ofTypeEmpid propertyeid columneid/idresult propertyename columnename/resultresult propertyage columnage/resultresult propertysex columnsex/result/collection
/resultMap
!--Dept getDeptEmpByDid(Param(did) int did);--
select idgetDeptEmpByDid resultMapdeptEmpMapselect dept.*,emp.* from t_dept dept left join t_emp emp on dept.did emp.did where dept.did #{did}
/select分步查询
①查询部门信息
/**
* 分步查询部门和部门中的员工
* param did
* return
*/
Dept getDeptByStep(Param(did) int did);resultMap iddeptEmpStep typeDeptid propertydid columndid/idresult propertydname columndname/resultcollection propertyemps fetchTypeeagerselectcom.atguigu.MyBatis.mapper.EmpMapper.getEmpListByDid columndid/collection
/resultMap
!--Dept getDeptByStep(Param(did) int did);--
select idgetDeptByStep resultMapdeptEmpStepselect * from t_dept where did #{did}
/select②根据部门id查询部门中的所有员工
/**
* 根据部门id查询员工信息
* param did
* return
*/
ListEmp getEmpListByDid(Param(did) int did);!--ListEmp getEmpListByDid(Param(did) int did);--
select idgetEmpListByDid resultTypeEmpselect * from t_emp where did #{did}
/select动态SQL
Mybatis框架的动态SQL技术是一种根据特定条件动态拼装SQL语句的功能它存在的意义是为了
解决 拼接SQL语句字符串时的痛点问题。 可以通过判断这个字段是否是null或“”空字符串如果是这两个值说明没有设置对应的条件如果不是说明设置了条件 if
if标签可通过test属性的表达式进行判断若表达式的结果为true则标签中的内容会执行反之
标签中的内容不会执行
!--ListEmp getEmpListByCondition(Emp emp);--
select idgetEmpListByMoreTJ resultTypeEmpselect * from t_emp where 11if testename ! and ename ! nulland ename #{ename}/ifif testage ! and age ! nulland age #{age}/ifif testsex ! and sex ! nulland sex #{sex}/if
/select可以看出来我们设置的几个字段都不是null和空字符串所以应该将这几个条件拼接到select语句中 if 标签中的ename等属性不用#{}也不用${}就可以通过属性名得到 where
if中存在的问题 1、以上个例子为例如果在输入参数时将name设置为null那么在select语句中就会出现where后面跟了第二个if标签中的and但是这种select出现了语法错误。 2、如果这几个字段都是null或空字符串那select语句最后多出来一个where让然出现语法错误
解决方法一在where后面添加一个恒成立的条件 解决方法二where关键字
where和if一般结合使用
a若where标签中的if条件都不满足则where标签没有任何功能即不会添加where关键字
b若where标签中的if条件满足则where标签会自动添加where关键字并将条件最前方多余的and去掉但是内容后多余的end无法去掉
注意where标签不能去掉条件最后多余的and
select idgetEmpListByMoreTJ2 resultTypeEmpselect * from t_empwhereif testename ! and ename ! nullename #{ename}/ifif testage ! and age ! nulland age #{age}/ifif testsex ! and sex ! nulland sex #{sex}/if/where
/selecttrim
trim用于去掉或添加标签中的内容
常用属性
prefix在trim标签中的内容的前面添加某些内容
prefixOverrides在trim标签中的内容的前面去掉某些内容
suffix在trim标签中的内容的后面添加某些内容
suffixOverrides在trim标签中的内容的后面去掉某些内容
select idgetEmpListByMoreTJ resultTypeEmpselect * from t_emptrim prefixwhere suffixOverridesandif testename ! and ename ! nullename #{ename} and/ifif testage ! and age ! nullage #{age} and/ifif testsex ! and sex ! nullsex #{sex}/if/trim
/selectchoose、when、otherwise
choose、when、 otherwise相当于if…else if…else when最少设置一个otherwise最多设置一个 只要有一个成立后面的条件就不在判断
!--ListEmp getEmpListByChoose(Emp emp);--
select idgetEmpListByChoose resultTypeEmpselect include refidempColumns/include from t_empwherechoosewhen testename ! and ename ! nullename #{ename}/whenwhen testage ! and age ! nullage #{age}/whenwhen testsex ! and sex ! nullsex #{sex}/whenwhen testemail ! and email ! nullemail #{email}/when/choose/where
/selectforeach
collection设置要循环的数组或集合 item用字符串来表示数组或集合中的每一个数据 separator设置每次循环间数据之间的分隔符 注意如果前面没有添加Param那MyBatis是默认是这个参数是list !--int insertMoreEmp(ListEmp emps);--
insert idinsertMoreEmpinsert into t_emp valuesforeach collectionemps itememp separator,(null,#{emp.ename},#{emp.age},#{emp.sex},#{emp.email},null)/foreach
/insert
!--int deleteMoreByArray(int[] eids);--
delete iddeleteMoreByArraydelete from t_emp whereforeach collectioneids itemeid separatororeid #{eid}/foreach
/delete
!--int deleteMoreByArray(int[] eids);--
delete iddeleteMoreByArraydelete from t_emp where eid inforeach collectioneids itemeid separator, open( close)#{eid}/foreach
/delete如果参数是list或者数组MyBatis是默认将参数放在map中 如果前面没有添加Param那MyBatis是默认是这个参数是list如果是数组那默认是Array 所以简单起见前面最好都加上Param 注意批量添加和批量删除代码中括号和foreach的位置 不加open和close的批量删除代码
前面的代码就是因为一个item是一个对象而我们要去的是对象中的属性所以写成#{emp.ename}但是后面的代码只是将id取出来每一个item就是id值所以直接写成#{eid}
open以……开始 close以……结束
另外一种方式
SQL片段
sql片段可以记录一段公共sql片段在使用的地方通过include标签进行引入
sql idempColumnseid,ename,age,sex,did
/sql
select include refidempColumns/include from t_empMyBatis的缓存
MyBatis的一级缓存默认是开启的
一级缓存是SqlSession级别的通过同一个SqlSession查询的数据会被缓存下次查询相同的数据就会从缓存中直接获取不会从数据库重新访问
使一级缓存失效的四种情况
1、不同的SqlSession对应不同的一级缓存
2、同一个SqlSession但是查询条件不同
3、同一个SqlSession两次查询期间执行了任何一次增删改操作
4、同一个SqlSession两次查询期间手动清空了缓存
创建了两个sql语句完全相同的sqlSession 执行结果执行了2次sql语句第二次不是从缓存中获得数据
MyBatis的二级缓存
二级缓存是SqlSessionFactory级别通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存此后若再次执行相同的查询语句结果就会从缓存中获取
二级缓存开启的条件
a在核心配置文件中设置全局配置属性cacheEnabled“true”默认为true不需要设置
b在映射文件中设置标签 cache/
c二级缓存必须在SqlSession关闭或提交之后有效
d查询的数据所转换的实体类类型必须实现序列化的接口
使二级缓存失效的情况
两次查询之间执行了任意的增删改会使一级和二级缓存同时失效
二级缓存的相关配置
在mapper配置文件中添加的cache标签可以设置一些属性
①eviction属性缓存回收策略默认的是 LRU。
LRULeast Recently Used – 最近最少使用的移除最长时间不被使用的对象。
FIFOFirst in First out – 先进先出按对象进入缓存的顺序来移除它们。
SOFT – 软引用移除基于垃圾回收器状态和软引用规则的对象。
WEAK – 弱引用更积极地移除基于垃圾收集器状态和弱引用规则的对象。
②flushInterval属性刷新间隔单位毫秒默认情况是不设置也就是没有刷新间隔缓存仅仅调用语句时刷新
③size属性引用数目正整数代表缓存最多可以存储多少个对象太大容易导致内存溢出
④readOnly属性只读 true/false
true只读缓存会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了 很重要的性能优势。
false读写缓存会返回缓存对象的拷贝通过序列化。这会慢一些但是安全因此默认是 false。(如果修改的话修改的是拷贝过的对象不会修改原始数据)
MyBatis缓存查询的顺序
先查询二级缓存因为二级缓存中可能会有其他程序已经查出来的数据可以拿来直接使用。
如果二级缓存没有命中再查询一级缓存
如果一级缓存也没有命中则查询数据库
SqlSession关闭之后一级缓存中的数据会写入二级缓存所以二级缓存中不一定包含所有一级缓存的
整合第三方缓存EHCache了解 第三方缓存是针对二级缓存的 添加依赖
!-- Mybatis EHCache整合包 --
dependencygroupIdorg.mybatis.caches/groupIdartifactIdmybatis-ehcache/artifactIdversion1.2.1/version
/dependency
!-- slf4j日志门面的一个具体实现 --
dependencygroupIdch.qos.logback/groupIdartifactIdlogback-classic/artifactIdversion1.2.3/version
/dependency各jar包功能 下面两个说白了前面那个是借口后面那个是前面的实现类
创建EHCache的配置文件ehcache.xml
?xml version1.0 encodingutf-8 ?
ehcache xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance
xsi:noNamespaceSchemaLocation../config/ehcache.xsd!-- 磁盘保存路径 --diskStore pathD:\atguigu\ehcache/defaultCachemaxElementsInMemory1000maxElementsOnDisk10000000eternalfalseoverflowToDisktruetimeToIdleSeconds120timeToLiveSeconds120diskExpiryThreadIntervalSeconds120memoryStoreEvictionPolicyLRU/defaultCache
/ehcache设置二级缓存的类型
cache typeorg.mybatis.caches.ehcache.EhcacheCache/加入logback日志
存在SLF4J时作为简易日志的log4j将失效此时我们需要借助SLF4J的具体实现logback来打印日志。 创建logback的配置文件logback.xml名字不能改
?xml version1.0 encodingUTF-8?
configuration debugtrue!-- 指定日志输出的位置 --appender nameSTDOUT classch.qos.logback.core.ConsoleAppenderencoder!-- 日志输出的格式 --!-- 按照顺序分别是 时间、日志级别、线程名称、打印日志的类、日志主体内容、换行--pattern[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger][%msg]%n/pattern/encoder/appender!-- 设置全局日志级别。日志级别按顺序分别是 DEBUG、INFO、WARN、ERROR --!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 --root levelDEBUG!-- 指定打印日志的appender这里通过“STDOUT”引用了前面配置的appender --appender-ref refSTDOUT //root!-- 根据特殊需求指定局部日志级别 --logger namecom.atguigu.crowd.mapper levelDEBUG/
/configurationEHCache配置文件说明 MyBatis的逆向工程
正向工程先创建Java实体类由框架负责根据实体类生成数据库表。 Hibernate是支持正向工程的。
逆向工程先创建数据库表由框架负责根据数据库表反向生成如下资源
Java实体类 Mapper接口 Mapper映射文件
创建逆向工程的步骤
①添加依赖和插件
!-- 依赖MyBatis核心包 --
dependenciesdependencygroupIdorg.mybatis/groupIdartifactIdmybatis/artifactIdversion3.5.7/version/dependency!-- junit测试 --dependencygroupIdjunit/groupIdartifactIdjunit/artifactIdversion4.12/versionscopetest/scope/dependency!-- log4j日志 --dependencygroupIdlog4j/groupIdartifactIdlog4j/artifactIdversion1.2.17/version/dependencydependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdversion8.0.16/version/dependency
/dependencies
!-- 控制Maven在构建过程中相关配置 --
build!-- 构建过程中用到的插件 --plugins!-- 具体插件逆向工程的操作是以构建过程中插件形式出现的 --plugingroupIdorg.mybatis.generator/groupIdartifactIdmybatis-generator-maven-plugin/artifactIdversion1.3.0/version!-- 插件的依赖 --dependencies!-- 逆向工程的核心依赖 --dependencygroupIdorg.mybatis.generator/groupIdartifactIdmybatis-generator-core/artifactIdversion1.3.2/version/dependency!-- MySQL驱动 --dependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdversion8.0.16/version/dependency/dependencies/plugin/plugins
/build②创建MyBatis的核心配置文件 ③创建逆向工程的配置文件 文件名必须是generatorConfig.xml
?xml version1.0 encodingUTF-8?
!DOCTYPE generatorConfigurationPUBLIC -//mybatis.org//DTD MyBatis Generator Configuration 1.0//ENhttp://mybatis.org/dtd/mybatis-generator-config_1_0.dtd
generatorConfiguration!--targetRuntime: 执行生成的逆向工程的版本MyBatis3Simple: 生成基本的CRUD清新简洁版MyBatis3: 生成带条件的CRUD奢华尊享版--context idDB2Tables targetRuntimeMyBatis3!-- 数据库的连接信息 --jdbcConnection driverClasscom.mysql.cj.jdbc.DriverconnectionURLjdbc:mysql://localhost:3306/mybatis?serverTimezoneUTCuserIdrootpassword123456/jdbcConnection!-- javaBean的生成策略实体类的生成策略--javaModelGenerator targetPackagecom.atguigu.mybatis.pojotargetProject.\src\main\javaproperty nameenableSubPackages valuetrue /!--是否使用子包如果设置为false前面的点不会被解释成一层层目录--property nametrimStrings valuetrue /!--会将表中的字段的名字的前后空格去掉来生成实体类中的属性--/javaModelGenerator!-- SQL映射文件的生成策略 --sqlMapGenerator targetPackagecom.atguigu.mybatis.mappertargetProject.\src\main\resourcesproperty nameenableSubPackages valuetrue //sqlMapGenerator!-- Mapper接口的生成策略 --javaClientGenerator typeXMLMAPPERtargetPackagecom.atguigu.mybatis.mapper targetProject.\src\main\javaproperty nameenableSubPackages valuetrue //javaClientGenerator!-- 逆向分析的表 --!-- tableName设置为*号可以对应所有表此时不写domainObjectName --!-- domainObjectName属性指定生成出来的实体类的类名设置之后mapper接口和映射文件都会根据他来起名 --table tableNamet_emp domainObjectNameEmp/table tableNamet_dept domainObjectNameDept//context
/generatorConfiguration④执行MBG插件的generate目标 效果 如果逆向工程要重新生成的话要把上一次生成的东西删除 QBC查询
Test
public void testMBG(){try {InputStream is Resources.getResourceAsStream(mybatis-config.xml);SqlSessionFactory sqlSessionFactory newSqlSessionFactoryBuilder().build(is);SqlSession sqlSession sqlSessionFactory.openSession(true);EmpMapper mapper sqlSession.getMapper(EmpMapper.class);//查询所有数据//这里不用创建Example是因为没有用到条件/*ListEmp list mapper.selectByExample(null);list.forEach(emp - System.out.println(emp));*///根据条件查询/*EmpExample example new EmpExample();创建一个条件对象Criteria后面可以直接写条件example.createCriteria().andEmpNameEqualTo(张三).andAgeGreaterThanOrEqualTo(20);!--example.or().后面的条件和上个调用and的那些条件之间是or的关系--example.or().andDidIsNotNull();ListEmp list mapper.selectByExample(example);list.forEach(emp - System.out.println(emp));*///选择修改修改mapper.updateByPrimaryKeySelective(newEmp(1,admin,22,null,456qq.com,3));} catch (IOException e) {e.printStackTrace();}
}普通和选择之间的区别如果是普通的条件中有属性的值为null或没有设置相关的属性那在数据表中会将相应的字段值设置为null但是选择不会修改未设置值或值为null的字段。 分页插件
SQL语句中实现分页
limit index,pageSize
pageSize每页显示的条数
pageNum当前页的页码
index当前页的起始索引index(pageNum-1)*pageSize
count总记录数
totalPage总页数
totalPage count / pageSize;
if(count % pageSize ! 0){
totalPage 1;
}
pageSize4pageNum1index0 limit 0,4
pageSize4pageNum3index8 limit 8,4
pageSize4pageNum6index20 limit 8,4 以最后的语句为例此时pageNum是6说明当前页是6前面一共有5页又因为pageSize是4说明前面的5页一共有20条数据但是第一条数据的索引是0所以当前的第6页的第一条数据的索引是20. 分页插件的使用步骤
①添加依赖
dependencygroupIdcom.github.pagehelper/groupIdartifactIdpagehelper/artifactIdversion5.2.0/version
/dependency②配置分页插件 在MyBatis的核心配置文件中配置插件
?xml version1.0 encodingUTF-8 ?
!DOCTYPE configurationPUBLIC -//mybatis.org//DTD Config 3.0//ENhttp://mybatis.org/dtd/mybatis-3-config.dtdconfigurationproperties resourcejdbc.properties/propertiestypeAliasespackage namecom.atguigu.mybatis.pojo//typeAliasesplugins!-- 设置分页插件 --plugin interceptorcom.github.pagehelper.PageInterceptor/plugin/pluginsenvironments defaultdevelopmentenvironment iddevelopmenttransactionManager typeJDBC/transactionManagerdataSource typePOOLEDproperty namedriver value${jdbc.driver}/property nameurl value${jdbc.url}/property nameusername value${jdbc.user}/property namepassword value${jdbc.password}//dataSource/environment/environmentsmapperspackage namecom.atguigu.mybatis.mapper//mappers
/configuration分页插件的使用
Test
public void testPage() {SqlSession sqlSession SqlSessionUtil.getSqlSession();DynamicMapperSQLMapper mapper sqlSession.getMapper(DynamicMapperSQLMapper.class);// 查询功能之前开启分页, 第1页, 每页显示4条, 并且获取部分分页的信息// Page{counttrue, pageNum2, pageSize4, startRow4, endRow8, total0, pages0, reasonablenull, pageSizeZeronull}[]PageObject page PageHelper.startPage(2, 4);System.out.println(page);// 查询ListEmp emps mapper.getEmpByCondition(null);//输出分页信息// Page{counttrue, pageNum2, pageSize4, startRow4, endRow8, total25, pages7, reasonablefalse, pageSizeZerofalse}[Emp{empId5, empName刘三, age22, gender男}, .....]System.out.println(page);// 输出数据emps.forEach(System.out::println);// 生成导航页码信息, 更丰富页面信息PageInfoEmp pageInfo new PageInfo(emps, 5);sqlSession.close();
}a在查询功能之前使用PageHelper.startPage(int pageNum, int pageSize)开启分页功能
pageNum当前页的页码
pageSize每页显示的条数
b在查询获取list集合之后使用PageInfo pageInfo new PageInfo(List list, int
navigatePages)获取分页相关数据
list分页之后的数据
navigatePages导航分页的页码数 比如在例子中navigatePages设置的值为5说明下方的导航页的起始页码是当前页-2末尾页码是当前页2 c分页相关数据
PageInfo{
pageNum8, pageSize4, size2, startRow29, endRow30, total30, pages8,
//PageInfo中其实也有Page对象 listPage{counttrue, pageNum8, pageSize4, startRow28, endRow32, total30,
pages8, reasonablefalse, pageSizeZerofalse},
prePage7, nextPage0, isFirstPagefalse, isLastPagetrue, hasPreviousPagetrue,
hasNextPagefalse, navigatePages5, navigateFirstPage4, navigateLastPage8,
navigatepageNums[4, 5, 6, 7, 8]
}
pageNum当前页的页码
pageSize每页显示的条数
size当前页显示的真实条数
total总记录数
pages总页数
prePage上一页的页码
nextPage下一页的页码
isFirstPage/isLastPage是否为第一页/最后一页
hasPreviousPage/hasNextPage是否存在上一页/下一页
navigatePages导航分页的页码数
navigatepageNums导航分页的页码[1,2,3,4,5]