文章目录
一、Mybatis简介
Mybatis是一款经典的ORM(对象关系映射)框架, Mybatis避免 了传统JDBC操作中设置参数、手动封装结果集等冗余的操作。可以使用简单的XML或注解完成对数据库的操作。相较于其他ORM框架,Mybatis支持定制SQL, 更容易学习。
可以使用Mybatis对数据库进行CRUD操作、结果集映射、动态SQL、缓存、与Spring框架整合以及逆向工程简化开发。
二、环境搭建
1、创建maven工程
2、导入jar包
配置pom.xml
<dependencies>
<!-- mybatis核心包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.1</version>
</dependency>
<!-- mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.29</version>
</dependency>
<!-- junit测试包 开发中不用导入-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
<!--日志包,方便看sql语句-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.1</version>
</dependency>
</dependencies>
<build>
<resources>
<!--默认编译resource下的配置文件不会编译java下的,这个配置以后就会执行-->
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<!--设置了编译resource下的配置文件不会再编译resource下的,默认配置需要显示写出来-->
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
</build>
3、创建实体类
部门类
package pojo;
import java.util.List;
public class Dept {
private Integer id;
private String name;
private List<Employee> employees;
//getter and setter方法略
}
员工类
package pojo;
import java.util.Date;
public class Employee {
private Integer id;//id
private String name;//姓名
private Dept dept;//部门
private String job;//职位
private Float salary;//薪水
private Date hireDate;//入职日期
//getter and setter方法略
}
4、创建表
部门表并添加数据
CREATE TABLE IF NOT EXISTS dept(id INT PRIMARY KEY,name VARCHAR(30));
INSERT INTO dept VALUES(1,"产品部");
INSERT INTO dept VALUES(2,"设计部");
INSERT INTO dept VALUES(3,"开发部");
INSERT INTO dept VALUES(4,"测试部");
INSERT INTO dept VALUES(5,"运营部");
INSERT INTO dept VALUES(6,"销售部");
INSERT INTO dept VALUES(7,"财务部");
INSERT INTO dept VALUES(8,"人事部");
INSERT INTO dept VALUES(9,"行政部");
员工表并添加数据
CREATE TABLE IF NOT EXISTS employee(
id INT PRIMARY KEY,
name VARCHAR(30),
dept_id INT,
job VARCHAR(30),
salary FLOAT,
hire_date DATE,
CONSTRAINT foreign key(dept_id) references dept(id)
);
INSERT INTO employee VALUES(1,"张三",1,"产品总监",29999,"2000-01-01");
INSERT INTO employee VALUES(2,"李四",3,"开发工程师",16000,"2015-01-01");
INSERT INTO employee VALUES(3,"王五",4,"测试工程师",12000,"2016-07-01");
INSERT INTO employee VALUES(4,"赵六",6,"销售",8000,"2000-01-01");
INSERT INTO employee VALUES(5,"张三",8,"人事经理",15000,"2016-01-01");
5、创建Mapper映射文件
在dao.mapper下面创建实体类映射文件DeptMapper.xml和EmployeeMapper.xml,两个文件只是mapper节点namespace值不一样,一个工程中namespace值要唯一,通常一个Mapper文件对应一张表的增删改查,namespace可以设置成表名。
空的Mapper映射文件如下:
<?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">
<mapper namespace="DEPT">
<!--<![CDATA[你的注释]]>-->
</mapper>
注意:Mapper文件如果要写中文注释,需要用<![CDATA[]]>将中文注释包起来,否则运行时会报编码错误。或者把工程的文件编码设置成UTF-8。
IDEA设置编码两种方式:
1)、File->Settings->Editor->File Encodings这种方式修改的文件编码方式只对当前project起作用,每次新建了一个工程后还需要重新设置编码方式。
2)、File->Other Settings->Default Settings->Editor->File Encodings,这儿设置的是默认的文件编码方式,所有新建的工程使用的都是默认的文件编码方式。
6、创建配置文件
mybatis配置文件mybatis-config.xml如下:
注意configuration中节点的位置是有先后顺序,写错了会看到提示。
<?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>
<!-- 类型别名,表示可以使用 Dept 来代替 pojo.Dept -->
<!-- 类型别名就是可以给类的全路径起一个简称 -->
<typeAlias type="pojo.Dept" alias="Dept"/>
<typeAlias type="pojo.Employee" alias="Employee"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!-- 数据库连接信息 -->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--实体类的映射文件,在dao.mapper包下创建完Mapper文件后在此追加即可-->
<mappers>
<mapper resource="dao/mapper/DeptMapper.xml"/>
<mapper resource="dao/mapper/EmployeeMapper.xml"/>
</mappers>
</configuration>
7、添加log4j日志文件
log4j.rootLogger=DEBUG,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
#begin
#for normal test, delete when online
log4j.logger.com.ibatis=DEBUG
1og4j.logger.com.ibatis.common.jdbc.SimpleDataSource=DEBUG
log4j.logger.com.ibatis.common.jdbc.ScriptRunner=DEBUG
1og4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=DEBUG
1og4j.logger.java.sql.Connection=DEBUG
1og4j.logger.java.sql.Statement=DEBUG
1og4j.logger.java.sql.PreparedStatement=DEBUG
1og4j.1ogger.java.sq1.ResultSet=DEBUG
#end
三、使用Mybatis进行增删改
使用Mybatis操作数据库,需要将SQL 写在Mapper映射文件中。sql语句封装到标签中,对应增删改查的操作,有四种标签
<insert>
用于执行insert插入数据
<delete>
用于执行delete删除数据
<update>
用于执行update修改数据
<select>
用于执行select查询数据
每个标签都有一个id属性, 在同一个Mapper文件中,每条语句的id都是唯一的,在执行语句的时候,通过namespace.id来调用语句。 sql语句中需要传入参数的时候,通过parameterType指定参数的类型,可以是基本数据类型,也可以是封装好的类或集合。通过#属性}或${属性}来得到参数值。
1、添加数据
以DEPT表为例。所有的语句都写在XXMapper.xml的节点中。 在Mybatis中,添加数据使用insert标签:
<mapper namespace="DEPT">
<!--parameterType指定传入参数的类型-->
<insert id="addDept" parameterType="Dept" keyProperty="id" useGeneratedKeys="true">
insert into DEPT(name) values (#{name})
</insert>
</mapper>
此标签对应生成的语句是: insert into DEPT (NAME) values (?)
parameterType="Dept"
指定了调用语句时,传入的参数是Dept类型的,#{name}
会获取到Dept对象中的name属性值,并在执行时替换掉语句中的?占位。
insert标签属性说明:
1)、 id
在同一个Mapper文件中,每条语句的id都是唯一的, 在执行语句的时候,通过namespace.id
来调用语句。
2) 、parameterType
是指参数类型,sql语句执行的时候需要绑定参数,通常我们将参数封装到实体类中。parameterType的值可以是类的全路径如pojo.Dept,也可以是mybatis-config.xml文件中typeAlias中配置的别名alias。
3) 、keyProperty
仅对insert有用,标记一个属性, MyBatis 会通过getGeneratedKeys或者通过insert 语句的selectKey 子元素设置它的值。默认:不设置。当添加完数据后, 如果想获得到刚刚添加的这条数据的主键,同时设置keyProperty和useGeneratedKeys即可。
4)、useGeneratedKeys
仅对insert语句有用,告诉MyBatis使用JDBC的getGeneratedKeys方法来取出由数据(比如:像MySQL和sQLServer这样的数据库管理系统的自动递增字段)内部生成的主键。默认值: false。
测试代码:
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import pojo.Dept;
import java.io.IOException;
import java.io.Reader;
public class InsertTest {
@Test
public void testInsert(){
String resource="mybatis-config.xml";//配置文件名
Reader reader = null;//读取配置文件的工具
SqlSession session = null;
try {
//使用MyBatis提供的Resources类加载配置文件
reader = Resources.getResourceAsReader(resource);
//创建sqlSession的工厂对象
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
//创建能执行映射文件中sql的sqlSession对象
session = sessionFactory.openSession();
//创建要insert到数据库中的实体类对象
Dept dept = new Dept();
dept.setName("新媒体");
//使用session的insert方法执行添加操作
//第一个参数是Mapper文件的namespace和语句的id
//第二个参数的类型和语句的parameterType一致
session.insert("DEPT.addDept",dept);
session.commit();//增删改要提交事务
System.out.println(dept.getId());//获取主键
} catch (IOException e) {
e.printStackTrace();
session.rollback();//有异常事务回滚
} finally {
//关闭资源
if(session!=null){
session.close();
}
try {
if (reader!=null) {
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
运行结果:
为了简化代码,把打开连接和关闭连接封装到@Before
和@After
里
String resource="mybatis-config.xml";//配置文件名
Reader reader = null;//读取配置文件的工具
SqlSession session = null;
@Before
public void open(){
try {
//使用MyBatis提供的Resources类加载配置文件
reader = Resources.getResourceAsReader(resource);
//创建sqlSession的工厂对象
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
//创建能执行映射文件中sql的sqlSession对象
session = sessionFactory.openSession();
} catch (IOException e) {
e.printStackTrace();
}
}
@After
public void close(){
if(session!=null){
session.close();
}
try {
if (reader!=null) {
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
简化后的测试方法
@Test
public void testInsert(){
try {
Dept dept = new Dept();
dept.setName("研发部");
session.insert("DEPT.addDept",dept);
session.commit();//增删改要提交事务
System.out.println(dept.getId());//获取主键
} catch (Exception e) {
e.printStackTrace();
session.rollback();//有异常事务回滚
}
}
2、修改数据
<update id="updateDept" parameterType="Dept">
update DEPT set name=#{name} where id=#{id}
</update>
测试代码:
@Test
public void testUpdate(){
try {
Dept dept = new Dept();
dept.setId(13);
dept.setName("市场部");
int res = session.update("DEPT.updateDept", dept);
System.out.println(res);
session.commit();
} catch (Exception e) {
e.printStackTrace();
session.rollback();
}
}
3、删除数据
<delete id="deleteDept" parameterType="int">
delete from dept where id=#{id}
</delete>
delete根据主键查询,Dept主键 是Integer的。没有必要封装进类里,上面的语句中parameterType="int”,其中int是Integer的别名。
Mybatis默认的对基本数据类型都有起了别名。基本数据类型的参数,直接写类型或者类名小写即可。比如java.lang.Integer别名有Integer、integer、int虽然不是对象类型,但是int可以自动转化成Integer.语句中使用了#{id}获取具体的参数值,但是int类型中并没有一个叫id的属性。可是测试发现语句仍然能执行成功。所以在parameterType=”基本数据类型”的时候,可以通过#任意名称}获得参数值。这里将#{id}写成#{abc}也可以。
测试代码:
@Test
public void testDelete(){
try {
int res = session.delete("DEPT.deleteDept", 41);
System.out.println(res);
session.commit();
} catch (Exception e) {
e.printStackTrace();
session.rollback();
}
}
四、Mybatis数据查询
1、直接查询
属性通过对象属性名映射到实体类,用resultType要求结果集的字段名(有别名以别名为准)和实体类的属性名一致才能映射,否则无法映射。
<select id="getDeptList" resultType="Dept">
select id,name from dept
</select>
测试代码:
@Test
public void testGetDeptList(){
List<Dept> Depts = session.selectList("DEPT.getDeptList");
for(Dept dept:Depts){
System.out.println(dept);
}
}
2、查询结果的映射
Mybatis通过<select>
进行查询。通过<resultMap>
将查询结果封装成实体类:
<resultMap id="empMap" type="Employee">
<!-- id是主键标签 -->
<!-- property是类名的属性,column是类名查询出来的字段 -->
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="dept.id" column="dept_id"/>
<result property="dept.name" column="dname"/>
<result property="job" column="job"/>
<result property="salary" column="salary"/>
<result property="hireDate" column="hire_Date"/>
</resultMap>
<select id="getEmpList" resultMap="empMap">
select id,name,dept_id,job,salary,hire_Date from employee
</select>
<resultMap>
用于封装结果集。一个Mapper中可以有多个<resultMap>
,每个<resultMap>
的id是唯一-的。 type用于指定结果集对应的实体类类型。type=“Dept” 即将<resultMap>
中所有的属性都对应到Dept类中。
我们可以这样理解,一个查询语句,从查询出结果,到封装成实体类,经历如下步骤:
1.当使用<select>
查询出结果后,会根据<select>
中resultMap="deptMap"找到id="deptMap"的<resultMap>
标签
2.根据<resultMap>
的type="Dept"找到Dept对应的类,这里Dept是别名,也可以写成全路径pojo.Dept。
3. <resultMap>
中的<result>
标签,将查询结果中的字段与实体类中的属性对应起来,property="name” 是找到Dept类中的name属性,column="NAME”中NAME是SQL语句中NAME字段对应的值,通过setName方法将结果集绑定到name属性上。
需要注意的是column="NAME"中的NAME不是数据库中的字段名,而是select语句中查询出来的字段别名,因为我们可以用as去给字段起别名。如上面改变上面的语句,为查询结果起别名,对应的column也要改变
测试代码:
@Test
public void testEmplist(){
List<Employee> list = session.selectList("EMPLOYEE.getEmpList");
for(Employee employee:list){
System.out.println(employee);
}
}
session.selectList适用于查询结果是一个集合的情况,如果实现按主键
查询,则使用session.selectOne方法。 如按主键查询:
<select id="getDeptById" parameterType="int' resultMap= " deptMap">
select ID, NAME from DEPT where ID=#{
id}
</select>
测试代码:
@Test
public void testSelectOne() {
Dept dept = session.selectOne( "DEPT. getDeptById", 21);
System.out.println( dept.getName()) ;
resultMap还支持继承:
<resultMap id=" deptMap" type="Dept">
<id column="ID" property="id"/>
<result column="NAME" property="name"/>
</resultMap>
<resultMap id="deptMapChild" type=