文章目录
一、什么是mybatis
- mybatis是apache组织下的一个开源框架。以前叫ibatis
- mybatis是一个持久层的框架解决了orm的问题。
- 可以简化jdbc的编码。
orm:对象关系映射。java是面向对象的语言,数据库是关系型数据库,两者阻抗不匹配。需要有一个中间媒介对两者进行转换。orm映射就是这个中间媒介。orm框架可以将java中的对象转换成数据库表里面的一行数据。也可以将表中的一行数据转换为java中的对象。
二、搭建mybatis的开发环境
HttpSession是浏览器和tomcat之间的会话,SqlSession是tomcat和数据库之间的会话。
- 引入依赖。
- 写mybatis的主配置文件
- 写入主函数
具体步骤:
- 引入jar包
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc</artifactId>
<version>6.0</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
提示:ojdbc的jar包由于版权问题,maven的远程仓库里面没有收录该jar包,我们需要在本地仓库里面手动上传。我们提供的本地仓库里面有ojdbc6这个jar包,当我们手动清除本地仓库后,需要专门为ojdbc6复制一份。
- 写mybatis的主配置文件。(放在resources里面.xml文件)
<?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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<!-- 数据库的连接信息 -->
<dataSource type="POOLED">
<property name="driver" value="oracle.jdbc.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />
<property name="username" value="hr" />
<property name="password" value="hr2" />
</dataSource>
</environment>
</environments>
<!-- <mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml" />
</mappers> -->
</configuration>
- 写主函数进行测试
public static void main(String[] args) throws IOException {
//SqlSession
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sqlSessionFactory.openSession();
System.out.println(session);
session.close();
}
三、查询操作
- 建表,插入数据。
- 创建实体类。
- 创建dao 接口。
- 在resource里面先创建文件夹。
com/baizhi/dao
. 注意不能建成com.baizhi.dao .创建好文件夹后,创建一个映射文件。StudentMapper.xml。这个文件内容是复制来的。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace为dao接口的全限定名
指定该映射文件为StudentDao接口服务
-->
<mapper namespace="com.baizhi.dao.StudentDao">
<!--
select标签 对应了select操作
id是dao接口中的方法名,resultType是接口的方法的返回值(实体类的全限定名) -->
<select id="selectOne" resultType="com.baizhi.entity.Student">
<!-- 定义该方法要执行的sql语句,其中#{}是占位符,大括号里面的值可以是任意值
所以要保证dao接口中的方法的参数的个数是一个。如果是两个的话,需要用其他的方式解决。
-->
select * from student where id=#{id}
</select>
</mapper>
- 在主配置文件中的对mapper文件进行注册。
<mappers>
<!-- 告知mybatis有了这个映射文件,注册映射文件 -->
<mapper resource="com/baizhi/dao/StudentMapper.xml" />
</mappers>
- 到主函数里面使用SqlSession对象执行方法。
public static void main(String[] args) throws IOException {
//将配置文件转换为流对象
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
//创建出SqlSession的工厂类。要把配置文件做为参数传入。
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
//得到session对象。就是数据库的连接
SqlSession session = sqlSessionFactory.openSession();
//获取到dao接口的对象。mybatis底层通过java代码动态的产生了一个接口的实现类。getMapper方法就返回了该实现类的对象。
StudentDao sd = session.getMapper(StudentDao.class);
Student student = sd.selectOne(41);
System.out.println(student);
//释放资源
session.close();
}
忘记注册映射文件时报的错误
Type interface com.baizhi.dao.StudentDao is not known to the MapperRegistry.
四、增删改查
查
- 在dao接口定义selectAll方法。
- 在映射文件中为selectAll定义sql语句。
<!-- resultType不用定义成集合,需要指定的是sql查询结果映射到哪个类上。mybatis会自动根据查询的条数决定放入到list里面还是一个实体里面。 -->
<select id="selectAll" resultType="com.baizhi.entity.User">
select * from t_user
</select>
增删改
- 在dao接口定义方法。跟以前一样。
- 在映射文件中定义标签。
- 添加的标签是insert
- 删除的标签是delete
- 修改的标签是update。
不管是哪个标签id一定是跟dao接口的方法名一致。
<!-- insert标签是用来做插入操作的
id还是dao的方法名
-->
<!-- sql语句中的占位符直接写成方法参数(Student对象)的属性(name,age) -->
<insert id="insert">
insert into student values(stu_seq.nextval,#{name},#{age})
</insert>
- 在主函数里面执行完增删改操作后,需要手动提交事务。
//手动提交事务。因为mybatis跟jdbc不一样,不会自动提交事务
session.commit();
特别注意:
mybatis跟jdbc做dml操作时,不一样,不会自动提交事务。需要程序员手动将事务提交。否则控制台不报错,但是表里面没有数据。
五、junt测试
junit是一个测试工具,可以在一个测试类中定义多个测试方法。
package com.baizhi;
import org.junit.Test;
public class TestDao {
/**
* 测试方法的返回值一定是void,没有参数
* 这种测试被称为junit测试。好处一个类中可以定义多个测试方法。
*/
@Test
public void testInsert(){
System.out.println("111");
}
@Test
public void test2(){
System.out.println("2222");
}
}
注意事项:
- 测试方法上面需要添加@Test注解。
- 测试方法的返回值是void,没有参数。
六、dao中多个参数问题
使用@Param对参数起别名。然后在sql语句中使用#{别名}
七、动态SQL
程序员在写代码代码的时候不能确定sql语句样子,等程序运行的时候才能确定sql的具体内容。
在映射文件中写sql语句的时候我们可以使用mybatis提供的标签实现sql语句的动态变化。
where标签:自动判断sql语句是否需要添加where关键字
if标签:做条件分支。
1. 在xml中小于和大于是特殊字符,需要进行转义。< :< >:>
2. 当使用了动态sql中的if标签,我们的dao方法中的参数必须用@Param起别名。否则if判断里面获取不到参数。
多条件查询
dao层:
public List<Emp> selectAllEmpAndDept(@Param("name")String name,@Param("minAge")Integer minAge,@Param("maxAge")Integer maxAge);
映射文件:
<select id="selectAllEmpAndDept" resultMap="empMap">
select e.*,d.id did,d.name dname,d.loc loc
from t_ems_employees e left join t_ems_departments d
on e.deptid=d.id
<where>
<if test="name!=null">
e.name like '%'||#{name}||'%'
</if>
<if test="minAge!=null">
and e.age > #{minAge}
</if>
<if test="maxAge!=null">
and e.age < #{maxAge}
</if>
</where>
</select>
打印sql语句
log4j是一个日志记录工具,可以在控制台上打印出来程序运行的过程。mybatis底层采用了该工具进行了日志打印,如果我们需要在控制上打印出详细的日志信息,需要对mybatis进行如下配置。
- 在resources文件夹里面创建配置文件log4j.properties
log4j.rootLogger=ERROR,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} %m%n
log4j.logger.org.springframework=DEBUG
log4j.logger.com.baizhi.dao=DEBUG
- 引入依赖
<!--日志记录工具-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
进过上面的配置之后,项目中就可以打印出真正执行的sql语句。
//表示执行了哪条sql语句
[DEBUG] 2021-04-25 14:37:50,202 ==> Preparing: select * from train_info WHERE mile > ? and mile < ?
//执行sql语句时传递的参数
[DEBUG] 2021-04-25 14:37:50,284 ==> Parameters: 600(Integer), 1200(Integer)
//sql语句执行查询出来了几条数据。
[DEBUG] 2021-04-25 14:37:50,362 <== Total: 6
批量删除
dao:
void deleteMany(@Param("ids") String[] ids);
映射文件:
<delete id="deleteMany">
delete from train_info where train_no in
<!--
collection:表示dao方法的参数是什么类型,数组使用array,list集合使用list.要dao方法只有一个参数,没有注解。
在dao方法中如果使用了注解,我们需要把collection值配置为注解名字。
open和close是一组的,表示循环数据前后添加什么字符。
item:表示每次循环获取到的元素。
separator:表示元素与元素之间的分割符
-->
<foreach collection="ids" open="(" close=")" item="a" separator=",">
#{a}
</foreach>
</delete>
八、数据库中字段名和实体类属性名不一致问题
有两种解决办法:
-
在执行sql语句的时候直接跟select后面起别名。
-
使用resultMap标签指定映射关系。
第一种:
执行sql的结果跟实体类的属性名已经一致了。这样就会自动的做映射。第二种:
这种使用mapper映射的办法是以后的主流方式。
<resultMap type="product" id="proMap">
<id column="product_id" property="id"/>
<result column="product_name" property="name"/>
<result column="price" property="price"/>
<result column="product_desc" property="desc"/>
</resultMap>
<select id="selectAll" resultMap="proMap">
select * from t_product
</select>
总结:
在映射文件中的select标签里面什么时候用resultType什么时候用resultMap?
一致时用:resultType
不一致时用:resultMap
九、映射文件中对实体类起别名
<!-- 每次映射文件中使用实体类时都要使用全限定名,很麻烦,我们可以给实体类起别名 -->
<typeAliases>
<!-- 给Product的类起了别名p,以后在Mapper文件就可以使用p替换全限定名 -->
<!-- <typeAlias type="com.baizhi.entity.Product" alias="product"/> -->
<!-- 给包下面所有的类起别名。 类名首字母大小写都可以。 -->
<package name="com.baizhi.entity"/>
</typeAliases>
注意这个标签要写在配置数据库连接信息的上面,否则会报错。
整体结构如下:
<?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>
<typeAliases>
<!--<typeAlias type="com.baizhi.entity.Train" alias="Train"></typeAlias>-->
<!--给这个包中所有的类都起别名,别名为类名-->
<package name="com.baizhi.entity"/>
</typeAliases>
<environments default="dev">
<!--开发的数据库连接信息-->
<environment id="dev">
<transactionManager type="JDBC" />
<!-- 数据库的连接信息 -->
<dataSource type="POOLED">
<property name="driver" value="oracle.jdbc.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />
<property name="username" value="hr" />
<property name="password" value="hr" />
</dataSource>
</environment>
</environments>
<mappers>
<!--<mapper resource="com/baizhi/dao/TrainMapper.xml" />-->
<!--自动扫描com.baizhi.dao包里面的映射文件。
要求:dao的接口名要跟映射文件的文件名一致。都叫XxxMapper
-->
<package name="com.baizhi.dao"/>
</mappers>
</configuration>
简化主配置文件中注册映射文件
缺了注册步骤会导致系统报错。mybatis提供了一个标签可以一次性解决该问题。
<mappers>
<!--<mapper resource="com/baizhi/dao/TrainMapper.xml" />-->
<!--自动扫描com.baizhi.dao包里面的映射文件。
要求:dao的接口名要跟映射文件的文件名一致。都叫XxxMapper
-->
<package name="com.baizhi.dao"/>
</mappers>
注意事项:要求mapper文件跟dao类名名称一致。
十、 两表联查
左外链接查询=两表中有关联的数据+左表中独立的数据。
右外连接查询=两表中有关联的数据+右表中独立的数据。
内连接查询=两表中有关联的数据。
在多对一查询中我们一般使用左外链接查询。
多对一
步骤:
- 建表
--创建部门表
create table t_ems_departments(
id number(7) primary key,
name varchar2(30) not null,
loc varchar2(30) not null
);
create sequence ems_departments_seq;
insert into t_ems_departments values(ems_departments_seq.nextval,'技术部','北京');
insert into t_ems_departments values(ems_departments_seq.nextval,'销售部','上海');
insert into t_ems_departments values(ems_departments_seq.nextval,'市场部','郑州');
insert into t_ems_departments values(ems_departments_seq.nextval,'小卖部','广州');
select * from t_ems_departments
--创建员工表
drop table t_ems_employees
create table t_ems_employees(
id number(7) primary key,
name varchar2(30) not null,
salary number(10,2),
age number(3),
sex varchar2(10),
deptid references t_ems_departments(id) --外键
);
create sequence ems_employees_seq;
insert into t_ems_employees values(ems_employees_seq.nextval,'张三',3000,23,'男',1);
insert into t_ems_employees values(ems_employees_seq.nextval,'叶前伟',13000,23,'男',1);
insert into t_ems_employees values(ems_employees_seq.nextval,'张鹏鹏',10000,23,'男',1);
insert into t_ems_employees values(ems_employees_seq.nextval,'徐通达',14000,23,'男',2);
insert into t_ems_employees values(ems_employees_seq.nextval,'李鹏运',11000,23,'男',3);
- 编写查询的sql语句。
<select id="selectAllEmpAndDept" resultMap="empMap">
select e.*,d.id did,d.name dname,d.loc loc
from t_ems_employees e left join t_ems_departments d
on e.deptid=d.id
</select>
- 创建实体类。在多的一端创建一个属性是一的一端的对象。
public class Emp {
private int id;
private String name;
private double salary;
private int age;
private String sex;
private int deptId;
private Dept dept;
....
}
- 定义映射关系
<resultMap type="Emp" id="empMap">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="salary" property="salary"/>
<result column="sex" property="sex"/>
<result column="age" property="age"/>
<result column="deptid" property="deptId"/>
<association property="dept" javaType="Dept">
<id column="did" property="id"/>
<result column="dname" property="name"/>
<result column="loc" property="loc"/>
</association>
</resultMap>
一对多
- 实体类的设计,在一的一端写一个属性是list类型的。泛型是多的一端的类型
- sql语句的编写。
<select id="selectAllDeptAndEmps" resultMap="deptMap">
select d.id did,d.name dname,d.loc,e.*
from t_ems_departments d left join t_ems_employees e
on d.id=e.deptid
</select>
- 映射文件
<resultMap type="Dept" id="deptMap">
<id column="did" property="id"/>
<result column="dname" property="name"/>
<result column="loc" property="loc"/>
<collection property="empList" ofType="Emp">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="salary" property="salary"/>
<result column="sex" property="sex"/>
<result column="age" property="age"/>
<result column="deptid" property="deptId"/>
</collection>
</resultMap>
十一、 分页插件PageHelper
步骤:
1.引入jar包
<!--mybatis的分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.11</version>
</dependency>
2.在mybatis主配置文件mybatis-config.xml中注册该插件。
3.只要在dao里面提供查询所有的方法。
4.调用dao的时候需要两行代码
SqlSession session = MybatisUtil.getSession();
ProductDao pd = session.getMapper(ProductDao.class);
//设置分页信息。第一个参数是页号,第二个参数是一页显示的条数
PageHelper.startPage(1, 3);
//查询全部
List<Product> proList = pd.selectAll();
//把查询全部返回的数据封装到PageInfo对象里面。
PageInfo<Product> pi=new PageInfo<>(proList);
List<Product> list = pi.getList();//获取到当前页的所有数据
pi.getNextPage();//下一页的页号
pi.getPages();//总页数
boolean hasNextPage = pi.isHasNextPage();//是否有下一页
boolean hasPreviousPage = pi.isHasPreviousPage();//是否有上一页
pi.getPageNum();//当前页号
session.close();