Mybatis的含义:Mybatis框架是一个持久层框架,几乎解决了jdbc代码在手动设置参数和对结果集的手动获取问题,原本是apache公司的开源项目,最后转给Google公司。Mybatis会将参数封装在一个对象中传递给数据库,并将sql语句执行后的结果集封装成对象。
它提供全局配置文件,建立与数据库的连接;将接口进行分装并提供类和方法实现对数据库的链接和操作;对sql语句执行后的结果进行高级映射并封装成对象;支持动态sql;支持缓存。
Mybatis中存在自动映射,因为当在数据库中查询的表的属性名和类中的属性名完全相同时才会自动映射并封装,所以需要保证类中的属性和数据库中的属性名相同;如果类中的属性为私有属性,那么类中必须实现get和set方法;还需要保证类中要有无参构造方法,由于mybatis在数据库中查询数据后需要创建对象,调用对应类的无参构造方法,如果找不到对应的无参构造方法,mybatis就会报错;当数据库中的属性名存在驼峰命名,mybatis也是会进行自动映射,但前提是需要开启属性mapUnderscoreToCamelCase,这个属性需要在全局配置文件中设置,表示是否开启驼峰命名的自动映射,true为开启,开启后例如在数据库中的属性名为student_id,那么类中的属性只要设置为studentId这种驼峰命名的方式就可以进行自动映射。
在数据库中创建表,并在创建的项目中创建对应表的模型类;例如在数据库中创建一个学生表,那么为了方便理解我们也在项目中创建一个学生类,如图所示
我们通常将类中所有的属性都定义为其包装类类型,这样方便后面动态查询条件的判断
1、在项目的pom.xml配置文件中导入mybatis的jar包
2、配置全局配置文件(数据库连接信息)
在resources目录创建一个.xml文件用来配置全局文件,并在文件中写入配置信息
其中
(1)⾸先先判断空闲连接池内有没有空闲连接,如果还有则给你返回⼀个空闲连接。
(2)、如果没有空闲连接,则去活动连接池内看看还有没有位置,如果还有,则new⼀个连接给你返回
(3)、如果活动连接池没有位置了,则返回在活动连接池使⽤最久的连接。意思就是给你返回⼀个在活动连接池内待最久的连接
3、写sql映射,访问接口
在创建映射文件之前需要先创建和映射文件进行绑定的接口,一个映射文件对应一个接口,配个映射配置文件中都需要加上
这样映射文件就和接口进行了绑定,我们还需要在配置文件中获取连接数据库的信息以及构建SqlSessionFactory,由于SqlSessionFactory一旦创建就会一直存在于Mybatis的应用过程中,并且由于创建SqlSessionFactory的开销过大 ,所以我们在构建SqlSessionFactory时只需要创建一次即可,所以我们可以创建一个类并将创建SqlSessionFactory的方法放在这个类的静态代码块中,这样即使多次调用这个类但是创建SqlSessionFactory只会执行一次。
public class MyBatisUtil {
static SqlSessionFactory sqlSessionFactory = null;
static {
try {
// 将全局配置文件放入到流中
InputStream inputStream = Resources.getResourceAsStream("mybatisConfig.xml");
// 与数据库建立连接
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public SqlSession getSqlSession() {
return sqlSessionFactory.openSession();//通过SqlSessionFactory对象获取SqlSession
}
}
getSqlSession方法时用来通过SqlSessionFactory对象的openSession方法获取SqlSession对象,我们可以通过SqlSession对象来执行与参数和返回值相匹配的接口
如果向执行sql语句,首先要在接口中定义抽象方法,并确定参数列表和返回值;然后在映射文件中写对应的sql语句,如果为select语句则需要在
public interface StudentDao {
Student find(int id);//查询通过id查询学生的信息
}
sql语句中的id需要与接口中的方法名相同,resultType为返回值类型这里的类型为Student,由于Student为自定义的类型,所以需要写该类型的包名,#{id}表示传过来的参数的值
以下为具体实现的方法
public void findStudent() {
SqlSession sqlSession = new MyBatisUtil().getSqlSession();//通过类获取SqlSession对象
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);//通过接口的class(类)对象,获取代理对象,由于接口和映射配置文件绑定,所以可以通过代理对象调用接口中的方法
Student student = studentDao.find(1);//通过代理对象调用接口中的find方法,并传入参数1
System.out.println(sqlSession);
sqlSession.close();
}
4、测试
执行方法后的结果如下,得到如下结果需要在Student中实现toString方法
与映射器绑定的接口中的方法中的参数可以是任意一个,因为映射器配置文件中的id属性的值和对应接口的方法名相同,所以同一个接口中的方法不能重名。当方法中的参数为一个时,直接将该参数进行传递即可;而当参数为多个时需要使用@Param注解对参数进行绑定,@Param里的值为类中的属性名,与其绑定的则为形参。
void find(@Param("id")Integer id,@Param("no")Integer no,@Param("name")String name);
当接口中的参数过于多时,我们可以将参数封装在一个对象中,通过传递对象来传递参数,但是我们还需要在映射器中加入parameterType属性来说明传递的参数的类型。
void find(Student student);
当我们对数据库进行增添操作时,我们先把数据封装在对象中并将值传给数据库中进行操作,此时如果我们还想通过刚新插入的数据的id查询其它的内容,由于id是自增的是由数据库进行自加的,所以我们并不知道id是多少。这时我们可以通过在sql语句的
insert into admin(account,password,gender)value(#{account},#{password},#{gender})
在新增,删除和修改操作中,我们在将SqlSession关闭之前,需要将SqlSession的实例化对象的commit方法进行提交,而查询操纵则不需要。因为查询操作没有对数据库中的数据进行改变。
Mybatis中还存在增删改查操作的注解标签,一般如果某些增删改查操作的sql语句较为简单,我们就可以直接在对应的接口上面通过注解标签的形式来进行操作。
@Select("select * from grade where id = #{id}")
int selectGrade(int id);
#{}为占位符,通过预编译的方式先用?代替出现占位符的地方,等到将sql语句编译完成再将传入的参数,可以防止sql注入
${}为拼接符,拼接符就像字符串一样被Mybatis拼接到sql语句中,不能防止sql注入
结果处理就是Mybatis对sql语句执行后的结果进行的封装处理,有时我们不止只在一张表中进行查询操作,返回的结果集中可能存在多个表中的数据,这时我们就需要在mapper映射器中将所有的映射关系进行配置,因为Mybatis在多表查询后时不会将结果进行自动映射并封装的。
例如在学生和年级关系中,如果想要查询一个学生的信息和其所对应的年级信息,这是一个多对一关系的关联查询,查询的结果集中存在不属于学生表的属性,这时我们需要在自定义的学生类中添加一个年级类型的属性用来存放和年级相关的所有信息,并在mapper映射器中配置各个查询结果的属性所对应类中的属性。
public class Student {
private Integer id;
private String name;
private String gender;
private Grade grade;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getNo() {
return no;
}
public void setNo(Integer no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Grade getGrade() {
return grade;
}
public void setGrade(Grade grade) {
this.grade = grade;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", no=" + no +
", name='" + name + '\'' +
", gender='" + gender + '\'' +
", grade=" + grade +
", admin=" + admin +
'}';
}
}
public class Grade {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Grade{" +
"id=" + id +
", name='" + name + '\'' +
", studentList=" + studentList +
'}';
}
}
如果查询结果集中的属性存在于多个表中,我们在查询
如果我们要查询在一个年级中有多少个学生以及每个学生的基本信息,这是一个一对多关系的关联查询,这时我们也需要在年级类中添加新的属性,由于是一对多关系所以一个年级就会对应多个学生,所以我们需要添加一个存放学生信息的集合属性。
public class Grade {
private int id;
private String name;
private List studentList;
}
配置映射关系的方式和前面大致相同,仍然是写在一个
在这种一对多的关联查询中我们还可以使用嵌套查询来解决分页问题的产生,通过将一个复杂的查询转换为多个简单查询。例如查询所有年级以及每个年级对应的所有学生的信息。具体的思路是我们先通过一个简单查询将所有的年级信息查询出来,再根据年级的id来查询每个年级所对应的所有学生。
其中我们还是使用
我们在进行查询操作时,有时查询的条件不止一个,这时我们就需要在select语句中手动添加查询条件例如:
select * from student where id = 1 and name = "小明"
前面我们将类中的属性创建为包装类类型,这样当参数无效时只有两中可能:一种是null,另一种是" ",这时我们如果不将为null或者为" "的属性删除的话我们就查询不到我们想要的数据,这时我们就需要动态地将查询条件进行改变,Mybatis框架中刚好有这种功能。我们可以将where语句的部分写入到Mybatis提供的
使用
update student
name = #{name},
gender = #{gender}
where id = #{id}
update student
name = #{name},
gender = #{gender}
where id = #{id}
通过缓存可以减少用户对数据库访问的次数,进而减少了数据库的压力,提高查询性能。我们可以将通过相同的操作而得到相同的结果集的数据保存到缓存中,这样当用户进行多次相同的操作时就不会再向数据库中访问数据,而是直接通过缓存提高了查询效率。我们一般将一段时间内不会发生改变的数据存放在缓存中,例如对某些网页的访问,一个网页在一段时间内可能有很多的用户对其进行访问,我们不能让用户都去访问数据库中的数据,而是可以通过缓存拿到相同的数据,而缓存中的数据我们只需要让其每过一段时间自动刷新一次即可。
一级缓存
一级缓存的作用域是同一个SqlSession中,在一个SqlSession中如果执行多次相同的sql操作,那么从第二次操作开始,读取到的数据都是来自于缓存中的,当一个SqlSession不存在后,其对应的缓存也将被销毁,Mybatis默认开启的是一级缓存。
二级缓存
二级缓存是 SqlSessionFactory 级别的,作用域为同一个namespace中,当用户执行同一个namespace中的同一个sql语句时,第一次访问会先向数据库中访问并将访问后的数据存放在二级缓存中,当第二次执行同一个namespace中的同一个sql语句时,就会从缓存中读取数据,除非当缓存在超时,被声明需要刷新,或者sqlSession在执行update,insert,delete操作并commit提交时,会清空缓存区,防止读取的数据存在问题。