1.基础方式的增删改查CRUD:
mybatis约定:
输入参数parameterType 和输出参数resultTyp,在形式都只能有一个,可以用集合,数组来表示
如果输入/输出参数 :是简单类型(8个基本数据类型)是可以使用任何占位符,
如果是对象类型,则必须是对象的属性#{属性名}
输出参数 :如果返回值 是一个对象,则无论返回一个,还是多个
在resultTyp都写成com.itt.entity.Student
即 resultType="com.itt.entity.Student"
注意事项:
如果使用的事务方式为jdbc, 则需要手工commit提交,即session.commit();
所有的标签<select><update>等,都必须有sql语句,但是sql参数值可选
select * from student where stuno = #{xx}
sql 有参数:session.insert(statement,student);
sql没有参数:session.insert(statment);
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="db.properties"/>
<!--全局参数-->
<settings>
<setting name="cacheEnabled" value="false"/>
</settings>
<!--设置单个/多个别名-->
<typeAliases>
<!--单个别名(别名 忽略大小写)-->
<!--<typeAlias type="com.itt.entity.Student" alias="student"/>-->
<!--批量定义别名,以下会自动将该包中的所有类 批量定义别名:别名就是类名(不带包名的类名)-->
<package name="com.itt.entity"/>
</typeAliases>
<typeHandlers>
<typeHandler handler="com.itt.converter.BooleanAndIntConverter"
javaType="boolean" jdbcType="INTEGER"/>
</typeHandlers>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="StudentMapper.xml"/>
</mappers>
</configuration>
-
当<transactionManager type="JDBC"/>事务的类型为JDBC时,在进行增、删、改时(也包括存储过程)必须手动的提交事务:
session.commit();//提交事务
2.mapper动态代理方式的CRUD(MyBatis接口开发):
原则:约定优于配置:
-
配置范式():
- abc.xml
- <name>myProject</name>
- abc.xml
-
硬编码方式:
- abc.java
- Configuration conf = new Configuration();
- con.setName("myProject")
- abc.java
-
具体实现步骤
- 基础环境
- 不同之处,
- 约定 目标:省略掉statement,根据约定,直接可以定位出SQL语句
a.接口
1.方法名和mapper.xml文件中标签的id值相同
2.方法的输入参数和Mapper.xml文件中标签的paramerType类型一致
3.方法的返回值和mapper.xml文件中标签的resultType一样
除了以上约定,要实现接口中的方法和Mapper.xml中SQL标签一一对应:
1 namespace的值 ,就是接口的全类名(接口-mapper.xml一一对应)
-
匹配的过程:
1.根据接口名找到mapper.xml文件(根据的是namespace==接口全类名)
2.根据接口的方法名找到mapper.xml文件中的SQL标签(方法名=SQL标签Id)
习惯:SQL映射文件(mapper.xml)和接口放在同一个包中
-
优化:
-
可以将配置信息,单独放入db.properties文件中,然后再动态引入
db.properties :
k = v
<configuration>
<properties resoure="db.propertios"/>
引入之后,使用${key}
-
MyBatis全局参数
<properties resoure="db.propertios"/> //全局参数的配置位置 <settings> <setting name="" value=""/> <setting name="" value=""/> </setting> <environments default="development"
-
定义别名 conf.xml
a.设置单个别名
b.批量设置别名
<properties resource="db.properties"/> <!--全局参数--> <settings> <setting name="cacheEnabled" value="false"/> </settings> <!--设置单个/多个别名--> <typeAliases> <!--单个别名(别名 忽略大小写)--> <!--<typeAlias type="com.itt.entity.Student" alias="student"/>--> <!--批量定义别名,以下会自动将该包中的所有类 批量定义别名:别名就是类名(不带包名的类名)--> <package name="com.itt.entity"/> </typeAliases>
-
除了自定义别名外,MyBatis还内置了一些 常见类的别名。
3.类型处理器(类型转换器)
-
MyBatis自带一些常见的类型处理器
int - number
-
自定义MyBatis类型处理器
java - 数据库(jdbc类型)
示例:
实体类Student: boolean stuSex
true:男
false:女
表student: number stuSex
1:男
0:女
-
自定义类型转换器(boolean - number):步骤:
- 创建转换器:实现TypeHandle接口
- 通过阅读源码发现,此接口有一个实现类BaseTypeHandler,因此,要实现转换器有两种方式:
- 实现TypeHandle接口
- 继承BaseTypeHandler类
- 配置conf.xml
需要注意的问题:INTEGER
<!--带转换器的增加--> <insert id="addStudentWithConverter" parameterType="student"> insert into student(stuno,stuname,stuage,graname,stusex) values(#{stuNo},#{stuName},#{stuAge},#{graName},#{stuSex,javaType=boolean,jdbcType=INTEGER}) </insert> //注意,values中的字段要与java类中的getter方法相对应,否则就会发生错误!!
-
resultMap可以实现两个功能:
- 类型转换
- 属性——字段的映射关系
<resultMap id="studentMapping" type="student"> <id property="id" column="stuno"/> <id property="stuName" column="stuname"/> <id property="stuAge" column="stuage"/> <id property="graName" column="graname"/> <id property="stuSex" column="stusex" javaType="boolean" jdbcType="INTEGER" /> </resultMap>
4.输入参数:parameterType
(1)类型为:简单类型(8个基本类型+String)
a.#{} ${} 不同之处:
-
{任意值}
<select id="queryStudentByStuno" parameterType="int" resultMap="studentMapping">
select * from student where stuno = #{stuno}
</select>
- ${value}:标识符只能是value
<select id="queryStudentByStuno" parameterType="int" resultMap="studentMapping">
select * from student where stuno = ${value }
</select>
b.#{}:自动给String类型加上 ' ' 【自动类型轮换】
<select id="queryStudentByStuname" parameterType="string" resultMap="studentMapping">
select * from student where stuname = #{stuName }
</select>
${}:原样输出,**但是适合于动态排序(动态字段)**
<select id="queryStudentByStuname" parameterType="string" resultMap="studentMapping">
select * from student where stuname = '${value }'
</select>
-
动态排序
select * from student order by ${value } desc
c.#{}可以防止SQL注入
${}不可以
(2) 对象类型
-
{属性名}
- ${属性名}
${}、#{}相同之处:
a.都可以 获取对象的值( 嵌套类型对象 )
模糊查询的两种方式:
i:获取对象的值
方式一、
//Mapper.xml
select * from student where stuage = #{stuAge} or stuname like #{stuName}
//传值
Student student = new Student();
student.setStuAge(23);
student.setStuName("%z%");
List<Student> students= studentMapper.queryStudentBystuageOrstuName(student);
方式二、
//Mapper.xml
<select id="queryStudentBystuageOrstuName" parameterType="student" resultType="student">
select * from student where stuage = #{stuAge} or stuname like '%${stuName}%'
</select>
//传值
Student student = new Student();
student.setStuAge(23);
student.setStuName("z");
List<Student> students= studentMapper.queryStudentBystuageOrstuName(student);
ii:嵌套类型对象
- Mapper.xml
//Mapper.xml
<!--输入参数为级联属性-->
<select id="queryStudentByaddress" parameterType="student" resultType="student">
select stuno,stuname,stuage from student where homeaddress = #{address.homeAddress} or schooladdress = '${address.schoolAddress}'
</select>
- Student
package com.itt.entity;
public class Student {
private int stuNo;
private String stuName;
private int stuAge;
private String graName;
private boolean stuSex;
private Address address;
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public int getStuNo() {
return stuNo;
}
public void setStuNo(int stuNo) {
this.stuNo = stuNo;
}
public boolean isStuSex() {
return stuSex;
}
public void setStuSex(boolean stuSex) {
this.stuSex = stuSex;
}
public String getStuName() {
return stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
public int getStuAge() {
return stuAge;
}
public void setStuAge(int stuAge) {
this.stuAge = stuAge;
}
public String getGraName() {
return graName;
}
public void setGraName(String graName) {
this.graName = graName;
}
public Student() {
}
public Student(int stuNo, String stuName, int stuAge, String graName) {
this.stuNo = stuNo;
this.stuName = stuName;
this.stuAge = stuAge;
this.graName = graName;
}
public Student(int stuNo, String stuName, int stuAge, String graName, boolean stuSex) {
this.stuNo = stuNo;
this.stuName = stuName;
this.stuAge = stuAge;
this.graName = graName;
this.stuSex = stuSex;
}
@Override
public String toString() {
return "Student{" +
"stuNo=" + stuNo + ", stuName='" + stuName + '\'' +", stuAge=" + stuAge +", graName='" + graName + '\'' +
", stuSex=" + stuSex +
'}'+"\n";
}
}
- Address
package com.itt.entity;
public class Address {
private String homeAddress;
private String schoolAddress;
public String getHomeAddress() {
return homeAddress;
}
public void setHomeAddress(String homeAddress) {
this.homeAddress = homeAddress;
}
public String getSchoolAddress() {
return schoolAddress;
}
public void setSchoolAddress(String schoolAddress) {
this.schoolAddress = schoolAddress;
}
public Address(String homeAddress, String schoolAddress) {
this.homeAddress = homeAddress;
this.schoolAddress = schoolAddress;
}
public Address() {
}
@Override
public String toString() {
return this.homeAddress +","+this.schoolAddress;
}
}
- StudentMapper
public interface StudentMapper {
...
List<Student> queryStudentByaddress(Student address);
....
}
- Test
public void Test{
//根据地址查学生
public static void queryStudentByaddress() throws Exception{
//Connection - SqlSession操作myBatis
//config.xml -> reader
Reader reader = Resources.getResourceAsReader("conf.xml");
//reader ->SqlSession
SqlSessionFactory sessionFactory=new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sessionFactory.openSession();
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
Student student = new Student();
Address address = new Address();
address.setHomeAddress("xa");
address.setSchoolAddress("x");
//学生里面包含着地址
student.setAddress(address);
List<Student> students= studentMapper.queryStudentByaddress(student);
System.out.println(students);
session.close();
}
}
- 结果
[Student{stuNo=1, stuName='zs', stuAge=23, graName='null', stuSex=false}
, Student{stuNo=2, stuName='kk', stuAge=23, graName='null', stuSex=false}
, Student{stuNo=3, stuName='cv', stuAge=10, graName='null', stuSex=false}
, Student{stuNo=22, stuName='jz', stuAge=15, graName='null', stuSex=false}
]
输入对象为HashMap:
where stuage = #{stuAge}
用map中key的值匹配 占位符#{stuAge},如果匹配成功,应用map的value替换占位符。
- studentMapper.xml
<select id="queryStudentBystuageOrstuNameWithHashMap" parameterType="HashMap" resultType="student">
select * from student where stuage = #{stuAge} or stuname like '%${stuName}%'
</select>
- StudentMapper
public interface StudentMapper{
...
List<Student> queryStudentBystuageOrstuNameWithHashMap(Map<Object,Object> map);
...
}
- Test
//查询全部学生,根据年龄或姓名(输入参数为:HashMap)
public static void queryStudentBystuageOrstuNameWithHashMap() throws Exception{
//Connection - SqlSession操作myBatis
//config.xml -> reader
Reader reader = Resources.getResourceAsReader("conf.xml");
//reader ->SqlSession
SqlSessionFactory sessionFactory=new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sessionFactory.openSession();
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
Map<Object, Object> studentMap = new HashMap<>();
studentMap.put("stuAge",23);
studentMap.put("stuName","jz");
List<Student> students= studentMapper.queryStudentBystuageOrstuNameWithHashMap(studentMap);
System.out.println(students);
session.close();
}
5.mybatis调用存储过程:
mapper.xml ->mapper接口 ->测试方法
- 存储过程:queryCountByGradeWithProcesure
DELIMITER $$
CREATE
PROCEDURE `test`.`queryCountByGradeWithProcesure`(IN gName VARCHAR,OUT scount INT)
BEGIN
SELECT COUNT(1) INTO scount FROM student WHERE graname = gName;
END$$
DELIMITER ;
CALL queryCountByGradeWithProcesure('g1');
- studentMapper.xml
<!--通过调用存储过程实现查询
CALLABLE:存储过程
存储过程的输入参数,在mybatis用Map来传递(HashMap)
-->
<select id="queryCountByGradeWithProcesure" statementType="CALLABLE" parameterType="HashMap">
{
CALL queryCountByGradeWithProcesure(
#{gName,jdbcType=VARCHAR,mode=IN},
#{sCount,jdbcType=INTEGER,mode=OUT}
)
}
</select>
- StudentMapper【接口】
//根据存储过程返回某个年级的学生总数
void queryCountByGradeWithProcesure(Map<Object,Object> params);
- Test
//根据存储过程返回某个年级的学生总数
public static void queryCountByGradeWithProcesure() throws Exception{
//Connection - SqlSession操作myBatis
//config.xml -> reader
Reader reader = Resources.getResourceAsReader("conf.xml");
//reader ->SqlSession
SqlSessionFactory sessionFactory=new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sessionFactory.openSession();
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
Map<Object,Object> params = new HashMap<>();
params.put("gName","s2");//指定存储过程中gName的值为s1
//调用存储过程,并传入输入参数
studentMapper.queryCountByGradeWithProcesure(params);
//获取存储过程的输出参数
Object count = params.get("sCount");
System.out.println(count);
session.close();
}
在使用时,通过HashMap的put方法传入输入参数的值;通过HashMap的get方法获取输出参数的值。要注意Jar包问题。
- 结果
"D:\Program Files\Java\jdk1.8.0_131\bin\java.exe" .....
3
Process finished with exit code 0
如果报错:No enum constant org.apache.ibatis.type.JdbcType.xxx
则说明mybatis不支持这个类型,需要查表:Mybatis中javaType和jdbcType对应关系表
注:存储过程中无论输入参数是什么值,语法上都需要用Map来传递该值;
6.输出参数:resultType
1.简单类型(8个基本类型+String)
2.输出参数为实体对象类型
3.输出参数为实体对象类型的集合:虽然输出类型为集合,但是【注意】
- 在映射文件中:resultType依然写集合的元素类型不是集合类型(resultType="Student")
- 在接口中仍然写的是List<Student>
4.输出类型为HashMap
------HashMap本身就是一个集合,可以存放多个元素,但是根据 提示返回值为HashMap时,查询的结果只能是1学生(no,name );
--->结论:一个HashMap对应一个学生的多个元素(多个属性)【一个map一个学生】
//StudentMapper.xml
<!--通过别名指定Map的key-->
<select id="queryStudentOutByHashMap" resultType="HashMap">
select stuno "no",stuname "name" from student where stuno = 2
</select>
//StudentMapper【interface】
HashMap<Object,Object> queryStudentOutByHashMap();
//Test
//查询一个学生,结果为map类型
public static void queryStudentOutByHashMap() throws Exception{
//Connection - SqlSession操作myBatis
//config.xml -> reader
Reader reader = Resources.getResourceAsReader("conf.xml");
//reader ->SqlSession
SqlSessionFactory sessionFactory=new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sessionFactory.openSession();
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
HashMap<Object, Object> studentMap = studentMapper.queryStudentOutByHashMap();
System.out.println(studentMap);
session.close();
}
可以将Map放入到List中,然后再打印就可以得到所有的记录了
//StudentMapper.xml
<select id="queryAllStudentsOutByHashMap" resultType="HashMap">
select stuno "no",stuname "name" from student
</select>
//StudentMapper【interface】
List<HashMap<Object,Object>> queryAllStudentsOutByHashMap();
//Test
//查询多个学生,结果为map类型
public static void queryAllStudentsOutByHashMap() throws Exception{
//Connection - SqlSession操作myBatis
//config.xml -> reader
Reader reader = Resources.getResourceAsReader("conf.xml");
//reader ->SqlSession
SqlSessionFactory sessionFactory=new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sessionFactory.openSession();
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
List<HashMap<Object, Object>> studentMap = studentMapper.queryAllStudentsOutByHashMap();
System.out.println(studentMap);
session.close();
}
resultType:
resultMap:实体为的属性、数据表的字段:类型、名字不同时
注意:当属性名 和字段名 不一致时,除了使用resultMap以外,还可以使用resultType+HashMap
a.resultMap
<select id="queryStudentById" parameterType="int" resultMap="queryStudentByIdMap">
select id,name from student where id = #{id}
</select>
<resultMap id="queryStudentByIdMap" type="student">
<!--指定类中的属性和表中的字段对应关系-->
<id property="stuNo" column="id" />
<result property="stuName" column="name" />
</resultMap>
b.resultType+HashMap
select 表的字段名 "类的属性名" from.....来指定字段名和属性名的对应关系
<select id="queryStudentByIdWithHashMap" parameterType="int" resultType="student">
select id "stuNo",name "StuName" from student where id = #{id}
</select>
注意:如果有10个字段,但发现某一个字段结果始终为默认值(0,0.0,null),则可能是表的字段和类的属性名写错了。
7.用mybatis实现动态SQL
mybatis提供了<if>、<where>、<foreach>等标签来实现SQL语句的动态拼接。
- 动态SQL的写法(第一种)
<select id="queryStuByNOrAWithSQLTag" parameterType="student" resultType="student">
select stuno,stuname,stuage from student where 1=1 //永远成立
<!-- <if test="student有stuname属性且不为空">-->
<if test="stuName != null and stuName != '' ">
and stuname = #{stuName}
</if>
<if test="stuAge != null and stuAge != 0">
and stuage = #{stuAge}
</if>
</select>
- 第二种(<where>标签会自动处理第一个<if>标签的and 语句,但不会处理之后<if>中的and)
<select id="queryStuByNOrAWithSQLTag" parameterType="student" resultType="student">
select stuno,stuname,stuage from student
<where>
<!-- <if test="student有stuname属性且不为空">-->
<if test="stuName != null and stuName != '' ">
and stuname = #{stuName}
</if>
<if test="stuAge != null and stuAge != 0">
and stuage = #{stuAge}
</if>
</where>
</select>
<foreach>
8.查询学号为1、3、5的学生信息
select stuno,stuname from student where stuno in (1,3,5);
<foreach>迭代的类型:数组、对象数组、集合、属性(Grade类:List<Integer> ids)
1.属性(Grade类:List<Integer> ids)
<!--将多个元素放入到对象的属性中-->
<select id="queryStudentsWithNOsInGrade" parameterType="grade" resultType="student">
select * from student
<!--
collection="stuNos":指的是grade中的元素
open=" and stuno in (":指的是语句的左半部分的语句
close=")":为语句的右半部分的语句
item="stuNo"为指定的别名,指的是集合中的每个元素
separator=",":指定的是in(1,2,4)之间是用","进行分隔的
-->
<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>
//Grade
public class Grade {
//学号
private List<Integer> stuNos;
public List<Integer> getStuNos() {
return stuNos;
}
public void setStuNos(List<Integer> stuNos) {
this.stuNos = stuNos;
}
}
//StudentMapper
List<Student> queryStudentsWithNOsInGrade(Grade grade);
//查询指定学号对应的学生
public static void queryStudentsWithNOsInGrade() throws Exception{
//Connection - SqlSession操作myBatis
//config.xml -> reader
Reader reader = Resources.getResourceAsReader("conf.xml");
//reader ->SqlSession
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sessionFactory.openSession();
StudentMapper mapper = session.getMapper(StudentMapper.class);
Grade grade = new Grade();
List<Integer> stuNos = new ArrayList<>();
stuNos.add(1);
stuNos.add(3);
stuNos.add(5);
grade.setStuNos(stuNos);
List<Student> student = mapper.queryStudentsWithNOsInGrade(grade);
System.out.println(student);
session.close();
}
//结果为
[Student{stuNo=1, stuName='lxs', stuAge=0, graName='null', stuSex=false}
, Student{stuNo=3, stuName='ls', stuAge=0, graName='null', stuSex=false}
, Student{stuNo=5, stuName='ww', stuAge=20, graName='null', stuSex=false}
]
2.简单类型的数组
- 无论编写代码时,传递的是什么参数名(stuNos),在Mapper.xml中,必须用array代替该数组。
//StudentMapper.xml
<!--将多个元素放入到数组中 int[] stuNos -->
<select id="queryStudentsWithArray" parameterType="int[]" resultType="student">
select * from student
<where>
<!--数组中传过来的参数不论名称是什么,统一默认为array-->
<if test="array != null and array.length ">
<foreach collection="array" open=" and stuno in(" close=")" item="stuNO" separator=",">
#{stuNO}
</foreach>
</if>
</where>
</select>
//查询指定学号对应的学生使用数组
public static void queryStudentsWithArray() throws Exception{
//Connection - SqlSession操作myBatis
//config.xml -> reader
Reader reader = Resources.getResourceAsReader("conf.xml");
//reader ->SqlSession
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sessionFactory.openSession();
StudentMapper mapper = session.getMapper(StudentMapper.class);
//传入一个int类型的数组
int[] stuNos = {1,3,5};
List<Student> student = mapper.queryStudentsWithArray(stuNos);
System.out.println(student);
session.close();
}
//StudentMapper
List<Student> queryStudentsWithArray(int[] stuNos);
//运行结果
[Student{stuNo=1, stuName='lxs', stuAge=0, graName='null', stuSex=false}
, Student{stuNo=3, stuName='ls', stuAge=0, graName='null', stuSex=false}
, Student{stuNo=5, stuName='ww', stuAge=20, graName='null', stuSex=false}
]
3.集合
无论编写代码时,传递的是什么参数名(stuNos),在Mapper.xml中,必须用list代替该数组。
<!--将多个元素放入到集合中 list<Integer> stuNos = {1,3,5}-->
<select id="queryStudentWithList" parameterType="list" resultType="student">
select * from student
<where>
<if test="list != null and list.size > 0">
<foreach collection="list" open=" and stuno in(" close=")" item="stuNo" separator=",">
#{stuNo}
</foreach>
</if>
</where>
</select>
List<Student> queryStudentWithList(List<Integer> stuNOs);
//查询指定学号对应的学生使用List集合
public static void queryStudentWithList() throws Exception{
//Connection - SqlSession操作myBatis
//config.xml -> reader
Reader reader = Resources.getResourceAsReader("conf.xml");
//reader ->SqlSession
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sessionFactory.openSession();
StudentMapper mapper = session.getMapper(StudentMapper.class);
List<Integer> stuNos = new ArrayList<>();
stuNos.add(1);
stuNos.add(3);
stuNos.add(5);
List<Student> student = mapper.queryStudentWithList(stuNos);
System.out.println(student);
session.close();
//运行结果
[Student{stuNo=1, stuName='lxs', stuAge=0, graName='null', stuSex=false}
, Student{stuNo=3, stuName='ls', stuAge=0, graName='null', stuSex=false}
, Student{stuNo=5, stuName='ww', stuAge=20, graName='null', stuSex=false}
]
4.对象数组
Student[ ] students = {student0,student1,student2}; 每个student包含一个学号属性
- 注意的几点 :parameterType="Object[]"
<!--将多个元素放入到对象数组中 Student[ ] students = {student0,student1,student2}-->
<select id="queryStudentWithObjectArray" parameterType="Object[]" resultType="student">
select * from student
<where>
<if test="array != null and array.length > 0">
<foreach collection="array" open=" and stuno in (" close=")" item="student" separator=",">
#{student.stuNo}
</foreach>
</if>
</where>
</select>
List<Student> queryStudentWithObjectArray(Student[] students);
//查询指定学号对应的学生使用对象数组
public static void queryStudentWithObjectArray() throws Exception{
//Connection - SqlSession操作myBatis
//config.xml -> reader
Reader reader = Resources.getResourceAsReader("conf.xml");
//reader ->SqlSession
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sessionFactory.openSession();
StudentMapper mapper = session.getMapper(StudentMapper.class);
Student student1 = new Student();
student1.setStuNo(1);
Student student2 = new Student();
student2.setStuNo(2);
Student student3 = new Student();
student3.setStuNo(3);
Student[] stus = new Student[]{student1,student2,student3};
List<Student> student = mapper.queryStudentWithObjectArray(stus);
System.out.println(student);
session.close();
}
//运行结果为
[Student{stuNo=1, stuName='lxs', stuAge=0, graName='null', stuSex=false}
, Student{stuNo=2, stuName='zs', stuAge=0, graName='null', stuSex=false}
, Student{stuNo=3, stuName='ls', stuAge=0, graName='null', stuSex=false}
]
9.SQL片段:
java:方法
数据库:存储过程、存储函数
Mybatis:SQL片段
a.提取相同的代码
b.引用
<sql id="ObjectArrayStunos">
<where>
<if test="array != null and array.length > 0">
<foreach collection="array" open=" and stuno in (" close=")" item="student" separator=",">
#{student.stuNo}
</foreach>
</if>
</where>
</sql>
<!--将多个元素放入到对象数组中 Student[ ] students = {student0,student1,student2}-->
<select id="queryStudentWithObjectArray" parameterType="Object[]" resultType="student">
select * from student
<!-- 如果sql片段和引用处不在同一个文件中,则需要在refid 引用时加上namespace:namespace.id
<include refid="com.itt.mapper.abcMapper.ObjectArrayStunos"></include>
-->
<include refid="ObjectArrayStunos"></include>
</select>
10.关联查询
10.1 一对一:
a.业务扩展类
* 核心:用resultType指定类的属性包含 多表查询的所有字段
* 思路:
代码:
//StudentMapper.xml
<select id="queryStudentByNoWithOO" parameterType="int" resultType="StudentBusiness">
select s.*,c.* from student s inner join studentcard c
on s.cardid = c.cardid
where s.stuno = #{stuNo}
</select>
//StudentMapper
StudentBusiness queryStudentByNoWithOO(int stuno);
//Test
public static void queryStudentByNoWithOO() throws Exception{
//Connection - SqlSession操作myBatis
//config.xml -> reader
Reader reader = Resources.getResourceAsReader("conf.xml");
//reader ->SqlSession
SqlSessionFactory sessionFactory=new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sessionFactory.openSession();
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
StudentBusiness studentBusiness = studentMapper.queryStudentByNoWithOO(2);
System.out.println(studentBusiness);
session.close();
}
-
结果
b.resultMap
- 1通过 属性成员 将2个类建立起联系
- 2
//StudentMapper.xml
<select id="queryStudentByNoWithOO2" parameterType="int" resultMap="student_card_map">
select s.*,c.* from student s inner join studentcard c
on s.cardid = c.cardid
where s.stuno = #{stuNo}
</select>
<resultMap id="student_card_map" type="student">
<!--学生的信息-->
<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>
//StudentMapper
Student queryStudentByNoWithOO2(int stuno);
//Test
//一对一映射使用resultMap方式实现
public static void queryStudentByNoWithOO2() throws Exception{
//Connection - SqlSession操作myBatis
//config.xml -> reader
Reader reader = Resources.getResourceAsReader("conf.xml");
//reader ->SqlSession
SqlSessionFactory sessionFactory=new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sessionFactory.openSession();
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
Student student = studentMapper.queryStudentByNoWithOO2(1);
System.out.println(student);
session.close();
}
注意:
一对一:association
一对多:collection
-
结果:
10.2 一对多:
表:student studentclass(关联:classid)
-
类:student studentClass(关联:List<Student> students)
注意:
在描述students属性类型:javaType=" "在描述该属性的元素类型为:ofType=" "
//一对多 <collection property="students" ofType="student"> </collection> List<Student> students;
代码实现过程
package com.itt.entity;
public class Student {
//只用这三个字段
private int stuNo;
private String stuName;
private int stuAge;
private String graName;
private boolean stuSex;
private StudentCard card;
public StudentCard getCard() {
return card;
}
public void setCard(StudentCard card) {
this.card = card;
}
// private Address address;
// public Address getAddress() {
// return address;
// }
// public void setAddress(Address address) {
// this.address = address;
// }
public int getStuNo() {
return stuNo;
}
public void setStuNo(int stuNo) {
this.stuNo = stuNo;
}
public boolean isStuSex() {
return stuSex;
}
public void setStuSex(boolean stuSex) {
this.stuSex = stuSex;
}
public String getStuName() {
return stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
public int getStuAge() {
return stuAge;
}
public void setStuAge(int stuAge) {
this.stuAge = stuAge;
}
public String getGraName() {
return graName;
}
public void setGraName(String graName) {
this.graName = graName;
}
public Student() {
}
public Student(int stuNo, String stuName, int stuAge, String graName) {
this.stuNo = stuNo;
this.stuName = stuName;
this.stuAge = stuAge;
this.graName = graName;
}
public Student(int stuNo, String stuName, int stuAge, String graName, boolean stuSex) {
this.stuNo = stuNo;
this.stuName = stuName;
this.stuAge = stuAge;
this.graName = graName;
this.stuSex = stuSex;
}
@Override
public String toString() {
return "stuNo"+"="+this.stuNo+","+"stuName"+"="+this.stuName+","+"stuAge"+"="+
this.stuAge+","+"CardId"+"="+this.card.getCardId()+","+"CardInfo"+
"="+this.card.getCardInfo();
}
}
package com.itt.entity;
import java.util.List;
public class StudentClass {
private int classId;
private String className;
//增加学生属性(通过该字段让Student类和StudentClass类建立起关联)
List<Student> students;
public List<Student> getStudents() {
return students;
}
public void setStudents(List<Student> students) {
this.students = students;
}
public int getClassId() {
return classId;
}
public void setClassId(int classId) {
this.classId = classId;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
}
//StudentMapper.xml
<!--建立一对多联系-->
<select id="queryClassAndStudents" parameterType="int" resultMap="class_student_map" >
<!--查询g1班的班级信息,和g1班的所有学生信息-->
select c.*,s.* from student s
inner join studentclass c
on s.classid = c.classid
where c.classid = #{classId}
</select>
<!--类-表的对应关系-->
<resultMap id="class_student_map" type="StudentClass">
<!--因为type的主类是班级,因此先配置班级的信息-->
<id property="classId" column="classId"/>
<result property="className" column="className"/>
<!--配置成员属性学生,一对多-->
<collection property="students" ofType="student">
<id property="stuNo" column="stuNo"/>
<result property="stuName" column="stuName"/>
<result property="stuAge" column="stuAge"/>
</collection>
</resultMap>
//studentMapper
StudentClass queryClassAndStudents(int ClassId);
//一对一映射使用resultMap方式实现
public static void queryClassAndStudents() throws Exception{
//Connection - SqlSession操作myBatis
//config.xml -> reader
Reader reader = Resources.getResourceAsReader("conf.xml");
//reader ->SqlSession
SqlSessionFactory sessionFactory=new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sessionFactory.openSession();
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
//班级
StudentClass studentClass = studentMapper.queryClassAndStudents(1);
System.out.println("ClassId="+studentClass.getClassId()+","+"ClassName="+studentClass.getClassName());
List<Student> students = studentClass.getStudents();
for (Student student : students) {
System.out.println(student.getStuNo()+","+student.getStuName()+
","+student.getStuAge());
}
session.close();
}
11.Log4j: 使用Maven
- 添加依赖:
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
- 开启日志,conf.xml
<settings>
<!--开启日志,并指定使用的具体日志-->
<setting name="LogImpl" value="LOG4J"/>
</settings>
如果不指定,Mybatis就会根据以下顺序寻找日志
-
编写配置日志输出文件
lgo4j.properties
- 内容
log4j.rootLogger=DEBUG,Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
日志级别:
DEBUG < INFO < WARN < ERROR
如果设置为info,则只显示info 及以上级别的信息;
建议:在开发时设置debug,在运行时设置为info或以上。
可以通过日志信息,详细的阅读mybatis执行情况(观察mybatis实际执行sql的语句以及参数和结果)
12.延迟加载
12.1 一对一:学生,学生证
- mybatis中使用延迟加载,需要先配置:
<settings>
<!--开启日志,并指定使用的具体日志-->
<setting name="logImpl" value="LOG4J"/>
<!--开启延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--关闭立即加载-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
注意:
如果添加了mapper.xml文件则我们需要在主配置文件conf.xml中添加新增的mapper.xml文件。
- 通过debug可以发现,如果程序只需要学生,则只向数据库发送了查询学生的SQL;
-
当我们后续 需要用到学生证的时候,再第二次发送 查询学生证的SQL
12.2 一对多
-
和一对一的延迟加载配置相同
延迟加载的步骤:先查班级,按需查询学生
开启延迟加载conf.xml配置settings
配置mapper.xml
2.1 写2个Mappper文件:
- 班级mapper.xml
<!--建立一对多联系:带延迟加载-->
<select id="queryClassAll" resultMap="class_student_LazyLoad" resultType="StudentClass" >
<!--查询g1班的班级信息,和g1班的所有学生信息-->
select * from studentclass
</select>
<!--类-表的对应关系-->
<resultMap id="class_student_LazyLoad" type="studentClass">
<!--因为type的主类是班级,因此先配置班级的信息-->
<id property="classId" column="classId"/>
<result property="className" column="className"/>
<!--配置成员属性学生,一对多-->
<collection property="students" select="com.itt.mapper.StudentCardMapper.queryCardByClassId"
column="classid">
<!--<id property="stuNo" column="stuNo"/>-->
<!--<result property="stuName" column="stuName"/>-->
<!--<result property="stuAge" column="stuAge"/>-->
</collection>
</resultMap
即查询学生的sql是通过select 属性指定,并且通过column指定外键
学生mapper.xml
<!--再根据班级查询学生-->
<select id="queryCardByClassId" parameterType="int" resultType="student">
select * from student where classid = #{classid}
<!-- com.itt.mapper.StudentCardMapper.queryCardByClassId-->
</select>
13.查询缓存
13.1一级缓存:同一个SqlSession对象
-
MyBatis默认开启一级缓存,如果用同样的SqlSession查询相同的数据,则只会在第一次查询时向数据库发送sql语句,并将查询的结果放入到SqlSession(作为缓存存在);后续再次查询该同样的对象时,则直接从缓存中查询该对象即可(即省略了数据库的访问)。
13.2二级缓存
- MyBatis默认情况下没有开启二级缓存,需要手工打开。
-
config.xml
<setting name="cacheEnabled" value="true"/>
-
在具体的mapper.xml中声明开启(studentMappper.xml)
<mapper namespace="com.itt.mapper.StudentMapper"> <!--声明此namespace开启二级缓存--> <cache/>
-
根据 异常提示:NotSerializableException可知,MyBatis是将对象放入到硬盘文件中。
------->序列化:内存----->硬盘
- 准备缓存的对象,必须实现了序列化接口(如果开启了缓存 namespace="com.itt.mapper.StudentMapper"),可知序列化对象为Student,因此要将Student对象序列化(序列化Stuent类,以及Student的级联属性和父类)
注意:触发将对象写入二级缓存的时机:SqlSession对象的close()方法。
-
Mybatis自带二级缓存:【同一个namespace】生成的mapper对象
-
回顾:namespace的值,就是接口的全类名(包名.类名),通过接口可以生成动态代理对象(studentMapper对象)
- ---------->namespace决定了studentMapper对象的产生
结论:只要产生的xxxMapper对象来自于同一个namespace,则这些对象共享二级缓存
注意:二级缓存的范围是同一个namespace,如果有多个xxxMapper.xml的namespace值相同,则通过这些xxxMapper.xml产生的xxxMapper对象,仍然共享二级缓存。
-
禁用:select标签中userCache="false"
-
清理:
-
执行commit();一级二级一样都使用这个(执行增删改时,会清理缓存;设计的原因是为了防止脏数据的产生)
在二级缓存 中,commit()不能是查询自身的commit。
commit会清理一级和二级缓存:但是清理二级缓存时,不能是查询自身的commit;
在select标签中增加属性:flushCache="true"
-
-
第三方提供的二级缓存:
echcache、memcache
要想整合三方提供的二级缓存(或者自定义缓存),必须实现Cache接口,该接口的默认实现类为PerpetualCache
整合echcache二级缓存:
1. 导入Maven依赖:
<!--mybatis使用ehacche--> <dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.0.3</version> </dependency> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache-core</artifactId> <version>2.6.10</version> </dependency> <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.25</version> </dependency>
2. 编写ehcache配置文件Ehcache.xml
```java <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd"> <diskStore path="F:\Ehcache"/> <defaultCache maxElementsInMemory="1000" maxElementsOnDisk="1000000" eternal="false" overflowToDisk="false" timeToIdleSeconds="100" timeToLiveSeconds="100" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> </defaultCache> </ehcache>
3. 开启EhCache二级缓存
在xxxMapper.xml中开启 ```java <cache type="org.mybatis.caches.ehcache.EhcacheCache"> <!--自定义Ehcache中的全局参数--> <property name="" value=""/> </cache> ```
14.逆向工程
-
表、类、接口、mapper.xml四者密切相关,因此,当知道一个的时候,其他三个应该可以自动生成。
表-------->其他三个
-
实现步骤:
- 添加Maven依赖
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
-