Dao层
MyBatis可以简化JDBC操作,实现数据的持久化(存入数据库)
ORM(概念):Object Relational Mapping
对象 《-》 表 之间的映射
MyBatis是ORM的一个实现
ORM使得开发人员像操作对象一样操作数据库表
day01
1、开发mybatis程序步骤
1)、mybatis.jar mysql.jar
2)、表 - 类 相对应
配置mybatis:配置数据库信息和需要加载的映射的文件(conf.xml)
映射文件(xxMapper.xml):增删改查标签
3)、测试类:
Reader reader = Resources.getResourceAsReader("conf.xml");
//SqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sessionFactory.openSession();
//namespace+id
String statement = "com.java.entity.personMapper.queryPersonById";
//session.selectOne("需要查询sql的namespace+id","sql的参数值")
Person person = session.selectOne(statement,1);
System.out.println(person);
day02
复习第一个MyBatis
1、mybatis.jar mysql.jar
2、conf.xml(数据库配置信息、映射文件)
3、表-类:映射文件 mapper.xml
4、测试
一、基础方式的增删改查CRUD
注意事项:
a、如果使用的事务方式为jdbc,则需要手动提交,即session.commit();(增删改需要提交事务)
b、所有增删改查标签都必须有sql语句,但是sql参数值可选
二、mapper动态代理方式的CRUD(mybatis接口开发):
原则:约定优于配置
硬编码方式:
abc.java
Configuration conf = new Configuration();
conf.setName(“myProject”);
配置方式:
abc.xml
myProject
约定:默认值就是myProject
具体实现的步骤:
1、基础环境
2、(不同之处)
约定的目标:省略掉statement,即根据约定直接定位出sql语句
a、接口(接口中的方法必须遵循以下约定)
1、方法名和mapper.xml文件中标签的id值相同
2、方法的输入参数和mapper.xml文件中标签的paramterType类型一致
3、方法的返回值和mapper.xml文件中标签的resultType类型一致
****除了以上约定,要实现接口中的方法和Mapper.xml中sql标签一一对应,还需要以下1点:
namespace的值就是接口的全类名(接口和xml文件的映射)
约定过程:
1、根据接口的全类名定位到mapper.xml文件(namespace值)
2、根据接口的方法名找到mapp.xml文件中的sql标签
(方法名=id,输入参数类型=paramterType,返回值类型=resultType)
当我们调用接口中的方法时,程序能自动定位到某一个Mapper.xml文件中的sql标签
习惯:将sql映射文件(mapper.xml)和接口放在同一个包中
执行:
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
studentMapper.方法;
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
List<Student> students = studentMapper.queryAllStudents();
优化:
1、可以将配置信息单独放入db.properties文件中,然后再动态引入(设置全局参数)
db.properties:key=value
<configuration>
<properties resource="db.properties"/>
使用方式:${key}=value
2、MyBatis全局参数配置
在conf.xml文件中设置
<settings>
<setting name="cacheEnabled" value="false"/><!-- 关闭缓存 -->
</settings>
3、别名
conf.xml中设置(大小写不敏感)
a、设置单个别名
<typeAliases>
<typeAlias type="com.java.entity.Student" alias="student"/>
</typeAliases>
b、批量设置别名
会自动将该包中的所有类批量定义别名:别名就是类名
<typeAliases>
<pakage name="com.java.entity" />
</typeAliases>
三、类型处理器(类型转换器)
1、MyBatis自带一些常见的类型处理器
2、自定义mybatis类型处理器
java - 数据库(jdbc类型)
自定义类型转换器(boolean - number)步骤
a、创建转换器
i、实现TypeHandler接口
此接口有一个实现类BaseTypeHandler
ii、继承实现类BaseTypeHandler
b、配置conf.xml
day03
1、输入参数:parameterType
i、类型为简单类型(8个基本类型+String)
a、#{任意值}
${value}
b、二者区别:
#{任意值}:自动给String类型加上单引号(自动类型转换)
${value}:原样输出,但是适用于动态排序(动态字段)
select * from student order by KaTeX parse error: Expected 'EOF', got '#' at position 16: {value} asc #̲{}可以防止SQL注入 c…{}两者相同之处:
都可以获取对象的值(嵌套对象)
模糊查询
select * from student where stuname like ‘%${stuName}%’
ii、类型为对象类型
#{属性名}
${属性名}
iii、嵌套类型对象
2、mybatis调用存储过程
mapper.xml->mapper接口(dao接口)->测试方法
1)、查询某个年级的学生总数
输入:年级
输出:该年级的学生总数
创建存储过程
CREATE PROCEDURE queryCountByGradeWithProcedure(in gName varchar(20),out scount INT)
BEGIN
SELECT count(*) INTO scount FROM student WHERE graname = gname;
END
<!-- 通过调用[存储过程]实现查询 statementType="CALLABLE"
存储过程的输入参数,在mybatis用Map来传递(HashMap) -->
<select id="queryCountByGradeWithProcedure" statementType="CALLABLE" parameterType="HashMap">
{
CALL queryCountByGradeWithProcedure(
#{gName,jdbcType=VARCHAR,mode=IN},
#{sCount,jdbcType=INTEGER,mode=OUT}
)
}
</select>
要点:
1、statementType="CALLABLE"
2、parameterType="HashMap"
3、Object count = params.get("sCount");//获取输出参数
4、注意jar问题
5、存储过程无返回值
2)、根据学号删除学生
CREATE PROCEDURE deleteStudentByStunoWithProcedure(in sno int)
BEGIN
DELETE FROM student WHERE stuno = sno;
END
day04
resultType
resultMap:当数据库字段和实体类的属性:类型、名字不同时
注意:当属性名和字段名不一致时,还可以使用resultType和HashMap
1、输出参数resultType
i、简单类型(8个基本+String)
ii、对象类型
iii、输出参数为实体对象类型的集合
映射文件中 resultType依然写集合的元素类型
接口中 为实体对象类型的集合
vi、输出类型为hashmap
–hashmap本身是一个集合,可以存放多个元素
select stuno “no”,stuname “name” from student
2、输出参数resultMap
当实体类属性名和数据库字段名不一致时
i、
<resultMap type="student" id="studentsexResult">
<!-- 分为主键和非主键 -->
<id property="stuNo" column="id"/>
<result property="stuName" column="name"/>
</resultMap>
ii、为数据库字段id和name设置别名“stuNo”和“stuName”
<select id="queryStudentOutByHashMap" resultType="Student">
select id "stuNo",name "stuName" from student
</select>
3、动语态sql
1)、where标签
select stuno,stuname,stuage from student
<where>
<!-- <if test="student有stuname属性且不为null时"> -->
<if test="stuName != null and stuName!='' ">
and stuname = #{stuName}
</if>
<if test="stuAge != null and stuAge!=0 ">
and stuage = #{stuAge}
</if>
</where>
//where会自动处理第一个<if>中的and,但不会处理之后的and
2)、foreach标签
迭代的类型:数组、集合、对象数组、属性(grade类:List<Integer> IDS)
open()
item:迭代标签
close()
separator=","定义每个元素之间的分隔符号
i、类属性的迭代(Grade类的属性:stuNos)
<select id="queryStudentsWithNosInAge" parameterType="com.java.entity.Grade" resultType="student">
select * from student
<where>
<if test="stuNos != null and stuNos.size>0">
<foreach collection="stuNos" open=" and stuage in(" close=")" item="name" separator=","><!-- 通过foreach遍历集合 -->
#{name}
</foreach>
</if>
</where>
</select>
ii、简单数组类型的迭代
迭代参数固定为array
collection="array"
无论编写代码时传递的是什么参数名(stuage),在mapper.xml中必须用array代替数组
iii、集合类型的迭代
无论编写代码时传递的是什么参数名(stuage),在mapper.xml中必须用list代替数组
vi、对象数组
无论编写代码时传递的是什么参数名(stuage),在mapper.xml中必须用Object[]代替数组
3)、相同的代码段(sql片段)
java:方法
数据库:存储过程、存储函数
mybatis:sql片段
<select id="queryStudentsWithList" parameterType="List" resultType="student">
select * from student
<include refid="queryStudentList"></include>
</select>
<!-- 提取相同的sql语句 -->
<sql id="queryStudentList">
<where>
<if test="list != null and list.size>0">
<foreach collection="list" open=" and stuage in(" close=")" item="name" separator=","><!-- 通过foreach遍历集合 -->
#{name}
</foreach>
</if>
</where>
</sql>
4、关联查询
1)、主键和外键的作用
1.主键是能确定一条记录的唯一标识,比如,一条记录包括身份正号,姓名,年龄。
身份证号是唯一能确定你这个人的,其他都可能有重复,所以,身份证号是主键。
2.外键用于与另一张表的关联。是能确定另一张表记录的字段,用于保持数据的一致性。
比如,A表中的一个字段,是B表的主键,那他就可以是A表的外键。
2)、一对一
a、业务扩展类
核心:用resultType指定的类的属性包含多表查询的所有字段
b、resultMap
i、通过属性成员将2个类建立联系
ii、
<resultMap type="student" id="student_card_map">
<id property="stuNo" column="stuNo"/><!-- 主键 -->
<result property="stuName" column="stuName"/><!-- 非主键 -->
<result property="stuAge" column="stuage"/>
<!-- 一对一时,对象成员使用association映射;javaType指定该属性的类型 -->
<association property="card" javaType="com.java.entity.StudentCard">
<id property="cardId" column="cardId"/>
<result property="cardInfo" column="cardInfo"/>
</association>
</resultMap>
<select id="queryStudentByNowithOO2" resultMap="student_card_map" parameterType="int">
select s.*,c.* from student s inner join studentcard c on s.cardid = c.cardid
where s.stuno = #{stuNo}
</select>
*一对一:association javaType
一对多:collection ofType
3)、一对多
<!-- 一对多关联查询 -->
<select id="queryClassAndStudents" resultMap="class_student_map" parameterType="int">
select c.*,s.* from student s
inner join studentclass c
on c.classid=s.classid
where c.classid = #{classid}
</select>
<resultMap type="com.java.entity.StudentClass" id="class_student_map">
<id property="classId" column="classId"/>
<result property="className" column="className"/>
<!-- 配置成员属性学生,一对多;ofType -->
<collection property="students" ofType="Student">
<id property="stuNo" column="stuNo"/>
<result property="stuName" column="stuName"/>
<result property="stuAge" column="stuAge"/>
<result property="graName" column="graName"/>
<association property="address" javaType="com.java.entity.Address">
<id property="homeAddress" column="homeAddress"/>
<result property="schoolAddress" column="schoolAddress"/>
</association>
<association property="card" javaType="com.java.entity.StudentCard">
<id property="cardId" column="cardId"/>
<result property="cardInfo" column="cardInfo"/><!-- 未关联 -->
</association>
</collection>
</resultMap>
day05
1、日志
a、Log4j:(mybatis.zip的lib中包含该jar)
log4j.jar
b、在conf.xml配置文件中指定日志
<settings>
<!-- 开启日志,并指定使用的具体日志 -->
<setting name="logImol" value="LOG4J"/>
</settings>
如果不指定,mybatis就会根据以下顺序寻找日志
常见日志SLF4J -> Apache Commons Logging -> Log4j 2 -> Log4j -> JDK logging
c、编写配置日志输出文件
log4j.properties 内容:
log4j.rootLogger=DEBUG, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
日志级别:
DEBUG<INFO<WARN<ERROR
如果设置为info,则只显示info及以上级别的信息;
建议:在开发时设置debug,在运行时设置为info或以上
通过日志信息,可以详细的阅读mybatis执行情况
(主要查看mybatis实际执行的sql语句以及sql中的参数和返回结果)
2、延迟加载
//将原查询sql语句分成两个子查询
延迟加载需要在另一个mapper中写
a、一对一
mybatis中使用延迟加载需要先在conf.xml中配置
如果增加了mapper.xml,要修改conf.xml中的配置文件(将新增的mapper.xml加载进去)
<select id="queryStudentByNowithOOLazyLoad" resultMap="student_card_LazyLoad_map" parameterType="int">
<!-- 先查询学生 -->
select * from student where cardId = #{cardid}
</select>
<!-- 类-表的对应关系 -->
<resultMap type="student" id="student_card_LazyLoad_map">
<id property="stuNo" column="stuNo"/><!-- 主键 -->
<result property="stuName" column="stuName"/><!-- 非主键 -->
<result property="stuAge" column="stuage"/>
<result property="graName" column="graName"/>
<!-- 一对一时,对象成员使用association映射;javaType指定该属性的类型
采用延迟加载:查询学生时,并不立即加载学生证信息
-->
<!-- 学生证 通过select在需要的时候再查学生证
查询学生证的sql是通过select属性指定,并且通过column指定外键-->
<association property="card" javaType="com.java.entity.StudentCard"
select="com.java.mapper.StudentCardMapper.queryCardById" column="cardId"><!-- column:外键字段 -->
</association>
</resultMap>
<mapper namespace="com.java.mapper.StudentCardMapper"><!-- namespace:映射文件的路径 -->
<!-- 查询学生证信息 -->
<select id="queryCardById" parameterType="int" resultType="com.java.entity.StudentCard">
<!-- 查询学生对应的学生证 -->
select * from studentcard where cardid = #{cardId}
</select>
<!-- 根据cardid查询学生证的sql: com.java.mapper.StudentCardMapper.queryCardById-->
</mapper>
b、一对多
和一对一的延迟加载配置方法相同
班级mapper.xml
学生mapper.xml
总结:延迟加载的步骤:先查班级,按需求查学生
1、开启延迟加载conf.xml配置setting
2、配置mapper.xml
学生mapper.xml
<select id="queryStudentByNowithOOLazyLoad" resultMap="student_card_LazyLoad_map" parameterType="int">
<!-- 先查询学生 -->
select * from student where cardid = #{cardid}
</select>
学生证mapper.xml
<select id="queryCardById" parameterType="int" resultType="com.java.entity.StudentCard">
<!-- 查询学生对应的学生证 -->
select * from studentcard where cardid = #{cardId}
</select>
day06
1、 查询缓存
i、一级缓存 同一个SqlSession对象
mybatis默认开启一级缓存(如果用相同的sqlsession对象查询相同的数据,则只会在第一次
查询时向数据库发送sql语句,并将查询的结果放入到sqlsession中[作为缓存],后续再次查询该同样的
对象时,则直接从缓存中查询该对象即可[即省略了数据库的访问])
session. commit()会清除缓存
ii、二级缓存
session.close();//进行二级缓存的时刻
a、mybatis自带的二级缓存(默认关闭):【同一个namespace】生成的mapper对象
回顾:namespace的值就是接口的全类名(包名.类名)
开启二级缓存:
1)、conf.xml
2)、在具体的mapper.xml中声明开启
异常java.io.NotSerializableException
mybatis的二级缓存是将对象从内存放入硬盘(序列化)
3)、准备缓存的对象,必须实现了序列化接口
序列化Student类的级联属性、父类
触发将对象写入二级缓存的时机:session.close();//进行二级缓存的时刻
Cache Hit Ratio [com.java.mapper.StudentMapper]: 0(第一次查询命中率)
Cache Hit Ratio [com.java.mapper.StudentMapper]: 0.5(第二次查询命中率)
Cache Hit Ratio [com.java.mapper.StudentMapper]: 0.75(第四次查询命中率)
4)、禁用:在select标签中useCache=“false”
5)、清理:与清理一级缓存的方法相同
session.commit();(一般执行增删改时会清理掉缓存[防止脏数据的产生])
commit会清理一级和二级缓存;但是清理二级缓存时,不能是查询自身的commit
清理缓存的第二种方式
在select标签中flushCache="true"
b、第三方提供的二级缓存
ehcache、memcache
要想整合三方提供的二级缓存(或自定义二级缓存)必须实现Cache接口,该接口的
默认实现类PerpetualCache
整合ehcache二级缓存:
1)、jar包
Ehcache-core.jar
mybatis-ehcache.jar
slf4j-api.jar
2)、编写ehcache配置文件EhCache.xml;
3)、开启ehcache二级缓存
<cache type="org.mybatis.caches.ehcache.EhcacheCache">
<!-- 通过property覆盖默认值 -->
<property name="" value=""/>
</cache>
2、逆向工程(不实用)
表、类、接口、mapper.xml四者密切相关,因此当知道一个的时候,其他三个应该可以自动生成
表-》其他三个
实现步骤:
a、jar包
mybatis-generator-core.jar
b、逆向工程的配置文件generator.xml
c、执行test类
List<String> warnings = new ArrayList<String>();
ConfigurationParser cp = new ConfigurationParser(warnings);
File file = new File("src/generator.xml");//配置文件
Configuration config = cp.parseConfiguration(file);
DefaultShellCallback callback = new DefaultShellCallback(true);
//逆向工程的核心类
MyBatisGenerator generator= new MyBatisGenerator(config,callback,warnings);
generator.generate(null);