MyBatis
-
半自动化的持久层框架
-
半自动化
- 还需要程序员手动编写sql语句
-
持久层框架
- 对JDBC的封装
-
1.1 HelloWorld
-
创建maven模块(java类型)
-
导入jar包
- mybatis
- mysql驱动
- junit
-
准备一个数据库和表格
-
创建实体类
-
创建EmployeeDao的接口
-
创建EmployeeDao的实现类,被映射文件替代
- 创建EmployeeDao的映射文件(xml配置文件)
-
创建MyBatis的核心配置文件
- 设置环境(数据库连接信息)
- 加载映射文件
-
测试
- 获取到持久层对象
- 调用方法测试即可
1.2添加日志记录
-
目的是为了查看sql语句、参数和结果
-
添加jar包
- log4j
-
添加log4j.xml配置文件
2.MyBatis的全局配置文件
2.1 properties
<!--
1. properties (后期会被Spring代替)
功能:引入外部属性文件
-->
<properties resource="db.properties">
<property name="password" value="123456"/>
</properties>
2.2 settings
<!--
2. settings
对MyBatis的整体设置
cacheEnabled 是否开启二级缓存
lazyLoadingEnabled 是否开启延迟加载
aggressiveLazyLoading 按需加载
mapUnderscoreToCamelCase 是否开启自动驼峰
...
-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
2.3 typeAliases
<!--
3.typeAliases 起别名
3.1 自带的类型,都有对应的别名
基本上就是类名首字母小写即可
3.2 自定义的类型
-->
<typeAliases>
<!--方式一:单个类起别名-->
<!-- <typeAlias type="com.atguigu.bean.Employee" alias="emp"/>-->
<!--方式二:设置包名 ★-->
<package name="com.atguigu.bean"/>
</typeAliases>
2.4 typeHandlers
<!--
4. typeHandlers
类型处理器,处理java和数据库数据类型的转换
目前:不用显示的声明
-->
<!--<typeHandlers>
<typeHandler handler=""
</typeHandlers>-->
2.5 plugins
<!--
5. plugins
添加插件:分页插件
-->
<!--<plugins>
<plugin interceptor=""></plugin>
</plugins>-->
2.6 environments
<!--
6.environments (后期交给Spring)
default属性:选择使用哪个环境
-->
<environments default="development">
<environment id="development">
<!--事务管理-->
<transactionManager type="JDBC"/>
<!--数据源-->
<dataSource type="POOLED">
<property name="driver" value="${driverClassName}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driverClassName}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
2.7 databaseIdProvider
<!--
7. databaseIdProvider
为不同的数据库起个别名,映射文件里就可以写多套标签
type:指定类型
-->
<databaseIdProvider type="DB_VENDOR">
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
</databaseIdProvider>
2.8 mappers
<!--
8.加载映射文件 (Spring加载)
-->
<mappers>
<!--第一种方式:通过映射文件的路径-->
<!-- <mapper resource="EmployeeDao.xml"/>-->
<!--第二种方式:通过接口的全类名
要求:接口和映射文件同包同名
-->
<!-- <mapper class="com.atguigu.dao.EmployeeDao"/>-->
<!--第三种方式:通过包名设置 ★
设置接口的包名
要求:接口和映射文件同包同名
-->
<package name="com.atguigu.dao"/>
</mappers>
3.MyBatis的映射文件
3.1 select标签
<!--实现接口中的抽象方法-->
<!--Employee getById(Integer id);
由于是查询,所以使用select标签
id属性:指定实现方法的方法名
parameterType属性: 参数类型 (MyBatis可以自行推断参数类型,所以可以省略)
resultType属性: 自动映射: 处理结果类型(如果是自定义的,需要全类名)
resultMap属性: 自定义映射(后面讲)
flushCache属性: 刷新缓存(后面讲)
useCache属性: 是否使用缓存(后面讲)
databaseId属性 设置数据库的厂商标识
标签体内编写sql语句即可
-->
<select id="getById" parameterType="int" resultType="employee" databaseId="oracle">
select * from employees where id=#{abc}
</select>
3.2 insert/update/delete标签
<!--
insert/update/delete
id属性: 方法名
parameterType属性: 参数类型(可以省略)
flushCache属性: 刷新缓存(后面讲)
useGeneratedKeys属性: 自动返回自增主键
keyProperty属性: 设置自增主键到参数的哪个属性
keyColumn属性: 如果主键不是第一列,设置列名
-->
<insert id="insert" parameterType="com.atguigu.bean.Employee" useGeneratedKeys="true" keyProperty="id">
insert into employees values(null,#{lastName},#{email},#{gender},#{salary},#{deptId})
</insert>
<!-- Oracle自动返回自增主键 -->
<insert id="addEmployee" databaseId="oracle">
<selectKey order="BEFORE" keyProperty="id" resultType="integer">
select employee_seq.nextval from dual
</selectKey>
insert into oracle_employees(id,last_name,email,salary,dept_id)
values(#{id},#{lastName},#{email},#{salary},#{deptId})
</insert>
<insert id="addEmployee" databaseId="oracle">
<selectKey order="AFTER" keyProperty="id" resultType="integer">
select employee_seq.currval from dual
</selectKey>
insert into oracle_employees(id,last_name,email,salary,dept_id)
values(employee_seq.nextval,#{lastName},#{email},#{salary},#{deptId})
</insert>
3.3 参数的传递方式
-
单个普通参数
- #{任意名字}
-
多个普通参数
-
<!--List<Employee> findEmployeeByNameAndSalary(String name,Double salary); 原理:将参数存放到Map集合内,一个参数放两次 arg0-n param1-n --> <select id="findEmployeeByNameAndSalary" resultType="employee"> select * from employees where last_name like concat('%',#{arg0},'%') and salary > #{param2} </select>
-
-
命名参数
-
/** * 命名参数 * 可以通过自定义的名字去取值 * 原理:替换掉原来的arg系列,param系列不影响的 */ List<Employee> findEmployeeByNameAndSalary(@Param("name") String name,@Param("salary") Double salary);
-
-
javaBean
- #{属性名}
-
Map集合
- #{key值}
-
/** * Map集合 * 映射文件通过key取值,并且没有arg或param相关的内容 */ List<Employee> findEmployeeByMap(Map map);
4.MyBatis缓存机制
- 后期大概率采用redis(非关系型数据库)作为缓存框架
4.1 本地(一级)缓存
-
默认开启,也关不掉
-
sqlSession级别的缓存
-
本地缓存失效的情况
- 不同的sqlSession对象
- 查询条件不同
- 两次查询中间做了增删改操作
- sqlSession进行了提交
- 手动清除一级缓存
4.2 二级缓存
-
二级缓存默认是关闭的,如果想用的话需要开通
-
总开关(默认是开启的)
-
settings
-
<setting name="cacheEnabled" value="true"/>
-
-
-
分开关
-
映射文件内添加标签cache
-
<!--二级缓存的开关--> <cache/>
-
-
-
-
二级缓存的使用
- 不同的SqlSession也是可以使用二级缓存,但是不同的SqlSession对象必须来自于同一个factory
- 往二级缓存添加的数据必须序列化
- 数据是在sqlSession.close的时候,才会添加
4.3 和缓存相关的配置
-
select标签
-
useCache 是否使用二级缓存
- 默认是true
-
flushCache 是否清除缓存(一级和二级)
- 默认值是false
-
-
insert/update/delete
-
flushCache 是否清除缓存(一级和二级)
- 默认是true
-
-
手动清除缓存只能清除一级,对二级无效
4.4 MyBatis如何整合第三方缓存框架
-
导入第三方缓存框架的jar包
-
<!-- mybatis-ehcache --> <dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.0.3</version> </dependency> <!-- slf4j-log4j12 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.6.2</version> <scope>test</scope> </dependency>
-
-
创建其配置文件
-
ehcache.xml
- 文件名称必须是ehcache.xml
- 内容从课件粘贴
-
-
将缓存框架的核心类配置到映射文件内
-
<!--二级缓存的开关 type属性:引入第三方缓存框架的核心类 --> <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
-
5.逆向工程
- 创建maven模块
- 导入jar包
- 准备数据库和表格
- 创建实体类
- 创建持久层接口
- 创建映射文件
- 创建核心配置文件
- 测试
5.1 步骤
-
导入jar包
-
<!-- mybatis-generator-core --> <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <version>1.3.6</version> </dependency>
-
-
创建逆向工程的配置文件
-
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> <generatorConfiguration> <!-- targetRuntime属性:设置逆向工程的模式 MyBatis3: 有基础的增删改查+QBC风格的查询 MyBatis3Simple: 有基础的增删改查 --> <context id="simple" targetRuntime="MyBatis3Simple"> <!--mysql8需要添加一个额外的配置--> <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/ssm" userId="root" password="root"> <property name="nullCatalogMeansCurrent" value="true" /> </jdbcConnection> <!--实体类放在哪?--> <javaModelGenerator targetPackage="com.atguigu.mbg.bean" targetProject="src/main/java"/> <sqlMapGenerator targetPackage="com.atguigu.mbg.dao" targetProject="src/main/resources"/> <javaClientGenerator type="XMLMAPPER" targetPackage="com.atguigu.mbg.dao" targetProject="src/main/java"/> <table tableName="employees" /> <table tableName="departments" /> </context> </generatorConfiguration>
-
-
执行逆向工程的程序
-
@Test public void test03(){ try { List<String> warnings = new ArrayList<String>(); boolean overwrite = true; File configFile = new File("src/main/resources/mbg.xml"); ConfigurationParser cp = new ConfigurationParser(warnings); Configuration config = cp.parseConfiguration(configFile); DefaultShellCallback callback = new DefaultShellCallback(overwrite); MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings); myBatisGenerator.generate(null); } catch (IOException e) { e.printStackTrace(); } catch (XMLParserException e) { e.printStackTrace(); } catch (InvalidConfigurationException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } }
-
5.2 测试
@Test
public void test01() throws Exception {
InputStream input = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(input);
SqlSession sqlSession = build.openSession();
EmployeesMapper mapper = sqlSession.getMapper(EmployeesMapper.class);
//QBC风格的条件
//需求:查询部门id为2的员工信息
EmployeesExample example=new EmployeesExample();
//设置条件
EmployeesExample.Criteria criteria = example.createCriteria();
criteria.andDeptIdGreaterThanOrEqualTo(2);
//需求:查询部门id大于等于2 并且 名字不为null
criteria.andLastNameIsNotNull();
//需求:(查询部门id大于等于2 并且 名字不为null ) 或者 工资大于15000
EmployeesExample.Criteria criteria1 = example.createCriteria();
criteria1.andSalaryGreaterThan(15000D);
//设置两个Criteria对象是或者关系
example.or(criteria1);
List<Employees> employees = mapper.selectByExample(example);
employees.forEach(System.out::println);
sqlSession.commit();
sqlSession.close();
}
6.分页插件
-
功能:帮助我们进行分页
-
步骤
-
导入jar包
-
<!-- pagehelper --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.0.0</version> </dependency>
-
-
配置分页插件
-
位置:核心配置文件
-
代码
-
<!--配置分页插件--> <plugins> <!-- 4版本:com.github.pagehelper.PageHelper 5版本:com.github.pagehelper.PageInterceptor --> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <!--配置方言:就是设置连接的数据库--> <!-- <property name="helperDialect" value="Mysql"/>--> </plugin> </plugins>
-
-
-
使用
-
//① 在查询之前,设置分页的信息 Page page = PageHelper.startPage(6, 2); //② 查询 List<Employees> employees = mapper.selectByExample(example); employees.forEach(System.out::println); //③ Page对象必须在查询之后使用 int pageNum = page.getPageNum(); System.out.println("pageNum = " + pageNum); int pageSize = page.getPageSize(); System.out.println("pageSize = " + pageSize); long total = page.getTotal(); System.out.println("total = " + total);//总条数 int pages = page.getPages(); System.out.println("pages = " + pages);//总页数 //④ PageInfo对象,内容就比较多 ★ //PageInfo对象中有查询结果,有当前页的所有信息 PageInfo pageInfo=new PageInfo(employees,3); int prePage = pageInfo.getPrePage(); System.out.println("prePage = " + prePage); int nextPage = pageInfo.getNextPage(); System.out.println("nextPage = " + nextPage); boolean hasPreviousPage = pageInfo.isHasPreviousPage(); System.out.println("hasPreviousPage = " + hasPreviousPage); boolean isFirstPage = pageInfo.isIsFirstPage(); System.out.println("isFirstPage = " + isFirstPage); boolean hasNextPage = pageInfo.isHasNextPage(); System.out.println("hasNextPage = " + hasNextPage); boolean isLastPage = pageInfo.isIsLastPage(); System.out.println("isLastPage = " + isLastPage); int startRow = pageInfo.getStartRow(); System.out.println("startRow = " + startRow); int endRow = pageInfo.getEndRow(); System.out.println("endRow = " + endRow); int[] navigatepageNums = pageInfo.getNavigatepageNums(); System.out.println(Arrays.toString(navigatepageNums));
-
-