- 框架
- mybatis概述
- MVC
- ORM
- mybatis架构
- mybatis搭建使用
- 参数传递
- @Param('acc')传参
- Map键/值串传参
- 结果返回
- 简单类型输出映射
- POJO对象输出映射
- 关联查询resultMap
- 主键返回
- 注解方式
- 添加log4j日志组件
- 单元测试
- 懒加载
- 动态sql
- where
- trim
- choose
- set
- foreach
- 特殊符号
- 缓存
- 一级缓存
- 二级缓存
框架会将很多基本功能进行封装,程序员在框架基础上再进行业务开发
作用:简洁代码,高效开发
mybatis概述 原apache的一个开源项目,2010迁移到谷歌,更名为mybatis
特点
它是一款半自动的ORM持久层框架,具有较高的SQL灵活性,支持高级映射(一对一,一对多),动态SQL,延迟加载和缓存等特性,但它的数据库无关性较低
MVC数据持久层(数据访问层 / M层):DAO,model
数据控制层(C层 control):servlet
前端(View)
ORMObject Relation Mapping,对象关系映射。对象指的是Java对象,关系指的是数据库中的关系模型
对象关系映射,指的就是在Java对象和数据库的关系模型之间建立一种对应关系,类中的属性和表中的列一一对应。
半自动ORM框架优点
用mybatis进行开发,需要手动编写SQL语句。而全自动的ORM框架,如hibernate,则不需要编写SQL语句。用hibernate开发,只需要定义好ORM映射关系,就可以直接进行CRUD操作了。由于mybatis需要手写SQL语句,所以它有较高的灵活性,可以根据需要,自由地对SQL进行定制,也因为要手写SQL,当要切换数据库时,SQL语句可能就要重写,因为不同的数据库有不同的方言(Dialect),所以mybatis的数据库无关性低。虽然mybatis需要手写SQL,但相比JDBC,它提供了输入映射和输出映射,可以很方便地进行SQL参数设置,以及结果集封装。并且还提供了关联查询和动态SQL等功能,极大地提升了开发的效率。并且它的学习成本也比hibernate低很多
mybatis架构 mybatis搭建使用导入MyBatis使用的jar包
org.mybatis
mybatis
3.4.2
mysql
mysql-connector-java
8.0.16
创建 MyBatis 全局配置文件
在resources目录下创建mybatis-config.xml
在mapper目录下创建对应的mapper接口并定义方法
package com.ff.mybatisPro.mapper;
import com.ff.mybatisPro.model.AdminUser;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
public interface AdminMapper {
void saveAdmin(AdminUser adminUser);
void updateAdmin(AdminUser adminUser);
void deleteAdmin(int id);
AdminUser selectAdminById(int id);
}
在resources.mapper目录下创建对应的mapper.xml文件
基本的增删改查
insert into admin(account, password, sex) values (#{account}, #{password}, #{sex}) update admin set account=#{account}, password=#{password}, sex=#{sex} where id = #{id} delete from admin where id = #{id}
在mybatis-config.xml核心配置文件中添加mapper映射
在util包下封装sqlSession创建过程
package com.ff.mybatisPro.util;
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.Reader;
public class MybatisUtil {
static SqlSessionFactory sessionFactory = null;
//静态代码块,在类加载时执行,只执行一次
static {
Reader reader = null;
try {
//读取mybatis核心配置文件
reader = Resources.getResourceAsReader("mybatis-config.xml");
} catch (IOException e) {
e.printStackTrace();
}
sessionFactory = new SqlSessionFactoryBuilder().build(reader);
}
public static SqlSession openSession(){
return sessionFactory.openSession();
}
}
在测试中调用方法
@Test
public void saveAdmin(){
AdminUser adminUser = new AdminUser();
adminUser.setAccount("admin2");
adminUser.setPassword("000");
adminUser.setSex("男");
SqlSession sqlSession = MybatisUtil.openSession();
//获得映射接口代理对象
AdminMapper mapper = sqlSession.getMapper(AdminMapper.class);
//使用代理对象调用接口中的方法,本质上并不是调用接口中的方法,实际调用的是与接口中方法名相同的xml文件中对应的某个sql
mapper.saveAdmin(adminUser);
//输出刚插入数据的id:在AdminMapper.xml中加入useGeneratedKeys="true" keyColumn="id" keyProperty="id"属性
System.out.println(adminUser.getId());
sqlSession.commit();
sqlSession.close();
}
@Test
public void updateAdmin(){
AdminUser adminUser = new AdminUser();
adminUser.setId(1);
adminUser.setAccount("admin2");
adminUser.setPassword("000");
adminUser.setSex("男");
SqlSession sqlSession = MybatisUtil.openSession();
AdminMapper mapper = sqlSession.getMapper(AdminMapper.class);
mapper.updateAdmin(adminUser);
sqlSession.commit();
sqlSession.close();
}
@Test
public void deleteAdmin(){
SqlSession sqlSession = MybatisUtil.openSession();
AdminMapper mapper = sqlSession.getMapper(AdminMapper.class);
mapper.deleteAdmin(1);
sqlSession.commit();
sqlSession.close();
}
@Test
public void selectAdmin(){
SqlSession sqlSession = MybatisUtil.openSession();
AdminMapper mapper = sqlSession.getMapper(AdminMapper.class);
//调用默认无参构造方法
AdminUser adminUser = mapper.selectAdminById(5);
System.out.println(adminUser);
sqlSession.commit();
sqlSession.close();
}
参数传递
传单个参数一般用parameterType,要指定参数就要使用@Param(‘acc’)形式
@Param(‘acc’)传参
AdminUser selectAdminByAccount(@Param("acc") String account, @Param("pwd") String password);
@Test
public void selectAdminByAccount(){
SqlSession sqlSession = MybatisUtil.openSession();
AdminMapper mapper = sqlSession.getMapper(AdminMapper.class);
AdminUser adminUser = mapper.selectAdminByAccount("admin","000");
System.out.println(adminUser);
sqlSession.commit();
sqlSession.close();
}
Map键/值串传参
AdminUser getAdmin(Map map);
@Test
public void getAdmin(){
SqlSession sqlSession = MybatisUtil.openSession();
AdminMapper mapper = sqlSession.getMapper(AdminMapper.class);
Map map = new HashMap<>();
map.put("acc","admin");
map.put("pwd","000");
AdminUser adminUser = mapper.getAdmin(map);
System.out.println(adminUser);
sqlSession.commit();
sqlSession.close();
}
结果返回
简单类型输出映射
int countAdmin();
POJO对象输出映射
resultType=“AdminUser”:返回值的类型 根据返回值的类型,创建一个对象或List 将数据库数据自动映射到java对象中,表中的类名与类中的属性名相同
ListadminList();
关联查询resultMap
在关联查询的时候,一个对象中需要存放其他类的属性,我们通常直接把其他类添加到该类属性中,如下把部门关联的员工集合和操作人都添加为部门的属性
public class Dept {
private int id;
private String name;
private List employees;
private AdminUser adminUser;
}
之后在查询语句中,resultMap中就可以去设置属性字段的映射
而封装的其他对象个体就会创建一个association标签配置该类的映射
对象集合会创建一个collection标签配置映射
主键返回
在插入一条记录后,得到数据库自动生成的这条记录的主键id
insert into admin(account, password, sex)
values (#{account}, #{password}, #{sex})
注解方式
也可以通过注解的方式编写简单的sql
@Select("select id,name from dept")
List getName();
@Test
public void getDeptList1(){
SqlSession sqlSession = MybatisUtil.openSession();
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
List deptName = mapper.getName();
System.out.println(deptName);
}
添加log4j日志组件
导入jar坐标
log4j
log4j
1.2.17
在resources下导入log4j.properties文件
log4j.rootLogger = debug,stdout,D
#System out Console
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%p] %d{yyyy-MM-dd HH:mm:ss,SSS} %m%n
#System out File
log4j.appender.D = org.apache.log4j.FileAppender
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = F://java project/logs/log.log
log4j.appender.D.Append = true
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] -[%l] %m%n
在mybatis-config.xml中添加设置
单元测试
导入jar包
junit
junit
4.12
provided
懒加载
对于关联查询,若不采用延迟加载策略,而是一次性将关联的从信息都查询出来,则在主信息比较多的情况下,会产生N+1问题,导致性能降低。比如在查询员工信息时,设置了关联查询部门信息,如不采用延迟加载策略,查询员工信息时就会把其部门信息全部查询出来,但我们却用不到,就会造成浪费。而懒加载就会让在需要部门信息的时候才执行查询部门的sql
@Test
public void getEmpById1(){
SqlSession sqlSession = MybatisUtil.openSession();
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
Employee employee = employeeMapper.getEmpById1(1);
System.out.println(employee.getName());
System.out.println(employee.getDept().getName());
System.out.println(employee.getAdminUser().getAccount());
sqlSession.commit();
sqlSession.close();
}
mybatis-config,xml中设置属性:
Employee getEmpById1(int id);
动态sql where
当需要如上筛选查询时,返回的都是员工列,所以servlet中只用写一套方法,让在其sql中动态添加条件就行
- name和age在第一次加载页面时传向servlet的值为null,在空值查询时为 ’ ’
- where 可以根据里边的if是否有成立, 动态添加where关键字 会自动删除and,or等开头的关键字
public class EmployeeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter out = null;
resp.setContentType("text/html;charset=utf-8");
try {
out = resp.getWriter();
//接收姓名年龄条件瓶存储到一个员工对象中
String name = req.getParameter("name");
String age = req.getParameter("age");
Employee employee = new Employee();
employee.setName(name);
//age使用parseInt时需要判断 (第一次查询age为null,筛选查询时为'')
if (age!=null && !age.equals("")){
employee.setAge(Integer.parseInt(age));
}
EmployeeService employeeService = new EmployeeService();
List empList = employeeService.getEmpList(employee);
out.println(new Gson().toJson(empList));
} catch (Exception e) {
e.printStackTrace();
out.println(500);
}
}
}
public class EmployeeService {
public List getEmpList(Employee employee){
SqlSession sqlSession = MybatisUtil.openSession();
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
List employees = employeeMapper.getEmpList(employee);
sqlSession.commit();
sqlSession.close();
return employees;
}
}
public interface EmployeeMapper {
List getEmpList(Employee employee);
}
SELECT emp.id,emp.name ename,emp.age,d.name dname,a.account
FROM employee emp LEFT JOIN dept d ON emp.deptId=d.id
LEFT JOIN admin a ON emp.adminId=a.id
emp.name=#{name}
and emp.age=#{age}
trim
动态修改
trim prefix=“where” prefixOverrides=“and||or”
- 当if有成立条件时,添加一个指定的前缀prefix (where)
- 如果以prefixOverrides开头,可以覆盖掉关键字(and,or)
SELECT emp.id,emp.name ename,emp.age,d.name dname,a.account FROM employee emp LEFT JOIN dept d ON emp.deptId=d.id LEFT JOIN admin a ON emp.adminId=a.id emp.name=#{name} and emp.age=#{age}
动态删除
trim prefix=“set” suffixOverrides="," suffix=“where”
- 当有if成立时,添加前缀set,后缀where
- 如果以","结尾,直接删除
update employee
name=#{name},
age=#{age},
deptId=#{dept.id},
id=#{id}
choose
类似if,可用otherwise
setSELECT emp.id,emp.name ename,emp.age,d.name dname,a.account FROM employee emp LEFT JOIN dept d ON emp.deptId=d.id LEFT JOIN admin a ON emp.adminId=a.id and emp.name=#{name} and emp.name='abc' and emp.age=#{age}
动态添加set关键字,删掉最后一个逗号
动态修改:传入的为空不改,不为空就修改
@Test
public void updateEmp(){
Employee employee1 = new Employee();
employee1.setId(1);
employee1.setName("abc");
employee1.setAge(20);
Dept dept = new Dept();
dept.setId(2);
employee1.setDept(dept);
SqlSession sqlSession = MybatisUtil.openSession();
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
employeeMapper.updateEmp(employee1);
sqlSession.commit();
sqlSession.close();
}
public interface EmployeeMapper {
void updateEmp(Employee employee);
}
update employee
name=#{name},
age=#{age},
deptId=#{dept.id},
where id=#{id}
foreach
通过多个age查询时,就要对List中的元素进行遍历
在sql语句中是这样写的:
SELECT * FROM employee WHERe age IN(19,20)
所以使用foreach遍历时,就可以设定前缀、后缀和分隔符
- item 表示集合中每一个元素进行迭代时的别名
- index 指定一个名字,用于表示在迭代过程中,每次迭代到的位置
- open 表示该语句以什么开始
- separator 表示在每次进行迭代之间以什么符号作为分隔符
- close 表示以什么结束
- collection 指定传来的类型(array、list …)
@Test
public void getEmpByAges(){
List ageList = new ArrayList<>();
ageList.add(18);
ageList.add(19);
SqlSession sqlSession = MybatisUtil.openSession();
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
List employees = employeeMapper.getEmpByAges(ageList);
System.out.println(employees);
sqlSession.commit();
sqlSession.close();
}
public interface EmployeeMapper {
List getEmpByAges(List ages);
}
select * from employee
where age in
#{age}
特殊符号
使用案例:通过年龄段查询
@Test
public void getEmpByAgeInterval(){
SqlSession sqlSession = MybatisUtil.openSession();
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
List employees = employeeMapper.getEmpByAgeInterval(18,20);
System.out.println(employees);
sqlSession.commit();
sqlSession.close();
}
public interface EmployeeMapper {
List getEmpByAgeInterval(@Param("min")int min,@Param("max")int max);
}
select * from employee
where age ">> #{min} and age < #{max}
缓存
- 缓存(cache)的作用是为了减去数据库的压力,提高数据库的性能。
- 缓存实现的原理是从数据库中查询出来的对象在使用完后不要销毁,而是存储在内存(缓存)中,当再次需要获取该对象时,直接从内存(缓存)中直接获取,不再向数据库执行 select 语句,从而减少了对数据库的查询次数,因此提高了数据库的性能。
- 缓存是使用 Map 集合缓存数据的
一级缓存的作用域是同一个 SqlSession,在同一个 sqlSession 中两次执行相同的 sql 语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查 询,从而提高查询效率。当一个 sqlSession 结束后该 sqlSession 中的一级缓存也就不存在了。Mybatis 默认开启一级缓存
生命周期:
- MyBatis 在开启一个数据库会话时,会创建一个新的 SqlSession 对象,SqlSession 对象中会有一个新的 Executor 对象。Executor 对象中持有一个新的 PerpetualCache 对象,如果 SqlSession 调用了 **close()**方法,会释放掉一级缓存 PerpetualCache 对象,一级缓存将不可用。
- 如果 SqlSession 调用了 clearCache(),会清空 PerpetualCache 对象中的数据,但是该对象仍可使用。
- SqlSession 中执行了任何一个 update 操作(update()、delete()、insert()) ,都会清空 PerpetualCache 对象的数据,但是该对象可以继续使用
一级缓存时执行commit,close,增删改等操作,就会清空当前的一级缓存;当对SqlSession执行更新操作(update、delete、insert)后并执行commit时,不仅清空其自身的一级缓存(执行更新操作的效果),也清空二级缓存(执行commit()的效果)
二级缓存 二级缓存是 SqlSessionFactory 级别的,根据 mapper 的 namespace 划分区域的,相同 namespace 的 mapper 查询的数据缓存在同一个区域,如果使用mapper 代理方法每个 mapper 的 namespace 都不同,此时可以理解为二级缓存区域是根据 mapper 划分
每次查询会先从缓存区域查找,如果找不到则从数据库查询,并将查询到数据写入缓存。Mybatis 内部存储缓存使用一个 HashMap,key 为hashCode+sqlId+Sql 语句。value 为从查询出来映射生成的 java 对象。
sqlSession 执行 insert、update、delete 等操作 commit 提交后会清空缓存区域,防止脏读
配置:
-
开启全局二级缓存配置,在mybatis-config.xml下加设置
-
POJO 序列化 :将所有的 POJO ()类实现序列化接口 Java.io. Serializable
-
配置映射文件
在 Mapper 映射文件中添加 cache,表示此 mapper 开启二级缓存。
当 SqlSeesion 关闭时,会将数据存入到二级缓存
-
禁用缓存
如测试sql语句性能时缓存会影响测试准确性 需要禁用在映射文件中:默认值是true useCache=”false” -
刷新缓存
在映射文件中:属性:flushCache=”true”刷新缓存,在查询语句中,默认值是false,在新增删除修改语句中,默认值是true(清空缓存)



