目录
一、MyBatis使用步骤:
① 导入 mybatis.jar ,mysql-connector-java.jar 等jar包
② conf.xml (数据库配置信息、映射文件)
③ 数据库表-实体类:映射文件 mapper.xml
④ 测试
二、基础方式的增删改查:
mybatis约定:
输入参数 parameterType 和 输出参数 resultType ,在形式上都只能有一个
1 输入参数 :
① 如果是简单类型(8个基本类型+String) 可以使用占位符:#{xxxx}
② 如果是对象类型,则必须是对象的属性 #{属性名}
2 输出参数:
如果返回值类型是一个 对象(如Student),则无论返回一个、还是多个,resultType 都写成 xxx.xxxx.entity.Student
即 resultType="xxx.xxx.entity.Student"
3 注意事项:
① 如果使用的 事务方式为 jdbc,则需要 commit提交,即session.commit();
② 所有的标签如 < select > < update >等 ,都必须有sql语句,但是sql参数值可选
例如:select * from student where sno= #{sno}
sql 有参数:session.insert(statement, 参数值 );
sql 没参数:session.insert(statement);
三、mapper动态代理方式的crud (MyBatis接口开发):
原则:约定优于配置
1 硬编码方式
xxx.java
Configuration conf = new Configuration();
con.setName("myProject") ;
2 配置方式:
xxx.xml
<name>myProject</name>
约定:默认值就是myProject
3 具体实现的步骤:
(1)基础环境
mybatis.jar,mysql-connector-java.jar,conf.xml,mapper.xml
(2)不同之处
约定的目标: 省略掉statement , 即 根据约定 直接可以定位出SQL语句
(3)接口
接口中的方法必须遵循以下约定:
① 方法名 和 mapper.xml文件中标签的id值相同
② 方法的 输入参数 和 mapper.xml文件中标签的 parameterType类型一致 (如果mapper.xml的标签中没有 parameterType,则说明方法没有输入参数)
③ 方法的返回值 和 mapper.xml文件中标签的 resultType类型一致 (无论查询结果是一个 还是多个(student、List),在mapper.xml标签中的resultType中只写 一个(Student);
如果没有resultType,则说明方法的返回值 为 void)
除了以上约定,要实现 接口中的方法 和 mapper.xml 中SQL标签一一对应,还需要注意:namespace的值 ,就是 接口的全类名( 接口 - mapper.xml 一一对应)
例:
在mapper.xml 中namespace设置如下,PersonMapper为接口的全类名
<mapper namespace="org.lanqiao.mapper.PersonMapper">
4 匹配的过程:(约定的过程)
(1)根据 接口名 找到 mapper.xml文件
(根据的是namespace=接口的全类名)
(2)根据 接口的方法名 找到 mapper.xml文件的SQL标签
(方法名=SQL标签的Id值)
以上2点可以保证: 当我们调用接口中的方法时,
程序能自动定位到 某一个mapper.xml文件中的sql标签
【ps:SQL映射文件(mapper.xml) 和 接口放在同一个包中 (注意修改conf.xml中加载mapper.xml文件的路径)】
以上,可以通过 接口的方法->SQL语句
执行语句:
PersonMapper personMapper = session.getMapper(PersonMapper.class) ;
personMapper.方法();
例:
//通过id查询信息
public static void queryPersonById(int id) throws IOException
{
// 加载MyBatis配置文件(为了访问数据库)
Reader reader = Resources.getResourceAsReader("conf.xml");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sessionFactory.openSession();
PersonMapper personMapper = session.getMapper(PersonMapper.class);
Person person = personMapper.queryPersonById(id);
System.out.println(person.toString());
session.close();
}
【 通过session对象获取接口(session.getMapper(接口.class);),再调用该接口中的方法,程序会自动执行该方法对应的SQL语句 】
5 优化
(1)将配置信息单独放入 db.properties文件中,然后再动态引入db.properties:
① File文件db.properties的内容
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/xxx
username=root
password=123456
② conf.xml 中 添加
<configuration>
<properties resource="db.properties"/>
③ 引入之后,使用${key}
<dataSource type="POOLED">
<!-- 配置数据库信息 -->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}"/>
</dataSource>
(2)MyBatis全局参数在conf.xml中设置 ,设置方式如下
<settings>
<setting name="cacheEnabled" value="true" />
<setting name="lazyLoadingEnabled" value="true" />
</settings>
(3).别名 conf.xml
设置单个别名,批量设置别名
<typeAliases>
<!-- 单个别名 (别名 忽略大小写) -->
<typeAlias type="org.lanqiao.entity.Student" alias="student"/>
<!-- 批量定义别名 (别名 忽略大小写),以下会自动将该包中的所有类 批量定义别名: 别名就是类名(不带包名,忽略大小写) -->
<package name="org.lanqiao.entity"/>
</typeAliases>
四、输入参数:parameterType详解
1 类型为 简单数据类型(8个基本类型+String)
(1)#{}、${}的区别
① 区别一:
#{任意值}
${value} ,其中的标识符只能是value
② 区别二:
#{}自动给String类型加上’ ’ (自动类型转换)
${} 原样输出,但是适合于 动态排序(动态字段)
select * from student where stuname = #{value}
select * from student where stuname = '${value}'
//动态排序:
select * from student order by ${value} asc
③ 区别三:
#{}可以防止SQL注入
${}不防止
(2)${}、#{}相同之处:
都可以 获取对象的值 (嵌套类型对象)
模糊查询,方式一:
select stuno,stuname,stuage from student where stuage= #{stuAge} or stuname like #{stuName}
Student student = new Student();
student.setStuAge(24);
student.setStuName("%w%");
List<Student> students = studentMapper.queryStudentBystuageOrstuName(student) ;//接口的方法->SQL
模糊查询,方式二:
student.setStuName("w");
select stuno,stuname,stuage from student where stuage= #{stuAge} or stuname like '%${stuName}%'
嵌套类型对象
2.对象类型
#{属性名}
${属性名}
五、输出参数:resultType详解
(1) 简单类型( 8个基本数据类型+String )
(2) 输出参数为 实体对象类型
(3) 输出参数为 实体对象类型的集合 :虽然输出类型为集合,但是resultType依然写 集合的元素类型(resyltType=“Student”)
(4) 输出参数类型为 HashMap
HashMap本身是一个集合,可以存放多个元素,但是返回值为HashMap时 ,查询的结果只能是1个学生的多个属性
结论:一个HashMap 对应一个学生的多个属性 【一个map,一个学生】
例:二维数组
{
{1,zs,23,xa}, -一个HashMap对象
{2,ls,24,bj},
{3,ww,25,tj}
}
注意:当属性名 和字段名 不一致时,除了使用resultMap以外,还可以使用resultType+HashMap:
(1)resultMap
<resultMap type="student" id="queryStudentByIdMap">
<!-- 指定类中的属性 和 表中的字段 对应关系 -->
<id property="stuNo" column="id" />
<result property="stuName" column="name" />
</resultMap>
(2) resultType+HashMap
select 表的字段名 “类的属性名” from… 来制定字段名 和属性名的对应关系
<select id="queryStudentByIdWithHashMap" parameterType="int" resultType="student" >
select id "stuNo",name "stuName" from student where id = #{id}
</select>
注意: 如果发现某一个字段结果始终为默认值(0,0.0,null),则可能是 表的字段 和 类的属性名字写错。
六、动态SQL:
【where】
<select id="queryStuByNOrAWishSQLTag" parameterType="student" resultType="student" >
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>
</select>
注意:< where > 会自动处理第一个< if >标签中的 and,但不会处理之后中的and
【 foreach】
迭代的类型:数组、对象数组、集合、属性(Grade类: List ids)
属性(Grade类: List ids)
简单类型的数组:
无论编写代码时,传递的是什么参数名(stuNos),在mapper.xml中 必须用 array 代替该数组
集合:
无论编写代码时,传递的是什么参数名(stuNos),在mapper.xml中 必须用 list 代替该数组
对象数组:
Student[] students = {student0,student1,student2} 每个studentx包含一个学号属性
注意:
parameterType="Object[]"
<foreach collection="array" open=" and stuno in (" close=")"
item="student" separator=",">
#{student.stuNo}
</foreach>
<!-- 将多个元素值 放入对象的属性中 -->
<select id="queryStudentsWithNosInGrade" parameterType="grade" resultType="student">
select * from student
<where>
<if test="stuNos!=null and stuNos.size>0">
<foreach collection="stuNos" open=" and stuno in (" close=")"
item="stuNo" separator=",">
#{stuNo}
</foreach>
</if>
</where>
</select>
<!-- 将多个元素值 放入数组中 int[] stuNos = {1,2,53} -->
<select id="queryStudentsWithArray" parameterType="int[]" resultType="student">
select * from student
<where>
<if test="array!=null and array.length">
<foreach collection="array" open=" and stuno in (" close=")"
item="stuNo" separator=",">
#{stuNo}
</foreach>
</if>
</where>
</select>
七、关联查询:
(1)一对一 (association):
① 业务扩展类 (不常用)
核心:用resultType指定类的属性 包含多表查询的所有字段
② resultMap
通过 属性成员 将2个类建立起联系
<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="StudentCard" >
<id property="cardId" column="cardId"/>
<result property="cardInfo" column="cardInfo"/>
</association>
</resultMap>
(2)一对多 (collection):
表:student studentclass (关联:classid)
类:student studentClass (关联:List students )
select c.*,s.* from student s
inner join studentclass c
on c.classid = s.classid
where c.classid = 1;
【 MyBatis:多对一,多对多的本质就是 一对多的变化 】
八、日志:Log4j
① Log4j: log4j.jar (mybatis.zip中lib中包含此jar)
② 开启日志,conf.xml
<settings>
<!-- 开启日志,并指定使用的具体日志 -->
<setting name="logImpl" value="LOG4J"/>
</settings>
如果不指定,Mybatis就会根据以下顺序 寻找日志
SLF4J →Apache Commons Logging →Log4j 2 → Log4j →JDK logging
③ 编写配置日志输出文件
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中的参数 和 返回结果)
九、延迟加载(懒加载):
( 一对一、一对多、多对一、多对多 )
一对多:班级-学生 ,
如果不采用延迟加载 (立即加载),查询时会将 一 和多 都查询,班级、班级中的所有学生。
如果想要 暂时只查询1的一方, 而多的一方 先不查询 而是在需要的时候再去查询 -->延迟加载
一对一:学生、学生证
mybatis中使用延迟加载,需要先配置:
<settings>
<!-- 开启延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 关闭立即加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
【 如果增加了mapper.xml ,要修改conf.xml配置文件(将新增的mapper.xml加载进去) 】
通过debug可以发现, 如果程序只需要学生,则只向数据库发送了查询学生的SQL;当我们后续 需要用到学生证的时候,再第二次发送 查询学生证的SQL。
一对多:和一对一的延迟加载配置方法相同
延迟加载的步骤:先查班级,按需查询学生
① 开启延迟加载conf.xml配置settings
② 配置mapper.xml
( 写2个Mapper: )
班级mapper.xml
<select id="queryClassAndStudents" resultMap="class_student_lazyLoad_map">
select c.* from studentclass c
</select>
<resultMap type="studentClass" id="class_student_lazyLoad_map">
<!-- 因为 type的主类是班级,因此先配置班级的信息-->
<id property="classId" column="classId"/>
<result property="className" column="className"/>
<!-- 配置成员属性学生,一对多;属性类型:javaType,属性的元素类型ofType -->
<!-- 2222222再查班级对应的学生 -->
<collection property="students" ofType="student" select="org.lanqiao.mapper.StudentMapper.queryStudentsByClassId" column="classid">
</collection>
</resultMap>
即查询 学生的sql是通过 select属性指定,并且通过column指定外键
学生mapper.xml
<!-- 一对多,延迟加载需要的: 查询班级中的所有学生 -->
<select id="queryStudentsByClassId" parameterType="int" resultType="student">
select * from student where classId = #{classId}
</select>