- Mybatis配置
- .主配置文件 mybatis.xml
<configuration>
<!-- setting 控制mybatis 全局行为 -->
<properties resource="jdbc.properties"/>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!-- 定义别名 -->
<typeAliases>
<!-- <typeAlias type="com.entity.Student" alias="Student"/>-->
<!-- 指定包 包里的所有类名就是别名 类名不区分大小写 会扫描包下所有的类 将全部的类都映射别名 如果别名相同就会出错-->
<package name="com.entity"/>
</typeAliases>
<plugins>
<!-- 添加分页插件 -->
<plugin interceptor="com.github.pagehelper.PageInterceptor"/>
</plugins>
<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>
<!--sql mapper映射文件的位置-->
<mappers>
<!-- mapper 标签 每次只能写一个 mapper 文件 同一个文件不能多次导入-->
<!-- <mapper resource="com/dao/StudentDao.xml"/>-->
<!-- 第二种方式 使用package
name: xml文件 mapper文件所在的包名 这个包中所有xml文件一次都能加载
使用package文件的要求:
mapper文件名称 和 接口名称一样 不区分大小写的一样
mapper文件和 dao接口需要在同一目录
-->
<package name="com.dao"/>
</mappers>
</configuration>
mybaits启动的时候会扫描到mybatis.xml 这个配置文件 ,使用 SqlSessionFactoryBuilder 将配置文件进行解析装载,得到 SqlSessionFactory 实例,然后使用的时候由 SqlSessionFactory 获取 SqlSession对象,所有的数据库操作都由这个对象进行处理。
注意 SqlSession实例并不是线程安全的,所以一般是在方法体里获取这个对象,将对象的有效域限制在局部方法中以保证多线程安全,使用完成之后需要手动执行 close 方法销毁这个对象。
在sqlSessionFactory.openSession()
获取时 ,默认得到的是sqlSession 对象是关闭自动提交的,如果有DDL操作(增 删 改)则需要手动执行 sqlSession.commit();
但是如果在获取SqlSession对象时 使用的是:SqlSession sqlSession = sqlSessionFactory.openSession(true);//获取自动提交事务
则不需要再手动提交
private static SqlSessionFactory sqlSessionFactory = null;
static{
//定义访问 mybatis 主配置文件的名称
String config = "mybatis.xml";
//读取这个config配置文件
try (InputStream resourceAsStream = Resources.getResourceAsStream(config)) {
//创建sqlsessionFactoryBulider 对象
SqlSessionFactoryBuilder bulider = new SqlSessionFactoryBuilder();
//创建sqlsessionFactory对象
sqlSessionFactory = bulider.build(resourceAsStream);
//获取 sqlsession对象 ,从sqlsessionFactory 中 获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();//获取非自动提交事务
//SqlSession sqlSession = sqlSessionFactory.openSession(true);//获取自动提交事务
}catch(Exception e) {
}
}
//执行数据库操作
String sqlId = "com.dao.StudentDao" + "." + "selectStudentList";
//执行语句 通过sqlId找到语句
List<Student> studentList = sqlSession.selectList("selectStudentList");
//如果 selectStudentList 是唯一的,那么上面和下面的都是可以正常执行的
//List<Student> studentList = sqlSession.selectList(sqlId );
studentList.forEach(stu-> System.out.println(stu));
mybatis 在执行sql 时 是根据命名空间 + id 的方式来查找到指定的sql,同时如果id 在全局里是唯一的,是可以直接使用 id 来执行sql的。
- Mapper配置文件 xx-mapper.xm
<mapper namespace="com.dao.StudentDao">
<select id="selectStudentList" resultType="Student">
select id,name,email,age from mstudent order by id desc
</select>
<select id="selectNextId" resultType="Integer">
select max(id)+1 from mstudent
</select>
</mapper>
-
Mybatis执行数据库操作 和 动态代理原理
很多人会好奇为什么mybaits 需要配置一个接口 和 一个同名的mapper文件 ? 这个要跟mybaits执行sql的流程说起
mybatis在启动之初会将 namespace 命名空间 和 所有sql的id 全部进行注册 。 在具体执行sql的时候是使用 命名空间 + id 的方法寻找到指定的sql , 然后根据把命名空间当做一个接口/类A 使用JDK自动代理的方式生成类A的实现类的一个实例,由这个实例来执行sql。
执行sql 时 根据mapper文件中的sql 的标签是 select 还是 update insert delete 来确定执行的是哪一种操作 ,比如
<select id="selectStudentList" resultType="Student"> select id,name,email,age from mstudent order by id desc </select>
mybatis 知道这是一个查询操作 ,但是具体是使用 selectOne 还是使用 selectList 哪一种方法呢?
这个时候返回值类型 resultType 就派上用场了,如果 resultType 是java基本类型 和 String 类型 ,那么mybaits 就知道了这是查询单条记录 ,如果是list类型(实际上resultType 是具体的对象类型)这时候就知道调用 selectList 了。
从上面的过程是可以看出来实际上执行sql 完全可以就靠mapper 文件就行了,**不需要接口**。
使用接口是为了符合java的使用规范,使用接口的时候
--//使用接口执行sql
StudentDao mapper = sqlSession.getMapper(StudentDao.class);
List<Student> students = mapper.selectStudentList();
// StudentDao 的全限量名 com.dao.StudentDao 就是 mapper文件的命名空间
//接口的方法 selectStudentList 就是sql 的id ,获取到sql的信息后 通过动态代理生成 StudentDao 的一个实现类的对象来执行sql
//打印出 mapper对象的类名,发现就是一个jdk代理生成的对象
System.out.println(mapper.getClass().getName()); com.sun.proxy.$Proxy5
-
Mybatis 传参
mybatis获取参数有两种办法:一种使用占位符: # , 一种使用 $ 替换符
比如执行select id , name, email, age from mstudent where id= #{id}
时,实际上过程是
select id , name, email, age from mstudent where id= #{id}`
//先获取一个 preparedStatement 对象
PreparedStatement pst = conn.preparedStatement();
String sql = "select id , name, email, age from mstudent where id= ? ";
//设置参数
pst.setInt(1, id);
//执行sql 获取结果
ResultSet rs = pst.executeQuery();
while (rs.hasNext){
Object obj = rs.next();
//如果有无参构造器
Student student = new Student();
student.setName(obj.getString("Name"));
。。。
//如果没有无参构造器
Student student = new Student(a,b,c,d);
//将返回结果中的字段按顺序 id , name, email, age 往 a b c d 上塞
}
- 单个参数传参
单个参数时,使用 # 取参数值时,参数名随便写,从现实逻辑来讲,反正就一个参数直接用就行了
Student selectStudentById(int id);
//mapper 文件中
<!-- paremeterType 是java的数据类型全限量名称或者是mybatis定义的别名 java.lang.Integer 或者 int (别名)
patemeterType 不是强制的,mybatis通过反射机制能够发现接口参数的类型-->
<select id="selectStudentById" resultType="com.entity.Student" parameterType="java.lang.Integer">
select id , name, email, age from mstudent where id= #{id}
</select>
或者
select id , name, email, age from mstudent where id= #{ajdj}
都是可以的
- 多个参数传参
//取值的时候 使用 arg0 arg1 或者 param1 param2 即可 3.4以前的版本可以直接只用 #{0} #{1}
List<Student> selectStudentByIdAndName(int id, String name);
//可以使用 #{myId} #{myName} 取值
List<Student> selectStudentByIdAndName2(@Param("myId") int id, @Param("myName") String name);
<!--多个参数传递 -->
<select id="selectStudentByIdAndName" resultType="student" >
select id , name, email, age from mstudent where id= #{arg0} or name = #{arg1}
</select>
<select id="selectStudentByIdAndName2" resultType="com.entity.Student" >
select id , name, email, age from mstudent where id= #{myId} or name = #{myName}
</select>
- 对象传参
// 直接使用 属性名 即可
// javaType=string, jdbcType=VARCHAR 可写 可不写,但是如果是进行插入操作,字段有可能null时,**必须写上 否则会报类型转换错误**
private String queryName;
private int queryId;
List<Student> selectStudentByObj(QueryEntity entity);
<select id="selectStudentByObj" resultType="com.entity.Student" >
select id , name, email, age from mstudent where id= #{queryId,javaType=int, jdbcType=INTEGER}
or name = #{queryName,javaType=string, jdbcType=VARCHAR}
</select>
<select id="selectStudentByObj" resultType="com.entity.Student" >
select id , name, email, age from mstudent where id= #{queryId} or name = #{queryName}
</select>
- Map传参 (不建议使用)
//使用Map 传参数 只能使用 Map<String,Object> 类型 ,xml文件中只能使用 #{key}
//不能使用 Map<String,String> 哪怕参数只有String 类型的也不行,会报类型错误
//因为这种参数不可控 不建议使用,在阿里的开发规范中明确建议禁止
/** 不建议使用
* @Description:
* @Param: [paramMap]
* @return: java.util.List<com.entity.Student>
* @Author: Administrator
* @Date: 2021/8/14
*/
List<Student> selectStudentByMap(Map<String,Object> paramMap);
- 条件和动态sql
主要是 if where foreach 这3个 和 sql片段
<!-- 动态 sql if where foreach
<if test="java 对象的属性" >
-->
<select id="selectStudentIf" resultType="student">
select id, name, email ,age from mstudent
where 1=1
<if test="name !=null and name !=''">
and name= #{name}
</if>
<if test="age>0">
and age > #{age}
</if>
</select>
<select id="selectStudentWhere" resultType="student">
select id, name, email ,age from mstudent
<where>
<if test="name !=null and name !=''">
and name= #{name}
</if>
<if test="age>10">
and age > #{age}
</if>
</where>
</select>
<select id="selectStudentForeach" resultType="student">
select id, name, email ,age from mstudent
where id in
<foreach collection="list" item="myId" open="(" close=")" separator=",">
#{myId}
</foreach>
</select>
<select id="selectStudentForeachEntity" resultType="student">
select id, name, email ,age from mstudent
where id in
<foreach collection="array" item="stu" open="(" close=")" separator=",">
<if test="stu.id != null">
#{stu.id}
</if>
</foreach>
</select>
最后提一下 like 查询 和 $ 替换符号,
$ 是使用拼接sql的方式,所以执行效率低,还有sql 注入风险。
实际上的主要用途是用来替换列名 和 表名,比如排序的时候 根据不同的字段进行排序
<select id="selectStudentOrder" resultType="com.entity.Student" >
select id , name, email, age from mstudent where id >=#{id} order by ${colname} asc
</select>
like 查询有2 种方式, 一种是将 查询条件在 java 中写好,直接送进去,这种最简单易懂
String nameLike = "%an%";
<select id="selectLikeName" resultType="student">
select id, name, email ,age from mstudent where name like #{nameLike }
</select>
一种是由mybatis 利用 concat 函数进行拼接
<!-- like 查询 使用字符串拼接函数 -->
<select id="selectLikeName2" resultType="student">
select id, name, email ,age from mstudent where name like concat(concat('%',#{likeName}),'%')
</select>
- 参数映射
主要用于解决数据库列名 和 实体对象属性名不一致的问题
其中 id 列的话 使用 id 标签, 一般列使用 result 标签
<!-- 返回结果中的列 如果在 resultMap 中没有指定映射则会使用默认字段映射-->
<resultMap id="studentInfo" type="com.entity.Student">
<id column="id" property="id"/>
<result column="name" property="email"/>
<result column="email" property="name"/>
</resultMap>
<select id="selectAllStudent" resultMap="studentInfo">
select id, name, email ,age from mstudent
</select>
mybatis 在映射时,如果 student对象中有个 age 属性,但resultMap 没有写aga的映射关系,则会使用默认的映射关系 如上图虽然没有指定age 映射到student对象上哪个属性,依然会赋值给student 对象上的 age属性
- 分页插件 PageHelper
这个比较简单,其实就是在原有的sql 再套一层。执行查询时先查出总记录数, 再确定 记录数的 beginNo 和 endNo
导入依赖包 和 配置 mybatis 主文件
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
plugins 插件要写在 environments 标签之前
<plugins>
<!-- 添加分页插件 -->
<plugin interceptor="com.github.pagehelper.PageInterceptor"/>
</plugins>
//获取第几页 一页中有多少记录
PageHelper.startPage(1, 3);
List<Student> students = studentDao.selectAll();
students.forEach(stu-> System.out.println(stu));
<select id="selectAll" resultType="student">
select * from mstudent order by id
</select>
执行过程如下:
==> Preparing: SELECT count(0) FROM mstudent
==> Parameters:
<== Columns: COUNT(0)
<== Row: 17
<== Total: 1
==> Preparing: SELECT * FROM ( SELECT TMP_PAGE.*, ROWNUM PAGEHELPER_ROW_ID FROM ( select * from mstudent order by id ) TMP_PAGE) WHERE PAGEHELPER_ROW_ID <= ? AND PAGEHELPER_ROW_ID > ?
==> Parameters: 3(Long), 0(Long)
<== Columns: ID, NAME, EMAIL, AGE, PAGEHELPER_ROW_ID
<== Row: 1, xiao, 123@qq.com, 10, 1
<== Row: 2, liu, 124@qq.com, 11, 2
<== Row: 3, 安安, 1235@qq.com, 25, 3
<== Total: 3
Student{id=1, name='xiao', email='123@qq.com', age=10}
Student{id=2, name='liu', email='124@qq.com', age=11}
Student{id=3, name='安安', email='1235@qq.com', age=25}