持久层框架-MaBatis
配合 b站 动力节点 mybatis 教程学习效果更佳! 视频链接
框架概述
框架是一个软件,半成品的软件,定义好了一些基础功能,需要加入你的功能就是完整的。基础功能是可重复使用的,可升级的。
框架是一个模板,一个骨架,向其添入自己的想法,自己的功能,最后呈现出来的就是一个具有独特功能的完整产物;
MyBatis框架概述
mybatis是MyBatis SQL Mapper Framework for Java (sql映射框架) , 它是一个优秀的持久层框架。
-
sql mapper : sql映射
可以把数据库表中的一行数据映射为一个java对象。
一行数据可以看做是一个java对象。操作这个对象,就相当于操作表中的数据 -
Data Access Objects (DAOs) : 数据访问,对数据库执行增删改查。
-
mybatis 是 sql 映射框架,可以把它当作增强后的 JDBC ,它能让开发人员更加专注于 sql 语句的编写,而不用关心资源的释放,查询结果集这类繁琐的事情。
MyBatis 框架的使用
MyBatis 依赖
<!--MyBatis 依赖 具体落实的版本根据需求来-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
<!--mysql 驱动依赖 具体落实的版本根据需求来-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.9</version>
</dependency>
使用步骤
- 数据库中新建表 (本次案例是 Student 表,具体内容可以自行设计)
- 加入maven的mybatis坐标,mysql 驱动的坐标
- 创建实体类,Student – 映射表中的一行数据
- 创建持久层的 dao 接口,定义操作数据库的方法
- 创建一个mybatis使用的配置文件:叫做sql映射文件,写sql语句的。一般一个表一个sql映射文件。这个文件是 xml 文件。
- 创建 mybatis 的主配置文件:一个项目就一个主配置文件。
主配置文件提供了数据库的连接信息和sql映射文件的位置信息 - 创建使用mybatis类,通过mybatis访问数据库
步骤具体落实
在表和 Maven 依赖处理完成后,创建 domain 包存放表文件对应的映射实体类,创建 dao 包存放 dao 接口,一张表对应一个 dao 接口,一个 dao 接口对应一个叫做 mapper 或者叫做 dao 的 xml 文件。也就是说,对一张表的具体操作落实到一个 dao 接口中,一个 dao 接口对应一个 mybatis 操作数据库的 xml 文件。
实体类:
package com.zy.domain;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author zhangyu
* @date 2021/10/10 19:01
*/
@NoArgsConstructor // 无参构造器
@Data // set 和 get 方法
public class Student {
private int id;
private String name;
private String email;
private int age;
// 插入 set get
}
需要注意的是,dao 接口和对应的 mapper 文件需要放在同一包下;并且 dao 接口和 mapper 文件需要同名!
我们只需要在 dao 接口中定义操作表的方法,具体的 sql 语句在对应的 mapper 文件中编写!
dao 接口:
// student dao 接口
public interface StudentDao {
// 查询 student 表中所有 学生信息
public List<Student> selectStudents();
}
以下配置文件直接拷贝即可!
mapper 文件
<?xml version="1.0" encoding="UTF-8" ?>
<!--
sql 映射文件 mybatis会执行里面的sql语句
-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--上面这是约束文件,格式固定
mybatis-3-mapper.dtd 是约束文件名字 扩展名是 dtd
约束文件用来限制和检查当前文件中出现的标签与属性等信息必须符合 mybatis 的规则
-->
<!-- mapper 是当前文件的根标签 -->
<mapper namespace="com.zy.dao.StudentDao">
<!--namespace:命名空间 可自定义 但是要求使用 mapper 对应的dao接口的全限定名称-->
<!--
id : id是这条 sql 语句的唯一标识 ,mybatis 通过id的值来查找需要执行sql语句
可以自定义 但是要求填写接口中的对应方法名
resultType:表示结果类型,
是来规定这条 sql 语句执行后得到的结果集(ResultSet) 中每个具体对象实体的类型
值是对应表文件的实体类的全限定名称 在mybatis的全局配置文件中如果指定了别名即可使用别名
-->
<select id="selectStudents" resultType="Student" >
select * from t_student
</select>
</mapper>
<!--
在当前文件中,可以使用特定的标签,表示数据库的特定操作。
<select>:表示执行查询,select语句
<update>:表示更新数据库的操作,就是在<update>标签中写的是update sql语句
<insert>:表示插入,放的是insert语句
<delete>:表示删除,执行的delete语句
-->
mybatis 主配置文件:
<?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">
<!--
mybatis的主配置文件,内部提供了 mybatis 配置的相关信息
如 连接数据库的配置信息,别名信息,sql 映射文件的位置信息
-->
<configuration>
<!--mybatis 全局控制-->
<settings>
<!--控制台打印日志信息-->
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>
<!--设置别名 (不推荐使用,最好使用全限定名称)-->
<typeAliases>
<!--name是包名
这个包下面的类名 就是该类的别名(不区分大小写)-->
<package name="com.zy.domain"/>
<!--单独指定一个类的别名
type是全限定名称 alias是别名
<typeAlias type="com.zy.domain.Student" alias="stu"/>
-->
</typeAliases>
<!--配置环境 数据库的连接信息 不用记忆 后续 ssm 框架整合之后 具体信息会在 spring 中指定-->
<environments default="dev-env"> <!-- default跟随使用的开发环境做改变 -->
<!--
一个数据库信息的配置,环境 id : 唯一标识,标识环境名称 比如当前环境为开发环境
想要换一个数据库连接配置 只需要再指定一个 environment 在其中重新指定连接信息 就可以了
比如 <environment id="test-env">
-->
<environment id="dev-env">
<!--
transactionManager : mybatis 的事务类型
type = JDBC ( 表示使用jdbc中的connection对象的commit,rollback做事务处理)
-->
<transactionManager type="JDBC"/>
<!--数据源 连接数据库的-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/> <!--驱动-->
<property name="url" value="jdbc:mysql://localhost:3306/zynb"/> <!--url-->
<property name="username" value="mysql账号"/> <!--登录mysql的账号-->
<property name="password" value="mysql密码"/> <!--登录密码-->
</dataSource>
</environment>
</environments>
<!-- 指定sql映射文件的位置 -->
<mappers>
<!--<mapper resource="com/zy/dao/StudentDao.xml"/> 一个mapper标签指定一个映射文件的位置-->
<!--packge:
name: xml文件( mapper文件)所在的包名,
这个包中所有xml文件一次都能加载给mybatis
使用package的要求:
1. mapper文件名称需要和接口名称―样,区分大小写的一样
2. mapper文件和dao接口需要在同一目录
-->
<package name="com.zy.dao"/> <!--指定这个包为存放 mapper 文件的位置-->
</mappers>
</configuration>
<!--
以下代码直接拷贝到pom文件中的build标签下
需要在pom文件中的build标签中需要加入 才可以把dao包下的xml文件读取到类路径 (target/classes)下
<resources>
<resource>
<directory>src/main/java</directory> <!–指定mapper文件所在目录–>
<includes>
<include>**/*.properties</include> <!–扫描这两种文件–>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
-->
以上文件配置完成之后,就可以进行mybatis的测试了;
测试mybatis:
public class MyApp {
public static void main(String[] args) throws IOException {
//访问mybatis读取student数据
//1.定义mybatis主配置文件的名称,从类路径的根开始 ( target/clasess )
String config="mybatis-config.xml";
//2.读取这个config表示的文件
InputStream in = Resources.getResourceAsStream(config);
//3.创建了sqlsessionFactoryBuilder对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//4.创建sqlsessionFactory对象
SqlSessionFactory factory = builder.build(in);
//5.【重要】获取sqlsession对象,从sqlsessionFactory中获取SqlSession
SqlSession sqlsession = factory. openSession();
//6.【重要】指定要执行的sql语句的标识。sql映射文件中的namespace + ".”+标签的id值
String sqlId = "com.zy.dao.StudentDao"+ "." + "selectStudents";
//7、执行sql语句,通过sqlId找到语句
List<Student> studentList = sqlsession.selectList(sqlId);
//8.输出结果
studentList.forEach(student -> System.out.println(student));
//9.关闭sqlSession对象
sqlsession.close();
// 以上代码拷贝使用 无需记忆 后序会有mybatis动态代理
}
}
执行结果:
由于创建 sqlSession 的过程过于繁琐,可以将创建过程封装到工具类中供我们使用;
SqlSessionUtil
/*
创建 SqlSession 的工具类
*/
public class SqlSessionUtil {
private static SqlSessionFactory factory = null;
static {
//访问mybatis读取student数据
//1.定义mybatis主配置文件的名称,从类路径的根开始 ( target/clasess )
String config="mybatis-config.xml";
//2.读取这个config表示的文件
InputStream in = null;
try {
in = Resources.getResourceAsStream(config);
} catch (IOException e) {
e.printStackTrace();
}
//3.创建了sqlsessionFactoryBuilder对象 创建sqlsessionFactory对象
factory = new SqlSessionFactoryBuilder().build(in);
}
public static SqlSession getSqlSession(boolean autoCommit){
if(factory != null){
return factory.openSession(autoCommit);
}
return null;
}
}
利用 SqlSession 工具类和 Junit 测试单元来进行对学生表的插入操作;
Dao 接口:
// 插入一条学生信息
public int insertStudent(Student student);
StudentMapper
<insert id="insertStudent">
<!--#号落实到最终预编译的sql语句中代表的是占位符 ?号
然后利用传入的对象的属性映射到占位符做填充处理-->
insert into t_student values(#{id},#{name},#{email},#{age})
</insert>
测试单元代码
@Test
public void testInsert(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession(true); // 获取自动提交事务的Session
String sqlId = "com.zy.dao.StudentDao.insertStudent";
Student student = new Student();
student.setId(3);
student.setName("牛飞");
student.setEmail("niufei@163.com");
student.setAge(18);
sqlSession.insert(sqlId, student);
sqlSession.close();
}
@Test
public void testSelect(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession(true); // 获取自动提交事务的Session
String sqlId = "com.zy.dao.StudentDao.selectStudents";
List<Student> students = sqlSession.selectList(sqlId);
sqlSession.close();
}
插入结果显示成功一条,观察数据库发现插入成功!
小结:
sqlSessionFactory : 重量级对象,程序创建一个对象耗时比较长,使用资源比较多,所以在整个项目中创建一次就够了;
sqlSessionFactory 作用 : 获取sqlSession对象。SqlSession sqlSession = factory.openSession ();
openSession()方法说明:
- openSession() :无参数的,获取是非自动提交事务的sqlsession对象
- openSession (boolean): opensession(true)获取自动提交事务的sqlSession.
- openSession (false) 非自动提交事务的sqlsession对象
SqlSeeion接口定义操作数据库的方法,例如 selectList() , insert (Object o);
由于 SqlSession 对象不是线程安全的,所以需要在方法内部使用,在使用完毕之后需要关闭(生命周期只存在于方法内部,不能让该对象逃逸出方法),这样它才是线程安全的。
Dao接口实现类
通过接口实现类来完成相应的sqlSession的调用,在实现类方法内部实现MyBatis的行为,这样dao层就被完全独立出来,而其内部逻辑完全对外隐藏,看上去就是利用Dao接口来完成数据库的交互;
// Dao 实现类 通过创建这个实现类的对象来调用对应的操作数据库的方法
public class StudentDaoImpl implements StudentDao {
// 查询所有学生信息
@Override
public List<Student> selectStudents() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession(true);
String sqlId = "com.zy.dao.StudentDao.selectStudents";
List<Student> res = sqlSession.selectList(sqlId);
sqlSession.close();
return res;
}
// 插入一条学生信息
@Override
public int insertStudent(Student st) {
SqlSession sqlSession = SqlSessionUtil.getSqlSession(true);
String sqlId = "com.zy.dao.StudentDao.insertStudent";
int res = sqlSession.insert(sqlId, st);
sqlSession.close();
return res;
}
}
单元测试
@Test
public void testSelectDao(){
// dao层内部逻辑完全对外隐藏 只需提供一个入口就进行数据库交互
StudentDao studentDao = new StudentDaoImpl();
studentDao.selectStudents();
}
执行成功:
==> Preparing: select * from t_student
==> Parameters:
<== Columns: id, name, email, age
<== Row: 1, 李四, 123@11.com, 18
<== Row: 2, jack, 456@22.com, 22
<== Row: 3, 牛飞, niufei@163.com, 18
<== Total: 3
仔细观察后会发现,在接口实现类的方法中出现的代码逻辑乃至功能上大部分都是类似的,对于这种重复的操作,我们能否有一种方式可以简化操作呢?
List studentList = dao.selectStudents(); 调用该方法时能确定的信息
-
dao对象,类型是StudentDao,全限定名称是:com.zy.dao.studentDao 全限定名称 和 namespace 是一样的。
-
方法名称,selectStudents,这个方法就是 mapper 文件中的 id 值
id 值和方法名一致 (以上两种都是遵循之前的命名约定) -
通过dao 中方法的返回值也可以确定 MyBatis 要调用的 sqlSession 的方法
如果返回值是List ,调用的是sqlSession .selectList()方法。
如果返回值int ,或是非 List 的,看mapper文件中的标签是<insert>, <update>
就会调用sqlSession的insert , update等方法;
MyBatis的动态代理
MyBatis 的动态代理: mybatis 根据 dao 的方法调用,获取执行sql语句的信息。
mybatis根据你的dao接口,创建出一个dao接口的实现类,并创建这个类的对象。完成Sqlsession调用方法,访问数据库。
通俗的说,就是上方的StudentDaoImpl这个类不需要我们自己创建了,而是MyBatis根据dao接口来动态的创建一个实现类,并生成该类的对象,并且完成sqlSession的方法调用。
而我们只需要按照约定的命名方式提供Dao接口与Mapper文件就可以享受dao层单独剥离开来,并且不用在意其中细节就可以利用dao接口来完成数据库交互的乐趣。
使用要求
1.dao接口和mapper文件放在一起,同一个目录
2.dao接口和mapper文件名称—致
3.mapper文件中的 namespace 的值是dao接口的全限定名称
4.mapper文件中增删改查标签使用的id 是接口中方法名称
5.dao接口中不要使用重载方法,不要使用同名的,不同参数的方法
如何实现?
实现mybatis的动态代理机制,使用sqlSession.getMapper(dao接口.class)
getMapper能获取dao接口对应的实现类对象
// 动态代理完成
@Test
public void testSelects(){
/*
实现mybatis的动态代理机制,使用sqlSession.getMapper(dao接口)
getMapper能获取dao接口对应的实现类对象
getMapper(dao.class) 使用的是 jdk 的动态代理;
*/
SqlSession ss = SqlSessionUtil.getSqlSession(true);
StudentDao dao = ss.getMapper(StudentDao.class); // 获取动态生成的dao对象
dao.selectStudents();
System.out.println(dao);//org.apache.ibatis.binding.MapperProxy@289d1c02
ss.close();
}
执行成功:
==> Preparing: select * from t_student
==> Parameters:
<== Columns: id, name, email, age
<== Row: 1, 李四, 123@11.com, 18
<== Row: 2, jack, 456@22.com, 22
<== Row: 3, 牛飞, niufei@163.com, 18
<== Total: 3
参数
MyBatis 标签内部参数
-
resultType:表示查询语句的结果类型。
resultType对应的值:
-
可以是简单类型如int,string,double,也可以使用全限定类名如java.lang.Integer,但要求返回的数据为一行一列,也就是只有一个值。
-
也可是全限定名称或者别名 如 com.zy.Student,会将列值赋值给与列同名的属性
-
也可以是Map,推荐使用Map<Object,Object>,会将列名作为key 列值作为value 插入到map中 ,但要求只能返回一行数据。
-
resultType:表示结果类型.
它是来规定这条 sql 语句执行后得到的结果集(ResultSet) 中每个具体对象实体的类型
值 可以是对应表文件的实体类的全限定名称 在mybatis的全局配置文件中如果指定了别名即可使用别名
值 可以是任意的java类型,会将同名的列值赋值给类型中同名的属性,不一定得是实体类
<select id="selectStudents" resultType="Student" >
select * from t_student
</select>
当使用map作为返回值的时候,返回的数据中,列名是key,列值是value,
并且最多返回一行数据,出现多行会报错。(map不常用,推荐使用对象接收)
<select id="selectAllById" resultType="java.util.HashMap">
select * from t_student where id=#{id}
</select>
结果:
map={name=李四, id=1, email=123@11.com, age=18}
- parameterType: dao接口中方法参数的数据类型。(一般不写)
parameterType : dao接口中方法参数的数据类型。
parameterType它的值是java的数据类型全限定名称或者是mybatis定义的别名
例如: parameterType="java.lang.Integer"
parameterType="int"
注意:parameterType不是强制的,mybatis通过反射机制能够发现接口参数的数据类型
所以可以没有,一般也不写。
<select id="selectStudent" parameterType="java.lang.Integer" resultType="Student">
select id , name , email,age from t_student where id = #{id}
</select>
resultMap结果映射
resultMap: 结果映射,指定列名和 java对象中属性的对应关系
- 可以自己指定列值赋值给对象中的那个属性。
- 当列名和属性名不一样时,使用resultMap来完成映射关系 (常用)
resultMap 与 resultType 不能一起用,只能二选一
定义列名和属性的对应关系
id:自定义的唯一名称,在标签 <select> 中使用
type:java类型的全限定名称或者别名
最终表的数据行就会按照列名对应属性名的逻辑映射到type指定的java对象中去
<resultMap id="studentMap" type="com.zy.domain.Student" >
<!-- 主键使用id标签
column 列名
property java类型的属性名
-->
<id column="id" property="id" />
<!-- 非主键列使用 result -->
<result column="name" property="email" /> 将name列的值映射到对象中的email属性
<result column="email" property="name" /> 将email列的值映射到对象中的name属性
<result column="age" property="age" />
</resultMap>
最终查询到的结果就使用上面这个映射关系给Student对象赋值
<select id="selectStuById" resultMap="studentMap" >
select id,name,email,age from t_student where id=#{id}
</select>
结果:
Student(id=1, name=123@11.com, email=李四, age=18)
Dao接口方法参数
- 传入一个简单类型的参数
简单类型:mybatis把基本数据类型和String都叫简单类型
传入一个简单类型的参数:
简单类型:mybatis把基本数据类型和String都叫简单类型
在mapper文件中获取传入的一个简单类型的参数,使用#{任意字符}接收
不要求与参数同名
dao:
public Student selectStudent(Integer id);
mapper:
select name from t_student where id = #{studentId} 也可行
- 传入多个参数,使用@Param命名参数
当传入多个参数时,为了区分每个参数,可以使用@Param给每个参数起一个它在mapper文件中的名称。
要求 mapper 文件中使用 @Param 定义好的名字
dao:
public Student selectStuByNameAge(@Param("myname") String name,
@Param("myage") Integer age);
mapper:
select * from t_student where name = #{myname} and age = #{myage}
- 传入多个参数,使用对象
把对象传入,要求mapper文件中使用对象中的属性名
@NoArgsConstructor
@ToString
@Data // 封装参数的类
public class QueryParam {
private String paramName;
private Integer paramAge;
}
多个参数,使用java对象作为接口中方法的参数
要求mapper文件中使用对象中的属性名
dao: 使用封装参数的类
public Student selectStudentByNA(QueryParam param);
mapper:
select * from t where name= #{paramName} and age = #{paramAge}
dao: 使用实体类 Student
public Student selectStudentByNA(Student stu);
mapper:
select * from t where name= #{name}
使用对象的完整语法:#{属性名,javaType=类型名称, jdbcType=数据类型}很少用。
javaType:指java中的属性数据类型。
jdbcType:在数据库中的数据类型。
例如:#{paramName ,javaType=java.lang,String,jdbcType=VARCHAR}
我们使用的简化方式:#{属性名},javaType,jdbcType的值mybatis反射能获取。不用提供
- 传入多个参数,利用Map来存放参数 (可读性差,不建议使用)
利用map的key做属性名,value做值,在mapper文件中通过填写属性名取对应的值 使用语法 #{map的key}
key 为属性名 value 为值
要求在mapper中填写map的key来获取对应的value值
Map<String,Object> paramMap = new HashMap<>();
map.put("myname","lisi");
map.put("age",18);
dao:
public Student selectStu(Map<String,Object> paramMap);
mapper:
select * from t where name = #{myname} and age = #{age}
- 多个参数,按照对应位置传参数(可读性差,容易出错,不建议使用)
mybatis 3.4 之前 使用#{0} #{1} ...
mybatis 3.4 之后 使用#{arg0} #{arg1} ...
dao:
Student selectStu(String name, Intger age);
mapper:
select * from t where age = #{arg1} and name = #{arg0}
#{} 与 ${}
使用#{ }之后,mybatis执行sql是使用的jdbc中的PreparedStatement对象。
#{ }代表的是?占位符,用参数中符合要求的值替换占位符。(替代=右边的?)
使用#{ }效率更高更安全,没有sql注入的风险,所以推荐使用#{ }。
可理解为由mybatis执行下面的代码:
select * from t_student where id = #{studentId}
1.mybatis创建Connection , PreparedStatement对象
String sql="select * from student where id=?";
PreparedStatement pst = conn.preparedStatement(sql);
pst.setInt(1,1);
2.执行sql封装为resultType="com.zy.domain.Student"这个对象
ResultSet rs = pst.executeQuery();
Student student = null;
if(rs.next()){
//从数据库取表的一行数据,存到一个java对象属性中
student = new Student();
student.setId(rs.getInt("id");
student.setName(rs.getString("name" ));
student.setEmail(rs.getString("email"));
student.setAge(rs.getInt("age"));
}
return student; 给dao方法返回查询的student对象
${ } 代表字符串替换,不使用占位符,它会告诉mybatis使用参数中对应的值替换掉它所在的位置,使用的是 Statement 把 sql 语句和具体的值连接起来(字符串的连接操作,存在sql注入风险)。主要用在替换表名,列名,动态更换需要排序的列等操作。
#{ } 和 ${ }都支持上述五种参数传递方式。
模糊查询like的使用
<!--第一种方式:直接注入一个在java代码中拼接好的字符串 推荐使用,最简单最直接-->
<select id="selectByLike" resultType="com.zy.domain.Student">
select * from t_student where name like #{name}
</select>
<!--第二种方式:在内部自行拼接-->
<select id="selectByLike2" resultType="com.zy.domain.Student">
select * from t_student where name like "%" #{name} "%"
</select>
@Test
public void testLike(){
SqlSession ss = SqlSessionUtil.getSqlSession(true);
StudentDao dao = ss.getMapper(StudentDao.class); // 获取动态生成的dao对象
String name = "%李%"; // 第一种方式 在java代码里就拼接好然后注入
Student student = dao.selectByLike(name);
System.out.println(student);
ss.close();
}
@Test
public void testLike2(){
SqlSession ss = SqlSessionUtil.getSqlSession(true);
StudentDao dao = ss.getMapper(StudentDao.class); // 获取动态生成的dao对象
String name = "李"; // 第二种方式先注入 在xml里拼接
Student student = dao.selectByLike2(name);
System.out.println(student);
ss.close();
}
两次结果一致结果:
Student(id=1, name=李四, email=123@11.com, age=18)
动态SQL
动态sql: sql 的内容是变化的,可以根据条件获取到不同的sql语句。
主要是where部分发生变化。
要实现动态sql,接口中方法的参数必须是java对象。
if标签
动态sql的实现,使用的是mybatis提供的标签,<if> ,<where>,<foreach>
<if>是判断条件的
语法 <if test="判断java对象的属性值" >
部分sql语句
</if>
使用if标签,需要在where后加上 1=1 或者 id>0 等操作来避免语法错误
<select id="selectstudentIf" resultType="com.zy.domain.Student">
select id,name , email,age from student
where 1=1
<if test="name != null and name !='' ">
and name = #{name}
</if>
<if test="age > 0 ">
and age > #{age}
</if>
</select>
Dao:
使用动态Sql 必须以Java对象作为参数
public Student selectStudentIf(Student stu);
单元测试 此时 student对象的name字段为null
@Test
public void testIf(){
SqlSession ss = SqlSessionUtil.getSqlSession(true);
StudentDao dao = ss.getMapper(StudentDao.class); // 获取动态生成的dao对象
Student student = new Student();
student.setAge(10);
List<Student> students = dao.selectStudentIf(student);
ss.close();
}
结果: 从sql语句可以看出if条件将name字段过滤掉了
==> Preparing: select id,name ,email,age from t_student where 1=1 and age > ?
==> Parameters: 10(Integer)
<== Columns: id, name, email, age
<== Row: 1, 李四, 123@11.com, 18
<== Row: 2, jack, 456@22.com, 22
<== Row: 3, 牛飞, niufei@163.com, 18
<== Row: 4, 张飞, zf@163.com, 33
where标签
<where> 用来包含多个 <if> 的,当多个 if 有一个成立的,<where> 会自动增加一个where关键字
同时会去掉if中多余的and , or等。
where 标签会动态的判断是否有if成立,如果有if成立则生成一个where,没有则不生成
并且它还会自动去除语句中多余的 or,and 等
<select id="selectStudentWI" resultType="com.zy.domain.Student">
select id,name ,email,age from t_student
<where>
<if test="name != null and name !='' ">
name = #{name}
</if>
<if test="age > 0 ">
and age > #{age}
</if>
</where>
</select>
Dao:
// 使用动态Sql 必须以对象作为参数
public List<Student> selectStudentWI(Student stu);
单元测试 此时 student对象的name字段为null
@Test
public void testWI(){
SqlSession ss = SqlSessionUtil.getSqlSession(true);
StudentDao dao = ss.getMapper(StudentDao.class); // 获取动态生成的dao对象
Student student = new Student();
student.setAge(20);
List<Student> students = dao.selectStudentWI(student);
ss.close();
}
结果: 可以看见 where 字段是自动生成的 并且去除了原语句中的and
==> Preparing: select id,name ,email,age from t_student WHERE age > ?
==> Parameters: 20(Integer)
<== Columns: id, name, email, age
<== Row: 2, jack, 456@22.com, 22
<== Row: 4, 张飞, zf@163.com, 33
foreach标签
<foreach> 循环java中的数组,list集合的。主要用在sql的in语句中。
举例查找学生id是1001,1002 ,1003的三个学生
select * from student where id in (1001,1002,1003)
可以将这三个id保存在一个List中,作为接口方法的参数,最后利用foreach标签对List进行遍历。
<select id="selectForeach" resultType="com.zy.domain.Student" >
select * from t_student where id in
<foreach collection="list" item="id" open="(" separator="," close=")" >
#{id}
</foreach>
</select>
collection:表示接口中的方法参数的类型,如果是数组使用array,如果是list集合使用list
item:自定义的,表示数组和集合遍历时每一个被遍历到的成员
open:循环开始时的字符
close:循环结束时的字符
separator:集合成员之间的分隔符
Dao:
public List<Student> selectForeach(List<Integer> ids);
@Test
public void testForeach(){
SqlSession ss = SqlSessionUtil.getSqlSession(true);
StudentDao dao = ss.getMapper(StudentDao.class); // 获取动态生成的dao对象
ArrayList<Integer> list = new ArrayList<>();
list.add(2);
list.add(3);
list.add(4);
List<Student> students = dao.selectForeach(list);
ss.close();
}
结果: 可以发现,<foreach> 自动对list进行了遍历,并且将值填入到占位符
==> Preparing: select * from t_student where id in ( ? , ? , ? )
==> Parameters: 2(Integer), 3(Integer), 4(Integer)
<== Columns: id, name, email, age
<== Row: 2, jack, 456@22.com, 22
<== Row: 3, 牛飞, niufei@163.com, 18
<== Row: 4, 张飞, zf@163.com, 33
<== Total: 3
上面是遍历简单类型,那么遍历对象呢?
<select id="selectForeach2" resultType="com.zy.domain.Student" >
select * from t_student where id in (
<foreach collection="list" item="stu" separator="," >
#{stu.id} 利用变量.属性的方式可以取出每一个对象的属性
</foreach>
)
</select>
Dao:
public List<Student> selectForeach2(List<Student> stus);
@Test
public void testForeach2(){
SqlSession ss = SqlSessionUtil.getSqlSession(true);
StudentDao dao = ss.getMapper(StudentDao.class); // 获取动态生成的dao对象
ArrayList<Student> list = new ArrayList<>();
Student stu1 = new Student();
stu1.setId(1);
Student stu2 = new Student();
stu2.setId(2);
Student stu3 = new Student();
stu3.setId(3);
list.add(stu1);
list.add(stu2);
list.add(stu3);
List<Student> students = dao.selectForeach2(list);
ss.close();
}
结果: 可以发现,<foreach> 自动对list进行了遍历,并且取出了对象的id填充占位符
==> Preparing: select * from t_student where id in ( ? , ? , ? )
==> Parameters: 1(Integer), 2(Integer), 3(Integer)
<== Columns: id, name, email, age
<== Row: 1, 李四, 123@11.com, 18
<== Row: 2, jack, 456@22.com, 22
<== Row: 3, 牛飞, niufei@163.com, 18
<== Total: 3
代码片段
sql 代码片段,就是复用一些语法
步骤:
1.先定义<sql id="唯一名称" > sql语句,表名,字段等 </sql>
2.再使用,<include refid="唯一名称" /> 来引用
自定义可复用的sql片段
<sql id="selectAllStudent" >
select * from t_student
</sql>
<select id="selectStudentSqlFragment"resultType="com.zy.domain.Student">
依据sql片段id引用sql片段
<include refid="selectAllStudent"/>
<if test="list !=null and list.size > 0 ">
where id in
<foreach collection="list" open="(" close=")"
item="stu" separator=",">
#{stu.id}
</foreach>
</if>
</select>
属性配置文件
数据库的属性配置文件: 把数据库连接信息放到一个单独的文件中,和mybatis主配置文件分开。
目的是便于修改,保存,处理多个数据库的信息。
1. 在resources目录中定义一个属性配置文件, xxxx.properties ,例如 jdbc.properties
在属性配置文件中,定义数据,格式是key=value
key: 一般使用 . 做多级目录的。
例如 jdbc.mysql.driver, jdbc.driver
2. 在mybatis的主配置文件,使用<property>指定文件的位置
在需要使用值的地方,$ { key }
jdbc.properties配置文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/zynb
jdbc.user=数据库账号
jdbc.passwd=数据库密码
主配置文件添加标签
<!--指定properties文件的位置,从类路径根开始找-->
<properties resource="jdbc.properties"/>
<!--重新填写连接信息,使用配置文件中的数据-->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/> <!--驱动-->
<property name="url" value="${jdbc.url}"/> <!--url-->
<property name="username" value="${jdbc.user}"/> <!--登录mysql的账号-->
<property name="password" value="${jdbc.passwd}"/> <!--登录密码-->
</dataSource>
岁月悠悠,衰微只及肌肤;热忱抛却,颓废必致灵魂