介绍

这几年来注解开发越来越流行,Mybatis也可以使用注解开发方式,这样我们就可以减少编写Mapper

映射文件了。我们先围绕一些基本的CRUD来学习,再学习复杂映射多表操作。

@Insert:实现新增

@Update:实现更新

@Delete:实现删除

@Select:实现查询

@Result:实现结果集封装

@Results:可以与@Result 一起使用,封装多个结果集

@One:实现一对一结果集封装

@Many:实现一对多结果集封装

初始化工程

导包

点击下载jar包

导配置

在src或resources目录下新建jdbc.properties

1
2
3
4
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://192.168.254.128:3306/mybatis03?characterEncoding=utf-8
jdbc.username=root
jdbc.password=123456

在src或resources目录下新建log4j.properties

1
2
3
4
5
6
7
8
### 设置###
log4j.rootLogger = debug,stdout

### 输出信息到控制抬 ###
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 = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

在src或resources目录下新建mybatis-config.xml核心配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?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="jdbc.properties"></properties>
<settings>
<setting name="logImpl" value="log4j"/>
</settings>

<typeAliases>
<!--如果类比较多,可以指定包,通过扫包的方式给包中的所有类起别名-->
<package name="cn.itcast.domain"/>
</typeAliases>

<!--数据源环境-->
<environments default="developement">
<environment id="developement">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>


</configuration>

使用注解增删改查

环境准备

建库建表

1
2
3
4
5
6
7
8
9
10
CREATE DATABASE `mybatis03`;
USE `mybatis03`;
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL,
`age` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
insert into `student`(`id`,`name`,`age`) values (1,'张三',23),(2,'李四',24),(3,'王五',25);

创建Student实体类

在cn.itcast.domain包下创建Student类

1
2
3
4
5
6
7
8
9
10
11
12
package cn.itcast.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
private Integer id;
private String name;
private Integer age;
}

创建StudentDao接口

在cn.itcast.dao包下创建StudentDao接口

1
2
3
package cn.itcast.dao;
public interface StudentDao {
}

配置映射关系

在mybatis-config.xml中配置映射关系,因为现在映射关系通过注解体现,所以应该扫描包含注解的接口

1
2
3
4
<mappers>
<!--扫描dao包下的所有接口中的注解-->
<package name="cn.itcast.dao"/>
</mappers>

查询演示

第一步:在StudentDao接口中添加查询方法

1
List<Student> findAll();

第二步:在查询方法上添加@select注解

1
2
@Select("select * from student")
List<Student> findAll();

第三步:编写测试方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
public void testFindAll()throws Exception{
/*1.读取配置文件*/
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
/*2.解析配置文件*/
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
/*3.获取操作数据库的对象*/
SqlSession sqlSession = sqlSessionFactory.openSession();
/*4.获取代理对象*/
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
/*5.调用代理对象的方法完成查询*/
List<Student> students = studentDao.findAll();
/*6.释放资源*/
sqlSession.close();

//测试查询结果
System.out.println(students);
}

形参中有多个值,在SQL中怎么获取?

1
2
3
4
5
//@Select("select * from student where id = #{arg0} and name = #{arg1}")
//@Select("select * from student where id = #{param1} and name = #{param2}")

@Select("select * from student where id = #{ID} and name = #{NAME}")
Student findByIdAndName(@Param("ID") Integer id, @Param("NAME") String name);//使用@Param给形参起名字,在SQL中使用#{名字} 获取形参的值

新增演示

第一步:在StudentDao接口中添加新增方法

1
int insert(Student student);

第二步:在新增方法上添加@Insert注解

1
2
3
/*#{name},#{age}对应的是形参Student对象的属性的名字,表示把name,age属性的值取出来*/
@Insert("insert into student(name,age) values(#{name},#{age})")
int insert(Student student);

第三步:编写测试方法,注意默认需要手动提交事务,必须手动commit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Test
public void testInsert()throws Exception{
/*1.读取配置文件*/
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
/*2.解析配置文件*/
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
/*3.获取操作数据库的对象*/
SqlSession sqlSession = sqlSessionFactory.openSession();
/*4.获取代理对象*/
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
/*5.调用代理对象的方法完成新增*/
Student stu = new Student(null, "李克勤", 33);
int count = studentDao.insert(stu);
sqlSession.commit();
/*6.释放资源*/
sqlSession.close();

//测试查询结果
System.out.println("共"+count+"行记录受到影响");
}

修改演示

第一步:在StudentDao接口中添加更新方法

1
int update(Student student);

第二步:在更新方法上添加@Update注解

1
2
3
/*#{name},#{age},#{id}对应的是形参Student对象的属性的名字,表示把name,age,id属性的值取出来*/
@Update("update student set name=#{name},age=#{age} where id=#{id}")
int update(Student student);

第三步:编写测试方法,注意默认需要手动提交事务,必须手动commit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Test
public void testUpdate()throws Exception{
/*1.读取配置文件*/
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
/*2.解析配置文件*/
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
/*3.获取操作数据库的对象*/
SqlSession sqlSession = sqlSessionFactory.openSession();
/*4.获取代理对象*/
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
/*5.调用代理对象的方法完成更新*/
Student stu = new Student(6, "李克清", 33);
int count = studentDao.update(stu);
sqlSession.commit();
/*6.释放资源*/
sqlSession.close();

//测试查询结果
System.out.println("共"+count+"行记录受到影响");
}

删除演示

第一步:在StudentDao接口中添加删除方法

1
int delete(Integer id);

第二步:在删除方法上添加@Delte注解

1
2
3
/*#{id}对应的是形参Student对象的属性的名字,表示把id属性的值取出来*/
@Delete("delete from student where id = #{id}")
int delete(Integer id);

第三步:编写测试方法,注意默认需要手动提交事务,必须手动commit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test
public void testDelete()throws Exception{
/*1.读取配置文件*/
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
/*2.解析配置文件*/
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
/*3.获取操作数据库的对象*/
SqlSession sqlSession = sqlSessionFactory.openSession();
/*4.获取代理对象*/
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
/*5.调用代理对象的方法完成删除*/
int count = studentDao.delete(1);
sqlSession.commit();
/*6.释放资源*/
sqlSession.close();

//测试查询结果
System.out.println("共"+count+"行记录受到影响");
}

#{}与${}的区别

以下两种写法的区别

1
2
@Insert("insert into student (name,age) values('${name}',${age})")
@Insert("insert into student (name,age) values(#{name},#{age})")

$底层使用Statement,最终会拼接出一个完整的SQL, insert into student (name,age) values(‘严朝阳’,12)

#底层使用PrepareStatement ,最终拼接的SQL带占位符insert into student (name,age) values(?,?)

PrepareStatement 效率高,防止SQL注入,书写更加方便。

使用注解多表查询

一对一

表关系

一对一关系中,表关系由任意一方维护,以人和身份证为例,一个人对应一个身份证,一个身份证对应一个人。本案例中由身份证表维护表关系

1589801843745

需求

查询所有的身份证,并且要将身份证对应的人也查询出来

实现步骤

第一步:建表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
DROP TABLE IF EXISTS `person`;
CREATE TABLE `person` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(30) NOT NULL COMMENT '姓名',
`age` int(11) NOT NULL COMMENT '年龄',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COMMENT='用户表';
insert into `person`(`id`,`name`,`age`) values (1,'陈乔恩',39),(2,'钟汉良',44),(3,'林志颖',44),(4,'吴奇隆',48);
DROP TABLE IF EXISTS `card`;
CREATE TABLE `card` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`number` varchar(30) NOT NULL COMMENT '身份证号',
`pid` int(11) NOT NULL COMMENT '所属用户',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COMMENT='身份证表';
insert into `card`(`id`,`number`,`pid`) values (1,' 110101199003079008',1),(2,'110101199003079331',2),(3,' 11010119900307299X',3),(4,' 110101199003070791',4);

第二步:创建实体类

Person 人类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package cn.itcast.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private Integer id;
private String name;
private Integer age;
}

Card 身份证类,因为需求是查询身份证,并且查询对应的人,所以应该是Card实体类中存放Perosn属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package cn.itcast.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Card {
private Integer id;
private String number;
private Person person;
}

第三步:创建dao接口(核心)

思考 使用2条SQL语句,查询id=1的身份证信息以及对应的人怎么查

1
2
3
4
-- 第一步 查询id=1的身份证信息,找到对应的pid,值为1
SELECT * FROM card WHERE id = 1;
-- 第二步 根据pid的值查询对应的人
SELECT * FROM person WHERE id=1;

1589902183333

接下来通过编写两个Dao分别完成以上两个查询语句

1.Person实体类比较简单,所以先编写PersonDao中的代码,根据id查询详细信息

1
2
3
4
5
6
7
8
9
10
package cn.itcast.dao;

import cn.itcast.domain.Person;
import org.apache.ibatis.annotations.Select;

public interface PersonDao {
/*#{id}对应的是形参Integer的值,名字可以任意*/
@Select("select * from person where id = #{id}")
Person findById(Integer id);
}

2.编写CardDao

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package cn.itcast.dao;

import cn.itcast.domain.Card;
import cn.itcast.domain.Person;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface CardDao {
@Select("select * from card")
@Results({
@Result(column = "id", property = "id"), //将结果集中的id字段的值赋值给id属性
@Result(column = "number", property = "number"),//将结果集中的number字段的值赋值给number属性
@Result(
column = "pid",
property = "person",
javaType = Person.class,
one = @One(select = "cn.itcast.dao.PersonDao.findById")
),//首先结果集中的pid字段的值传给findeById方法,然后将查询结果封装成Person类的对象,最后赋值给person属性

})
List<Card> findAll();
}

第四步:编写测试方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
public void testFindAll()throws Exception{
/*1.读取配置文件*/
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
/*2.解析配置文件*/
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
/*3.获取操作数据库的对象*/
SqlSession sqlSession = sqlSessionFactory.openSession();
/*4.获取代理对象*/
CardDao cardDao = sqlSession.getMapper(CardDao.class);
/*5.调用代理对象的方法完成查询*/
List<Card> cards = cardDao.findAll();
/*6.释放资源*/
sqlSession.close();

//测试查询结果
System.out.println(cards);
}

一对多

表关系

一对多关系中,表关系由多的一方维护,以班级和学生为例,一个班级可以有多个学生,学生表应该维护表关系。

1589806452924

需求

查询所有班级信息,并且要将班级对应的学生信息也同时查询出来

实现步骤

第一步:建表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
DROP TABLE IF EXISTS `classes`;
CREATE TABLE `classes` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(20) DEFAULT NULL COMMENT '班级名称',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='班级表';
INSERT INTO `classes`(`id`,`name`) VALUES (1,'黑马一班'),(2,'黑马二班');

DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(24) NOT NULL,
`age` INT(11) NOT NULL,
`cid` INT(11) NOT NULL COMMENT '所属班级',
PRIMARY KEY (`id`),
KEY `cid` (`cid`)
) ENGINE=INNODB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8;

INSERT INTO `student`(`id`,`name`,`age`,`cid`) VALUES (8,'王力宏',23,1),(9,'张韶涵',22,1),(10,'张晋',24,1),(11,'罗晋',25,1),(12,'唐僧',12,2),(13,'孙悟空',22,2),(14,'猪八戒',21,2);

第二步:创建实体类

Student学生类,和上文提到的一样。有就不用创建了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package cn.itcast.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private Integer id;
private String name;
private Integer age;
}

Classes班级类,因为需求是查询班级,以及对应的学生,所以应该是班级类中定义List表示一堆学生

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package cn.itcast.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Classes {
private Integer id;
private String name;
private List<Student> students;
}

第三步:创建dao接口(核心)

思考 使用2条SQL语句,查询id=1的班级信息以及对应的学生怎么查

1
2
3
4
-- 第一步 查询ID=1的班级信息
SELECT * FROM classes WHERE id = 1;
-- 第二步 查询对应的学生信息
SELECT * FROM student WHERE cid = 1;

1589902058933

接下来通过编写两个Dao分别完成以上两个查询语句

1.Student实体类比较简单,所以先编写StudentDao中的代码,根据cid查询学生信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package cn.itcast.dao;

import cn.itcast.domain.Student;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import java.util.List;

public interface StudentDao {
/*#{id}对应的是形参Integer的值,名字可以任意*/
@Select("SELECT * FROM student WHERE cid = #{cid};")
List<Student> findByCid(Integer cid);
}

2.编写ClassesDao

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package cn.itcast.dao;

import cn.itcast.domain.Classes;
import org.apache.ibatis.annotations.Many;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface ClassesDao {
@Select("SELECT * FROM classes")
@Results({
@Result(column = "id", property = "id"),//将结果集中的id字段的值赋值给id属性
@Result(column = "name", property = "name"),//将结果集中的name字段的值赋值给name属性
@Result(
column = "id",
property = "students",
javaType = List.class,
many = @Many(select = "cn.itcast.dao.StudentDao.findByCid")
)//首先结果集中的id字段的值传给findByCid方法,然后将查询结果封装成List类的对象,最后赋值给students属性

})
List<Classes> findAll();
}

第四步:编写测试方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
public void testFindAll()throws Exception{
/*1.读取配置文件*/
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
/*2.解析配置文件*/
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
/*3.获取操作数据库的对象*/
SqlSession sqlSession = sqlSessionFactory.openSession();
/*4.获取代理对象*/
ClassesDao classesDao = sqlSession.getMapper(ClassesDao.class);
/*5.调用代理对象的方法完成查询*/
List<Classes> classes = classesDao.findAll();
/*6.释放资源*/
sqlSession.close();

//测试查询结果
System.out.println(classes);
}

多对多

表关系

多对多关系中表关系由中间表维护,以学生和课程为例,一个学生对应多们课程,一个课程同时对应多个学生

1589814558313

需求

查询所有的学生,并且要将学生对应的课程也查询出来

实现步骤

第一步: 建表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
DROP TABLE IF EXISTS `classes`;
CREATE TABLE `classes` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(20) DEFAULT NULL COMMENT '班级名称',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='班级表';
INSERT INTO `classes`(`id`,`name`) VALUES (1,'黑马一班'),(2,'黑马二班');

DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(24) NOT NULL,
`age` INT(11) NOT NULL,
`cid` INT(11) NOT NULL COMMENT '所属班级',
PRIMARY KEY (`id`),
KEY `cid` (`cid`)
) ENGINE=INNODB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8;

INSERT INTO `student`(`id`,`name`,`age`,`cid`) VALUES (8,'王力宏',23,1),(9,'张韶涵',22,1),(10,'张晋',24,1),(11,'罗晋',25,1),(12,'唐僧',12,2),(13,'孙悟空',22,2),(14,'猪八戒',21,2);

DROP TABLE IF EXISTS `course`;
CREATE TABLE `course` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(30) NOT NULL COMMENT '课程名',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='课程表';
INSERT INTO `course`(`id`,`name`) VALUES (1,'语文'),(2,'数学');

DROP TABLE IF EXISTS `stu_cr`;
CREATE TABLE `stu_cr` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`sid` INT(11) NOT NULL,
`cid` INT(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;
INSERT INTO `stu_cr`(`id`,`sid`,`cid`) VALUES (1,8,1),(2,8,2),(3,9,1),(4,10,2),(5,13,2),(6,13,1),(7,14,1),(8,11,2),(9,12,1),(10,12,2);

第二步:创建实体类

Course 课程类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package cn.itcast.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Course {
/**课程ID*/
private Integer id;
/**课程名*/
private String name;
}

Student 学生类,学生类之前已经定义过了,只需要添加新的course属性即可。因为需求是查询学生信息,以及对应的课程,所以应该在学生类中定义List,表示一堆课程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package cn.itcast.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private Integer id;
private String name;
private Integer age;
private List<Course> courses;
}

第三步:创建Dao接口(核心)

思考,使用2条SQL如何查询id=8的学生的信息以及对应的课程信息

1
2
3
4
-- 第一步 查询id=8的学生信息
SELECT * FROM student WHERE id = 8;
-- 第二步 根据学生的id查询对应的课程信息
SELECT * FROM course c INNER JOIN stu_cr sc ON c.id = sc.cid AND sc.sid=8;

1589901385014

接下来通过编写两个Dao分别完成以上两个查询语句

1.Course实体类比较简单,所以先编写CourseDao中的代码,根据sid查询课程信息

1
2
3
4
5
6
7
8
9
10
11
12
package cn.itcast.dao;

import cn.itcast.domain.Course;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface CourseDao {
/*#{sid}对应的是形参Integer的值,名字可以任意*/
@Select("SELECT * FROM course c INNER JOIN stu_cr sc ON c.id = sc.cid AND sc.sid=#{sid};")
List<Course> findBySid(Integer sid);
}

2.编写StudentDao

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package cn.itcast.dao;
import cn.itcast.domain.Student;
import org.apache.ibatis.annotations.*;
import java.util.List;

public interface StudentDao {
@Select("select * from student")
@Results({
@Result(column = "id", property = "id"),//将结果集中的id字段的值赋值给id属性
@Result(column = "name", property = "name"),//将结果集中的name字段的值赋值给name属性
@Result(column = "age", property = "age"),//将结果集中的age字段的值赋值age属性
@Result(
column = "id",
property = "courses",
javaType = List.class,
many = @Many(select = "cn.itcast.dao.CourseDao.findBySid")
)//首先结果集中的id字段的值传给findBySid方法,然后将查询结果封装成List类的对象,最后赋值给courses属性
})
List<Student> findAll();
}

第四步:编写测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
public void testFindAll()throws Exception{
/*1.读取配置文件*/
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
/*2.解析配置文件*/
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
/*3.获取操作数据库的对象*/
SqlSession sqlSession = sqlSessionFactory.openSession();
/*4.获取代理对象*/
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
/*5.调用代理对象的方法完成查询*/
List<Student> students = studentDao.findAll();
/*6.释放资源*/
sqlSession.close();

//测试查询结果
System.out.println(students);
}

注解版本的多表查询分析过程

1.需求分析,分析主要查询哪个表中的数据,顺便要查询出的表的名字

2.编写实体类,通过需求分析,可以确定实体类和实体类之间的关系,是谁包含谁

3.编写dao,先编写SQL语句,第一条SQL 肯定是 select * from 表 , 第二条 select * from 表 where x= 主表中查询出的某个数据

4.分别编写两个Dao,先编写第二个SQL对应的DAO,然后再编写第一个SQL对应的DAO

SQL功能类

介绍

我们在使用注解开发的时候有一些不足

  • sql语句中的关键字可能不小心写错。
  • 不方便执行动态SQL语句。

那么SQL功能类是个不错的选择。

使用方式分为2步

  • 第一步:使用SQL对象生成要执行的sql语句
  • 第二步:使用@SelectProvider替换之前的@Select,使用@InsertProvider替换之前的@Insert….

环境搭建

创建表

1
2
3
4
5
6
7
8
9
10
11
CREATE TABLE `user`(
uid INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(100) NOT NULL,
PASSWORD VARCHAR(50) NOT NULL,
email VARCHAR(50),
birthday DATE
);
INSERT INTO `user` (NAME,PASSWORD,email,birthday) VALUES("尼古拉斯赵四",'123456','nglszs@qq.com','1888-09-02');
INSERT INTO `user` (NAME,PASSWORD,email,birthday) VALUES("尼古拉斯赵五",'123456','nglszw@qq.com','1889-09-03');
INSERT INTO `user` (NAME,PASSWORD,email,birthday) VALUES("尼古拉斯赵六",'123456','nglszl@qq.com','1890-09-04');
INSERT INTO `user` (NAME,PASSWORD,email,birthday) VALUES("尼古拉斯赵七",'123456','nglszq@qq.com','1891-09-05');

创建实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package cn.itcast.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer uid;
private String name;
private String password;
private String email;
private String birthday;//数据库中是Date类型,java中可以是Date,也可以是String类型
}

定义UserSQL功能类

在cn.itcast.sql 包下新建UserSQL功能类

1
2
3
4
5
6
7
package cn.itcast.sql;

import org.apache.ibatis.jdbc.SQL;

public class UserSQL {

}

定义UserDao接口

在cn.itcast.dao包下新建UserDao接口

1
2
3
4
5
6
7
8
9
10
package cn.itcast.dao;

import cn.itcast.domain.User;
import cn.itcast.sql.UserSQL;
import org.apache.ibatis.annotations.SelectProvider;

import java.util.List;

public interface UserDao {
}

查询@SelectProvider

普通查询

1.在UserSQL功能类中添加生成SQL语句的方法

1
2
3
4
5
6
7
8
9
/**功能:生成查询全部的SQL*/
public String findAll(){
return new SQL(){
{
SELECT("*");
FROM("`user`");
}
}.toString();
}

2.在UserDao接口中添加findAll方法

1
2
@SelectProvider(type = UserSQL.class, method = "findAll")
List<User> findAll();

3.测试方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
public void testFindAll()throws Exception{
/*1.读取配置文件*/
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
/*2.解析配置文件*/
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
/*3.获取操作数据库的对象*/
SqlSession sqlSession = sqlSessionFactory.openSession();
/*4.获取代理对象*/
UserDao userDao = sqlSession.getMapper(UserDao.class);
/*5.调用代理对象的方法完成查询*/
List<User> users = userDao.findAll();
/*6.释放资源*/
sqlSession.close();

//测试查询结果
System.out.println(users);
}

动态SQL查询

1.在UserSQL功能类中添加生成SQL语句的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**功能:根据传入的条件动态拼接查询SQL*/
/*#{}表示获取形参User对象的属性值,所以#{}中写的是属性名*/
public String findByCondition(User user){
return new SQL(){
{
SELECT("*");
FROM("`user`");
if(user!=null){
if (user.getUid() != null){
WHERE("uid = #{uid}");
}
if (user.getName() != null){
WHERE("name = #{name}");
}
if (user.getPassword() != null){
WHERE("password = #{password}");
}
if (user.getEmail() != null){
WHERE("email = #{email}");
}
if (user.getBirthday() != null){
WHERE("birthday = #{birthday}");
}
}
}
}.toString();
}

2.在UserDao接口中添加findAll方法

1
2
@SelectProvider(type = UserSQL.class,method = "findByCondition")
List<User> findByCondition(User user);

3.测试方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Test
public void testFindByCondition()throws Exception{
/*1.读取配置文件*/
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
/*2.解析配置文件*/
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
/*3.获取操作数据库的对象*/
SqlSession sqlSession = sqlSessionFactory.openSession();
/*4.获取代理对象*/
UserDao userDao = sqlSession.getMapper(UserDao.class);
/*5.调用代理对象的方法完成查询*/
User user = new User();
user.setUid(1);
List<User> users = userDao.findByCondition(user);
/*6.释放资源*/
sqlSession.close();

//测试查询结果
System.out.println(users);
}

新增@InsertProvider

1.在UserSQL功能类中添加生成SQL语句的方法

1
2
3
4
5
6
7
8
9
10
/**功能:插入*/
/*#{}表示获取形参User对象的属性值,所以#{}中写的是属性名*/
public String insert(User user){
return new SQL(){
{
INSERT_INTO("`user`");
VALUES("name,password,email,birthday","#{name},#{password},#{email},#{birthday}");
}
}.toString();
}

2.在UserDao接口中添加insert方法

1
2
@InsertProvider(type = UserSQL.class,method = "insert")
int insert(User user);

3.测试方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Test
public void testInsert()throws Exception{
/*1.读取配置文件*/
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
/*2.解析配置文件*/
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
/*3.获取操作数据库的对象*/
SqlSession sqlSession = sqlSessionFactory.openSession();
/*4.获取代理对象*/
UserDao userDao = sqlSession.getMapper(UserDao.class);
/*5.调用代理对象的方法完成查询*/
User user = new User();
user.setName("猪悟能");
user.setPassword("123456");
user.setBirthday("1800-09-09");
user.setEmail("zhuwuneng@qq.com");
int count = userDao.insert(user);

/*6.提交事物*/
sqlSession.commit();
/*6.释放资源*/
sqlSession.close();

//测试查询结果
System.out.println("共"+count+"行受到影响");
}

更新@UpdateProvider

1.在UserSQL功能类中添加生成SQL语句的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**生成根据ID更新的SQL*/
/*#{}表示获取形参User对象的属性值,所以#{}中写的是属性名*/
public String update(User user) {
return new SQL() {
{

UPDATE("`user`");
if (user.getName() != null) {
SET("name = #{name}");
}
if (user.getPassword() != null) {
SET("password = #{password}");
}
if (user.getEmail() != null) {
SET("email = #{email}");
}
if (user.getBirthday() != null) {
SET("birthday = #{birthday}");
}
WHERE("uid=#{uid}");

}
}.toString();
}

2.在UserDao接口中添加update方法

1
2
@UpdateProvider(type = UserSQL.class,method = "update")
int update(User user);

3.测试方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Test
public void testUpdate()throws Exception{
/*1.读取配置文件*/
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
/*2.解析配置文件*/
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
/*3.获取操作数据库的对象*/
SqlSession sqlSession = sqlSessionFactory.openSession();
/*4.获取代理对象*/
UserDao userDao = sqlSession.getMapper(UserDao.class);
/*5.调用代理对象的方法完成查询*/
User user = new User();
user.setUid(1);
user.setName("猪悟能");
user.setEmail("zhuwuneng@qq.com");
int count = userDao.update(user);

/*6.提交事物*/
sqlSession.commit();
/*6.释放资源*/
sqlSession.close();

//测试查询结果
System.out.println("共"+count+"行受到影响");
}

删除@DeleteProvider

1.在UserSQL功能类中添加生成SQL语句的方法

1
2
3
4
5
6
7
8
9
10
/**生成根据uid删除的SQL语句*/
/*#{uid}表示获取形参Integer的值,名字任意*/
public String deleteByUid(Integer uid){
return new SQL(){
{
DELETE_FROM("`user`");
WHERE("uid = #{uid}");
}
}.toString();
}

2.在UserDao接口中添加deleteByUid方法

1
2
@DeleteProvider(type = UserSQL.class, method = "deleteByUid")
int deleteByUid(Integer uid);

3.测试方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Test
public void testDelete()throws Exception{
/*1.读取配置文件*/
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
/*2.解析配置文件*/
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
/*3.获取操作数据库的对象*/
SqlSession sqlSession = sqlSessionFactory.openSession();
/*4.获取代理对象*/
UserDao userDao = sqlSession.getMapper(UserDao.class);
/*5.调用代理对象的方法完成查询*/

int count = userDao.deleteByUid(1);

/*6.提交事物*/
sqlSession.commit();
/*6.释放资源*/
sqlSession.close();

//测试查询结果
System.out.println("共"+count+"行受到影响");
}