映射文件
增删改查
之前已经演示过查询操作,这里重点来说一下增删改操作,使用接口的形式来映射
public interface EmployeeDao {
Employee getEmployeeById(Integer id);
void addEmployee(Employee employee);
void udpateEmployee(Employee employee);
void deleteEmployee(Integer id);
}
增加一行数据
这里特别注意的地方是:sqlSessionFactory.openSession()默认设置的是不自动提交,所以在做完对数据表的操作之后要手动提交数据
@Test
public void test03(){
SqlSessionFactory sqlSessionFactory = null;
SqlSession sqlSession = null;
try {
sqlSessionFactory = MyBatisTest.getSqlSessionFactory();
sqlSession = sqlSessionFactory.openSession();
EmployeeDao mapper = sqlSession.getMapper(EmployeeDao.class);
Employee employee = new Employee(null, "Make", "1", "make@123.com");
mapper.addEmployee(employee);
System.out.println(employee);
sqlSession.commit();
} catch (IOException e) {
e.printStackTrace();
}finally {
sqlSession.close();
}
}
insert语句是可以获取插入数据的返回值的,例如主键id是自增的,那么我们设置keyProperty=“id”,会自动将id值返回到对象中
<insert id="addEmployee" useGeneratedKeys="true" keyProperty="id">
insert into employee (last_name,gender,email) values (#{lastName},#{gender},#{email})
</insert>
修改一条数据
@Test
public void test04(){
SqlSessionFactory sqlSessionFactory = null;
SqlSession sqlSession = null;
try {
sqlSessionFactory = MyBatisTest.getSqlSessionFactory();
sqlSession = sqlSessionFactory.openSession();
EmployeeDao mapper = sqlSession.getMapper(EmployeeDao.class);
mapper.udpateEmployee(new Employee(5,"Jim","1","jim@123.com"));
sqlSession.commit();
} catch (IOException e) {
e.printStackTrace();
}finally {
sqlSession.close();
}
}
<update id="udpateEmployee">
update employee set last_name = #{lastName} , gender = #{gender} , email = #{email} where id = #{id}
</update>
删除一条数据
@Test
public void test05(){
SqlSessionFactory sqlSessionFactory = null;
SqlSession sqlSession = null;
try {
sqlSessionFactory = MyBatisTest.getSqlSessionFactory();
sqlSession = sqlSessionFactory.openSession(true);
EmployeeDao mapper = sqlSession.getMapper(EmployeeDao.class);
mapper.deleteEmployee(7);
} catch (IOException e) {
e.printStackTrace();
}finally {
sqlSession.close();
}
}
<delete id="deleteEmployee">
delete from employee where id = #{id}
</delete>
参数处理
在给sql语句注入参数时,会有三种不同的情况,以下我们一一讲解
无参数注入
直接执行sql语句,这没什么好说的
单个参数注入
MyBatis不会对参数进行特殊处理,只需要使用#{参数名}即可获取参数
多个参数注入
MyBatis会对参数进行特殊处理,多个参数会被封装成一个map对象,调用时使用#{key}即可
key为param1,param2…或参数的索引
value为传入的参数值
这个方法非常不方便我们阅读sql语句,所以MyBatis也为我们提供了命名参数的说法,比如我现在要根据id和lastName查询数据
public interface EmployeeDao {
//@Param("id"),括号中的值为#{}中的值
Employee getEmployeeByIdAndName(@Param("id") Integer id,@Param("lastName") String lastName);
}
POJO
如果多个参数正好是我们业务逻辑的数据模型,我们就可以直接传入pojo;
#{属性名}:取出传入的pojo的属性值
Map
如果多个参数不是业务模型中的数据,没有对应的pojo,不经常使用,为了方便,我们也可以传入map
#{key}:取出map中对应的值
TO
如果多个参数不是业务模型中的数据,但是经常要使用,推荐来编写一个TO(Transfer Object)数据传输对象
参数封装为Map的过程
来看一下源码,下面这段源码分别对无参数、单个参数、多个参数分别进行了处理
public Object getNamedParams(Object[] args) {
int paramCount = this.names.size();
//如果参数为null直接返回
if (args != null && paramCount != 0) {
//如果是单个参数,且没有呗@param标注,arg[0]直接返回
if (!this.hasParamAnnotation && paramCount == 1) {
Object value = args[(Integer)this.names.firstKey()];
return wrapToMapIfCollection(value, this.useActualParamName ? (String)this.names.get(0) : null);
} else {
//如果为多个参数或者参数呗@param标识
Map<String, Object> param = new ParamMap();
int i = 0;
//遍历names集合
for(Iterator var5 = this.names.entrySet().iterator(); var5.hasNext(); ++i) {
Entry<Integer, String> entry = (Entry)var5.next();
//将数据存为key=下标,也就是取值的时候可以使用#{0},#{1}来表示所对应的值
param.put(entry.getValue(), args[(Integer)entry.getKey()]);
String genericParamName = "param" + (i + 1);
//将key设置为"param" + (i + 1)的形式,可以是用#{param1},#{param2}来表示所对应的值
if (!this.names.containsValue(genericParamName)) {
param.put(genericParamName, args[(Integer)entry.getKey()]);
}
}
return param;
}
} else {
return null;
}
}
#{}与${}的区别
两者都是用来获取map中的值或者pojo对象属性的值,通过一个例子来看一下区别,设置id的取值为${},lastname的取值为#{}
<select id="getEmployeeByIdAndName" resultType="com.yellowstar.mybatis.bean.Employee">
select * from employee where id = ${id} and last_name = #{lastName}
</select>
日志输出的sql语句为
Preparing: select * from employee where id = 1 and last_name = ?
Parameters: Tom(String)
不难看出,#{}是以预编译的形式,将参数设置到sql语句中,防止sql注入,而${}是直接将值拼接到sql语句中会出现安全问题
select用法
返回List集合
接收数据的类型应为List的泛型类型
<!--List<Employee> getEmployeeResultList(String name);-->
<select id="getEmployeeResultList" resultType="com.yellowstar.mybatis.bean.Employee">
select * from employee where last_name like #{name}
</select>
返回map集合(返回值类型为map)
返回map集合时,如果我们设置返回值类型为map,则给我返回一个map集合,集合的值为key=value,其中key的值为列名,value值为那一列的数据
<!--Map<Integer,Object> getEmployeeResultMap(String name);-->
<select id="getEmployeeResultMap" resultType="map">
select * from employee where id = #{id}
</select>
返回map集合(返回值类型为实体类对象)
返回map集合,需要返回实体类对象,并且将主键值赋值给key
@MapKey("id") //作用:将id设为map集合的key值
Map<Integer, Employee> getEmployeeLikeLastNameResultMap(String lastName);
<!--Map<Integer,Employee> getEmployeeLikeLastNameResultMap(String lastName);-->
<select id="getEmployeeLikeLastNameResultMap" resultType="com.yellowstar.mybatis.bean.Employee">
select * from employee where last_name like #{name}
</select>
resultMap
resultMap和resultType作用类似,且更加强大,两者不能得兼,使用一种即可
<!--Employee getEmployeeById(Integer id);-->
<!--resultMap
id:唯一标识
type:返回值类型的全类名
-->
<resultMap id="myResultMap" type="com.yellowstar.mybatis.bean.Employee">
<!--id、result的作用是将数据库中的字段名与实体类的属性名对应起来-->
<!--id用来表示主键-->
<id column="id" property="id"/>
<!--result表示普通列,如不设置则调用系统默认的值-->
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
</resultMap>
<select id="getEmployeeById" resultMap="myResultMap">
select * from employee where id = #{id}
</select>
级联查询:多对一
上面讲到resultMap的作用是将数据库中的字段名与实体类的属性名相对应,有一种情况是我们的实体类中包含另一个对象的属性
比如员工类与部门类,每个员工通过部门id与部门类相关联
public class Employee {
private Integer id;
private String lastName;
private String gender;
private String email;
private Department department;
}
public class Department {
private Integer id;
private String d_name;
}
数据库视图是这样构造的,其中员工表中的d_id与部门表中的id相关联
现在我们的需求是根据员工id查出员工信息以及他的部门信息
第一种方案:直接赋值
在查询sql语句时,通过左连接的方式将两张表关联起来
<!--Employee getEmployeeAndDeptById(Integer id);-->
<resultMap id="myResultMap2" type="com.yellowstar.mybatis.bean.Employee">
<!--id用来表示主键-->
<id column="id" property="id"/>
<!--result表示普通列,如不设置则调用系统默认的值-->
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
<result column="dept_id" property="department.id"/>
<result column="department_name" property="department.d_name"/>
</resultMap>
<select id="getEmployeeAndDeptById" resultMap="myResultMap2">
SELECT e.id id,e.last_name,e.gender,e.email,e.d_id,d.id dept_id,d.department_name FROM `employee` e
left JOIN `department` d
on e.d_id = d.id
where e.id = #{id}
</select>
第二种方案:使用association属性
个人理解为一个resultMap的嵌套,与上面的方法大致相同
<!--Employee getEmployeeAndDeptById(Integer id);-->
<resultMap id="myResultMap3" type="com.yellowstar.mybatis.bean.Employee">
<!--id用来表示主键-->
<id column="id" property="id"/>
<!--result表示普通列,如不设置则调用系统默认的值-->
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
<association property="department" javaType="com.yellowstar.mybatis.bean.Department">
<id column="dept_id" property="id"/>
<result column="department_name" property="d_name"></result>
</association>
</resultMap>
<select id="getEmployeeAndDeptById" resultMap="myResultMap3">
SELECT e.id id,e.last_name,e.gender,e.email,e.d_id,d.id dept_id,d.department_name FROM `employee` e
left JOIN `department` d
on e.d_id = d.id
where e.id = #{id}
</select>
第三种方案:分步查询&延迟加载
这里可以简化sql语句,以我们自己的思维方式来解决,需要查找员工所对应的部门,先找到员工,并且找到员工数据中的d_id值,在根据d_id去部门表中查询部门信息。而mybatis为我们准备了这种写法,就是分步查询
<!--第一步:根据id查找员工信息-->
<!--Employee getEmployeeById(Integer id);-->
<resultMap id="getEmpStep" type="com.yellowstar.mybatis.bean.Employee">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
<!--
select:下一步所调用的方法
column:传递给下一步方法的参数
-->
<association property="department" select="com.yellowstar.mybatis.dao.DepartmentDao.getDepartmentById" column="d_id"></association>
</resultMap>
<select id="getEmployeePlusById" resultMap="getEmpStep">
select * from employee where id = #{id}
</select>
<!--Department getDepartmentById(Integer id);-->
<select id="getDepartmentById" resultType="com.yellowstar.mybatis.bean.Department">
select id,department_name d_name from department where id = #{id}
</select>
到这里其实个人觉得分步查询不如直接在sql语句中写好,因为分步查询与数据库进行了两次交互,mybatis为我们提供了延迟加载的机制,意思是我们用到此数据时再去发起sql语句进行查找,比如查找员工姓名,只会与数据库进行一次交互,不会发起第二条sql语句,我们需要在全局配置文件中设置
<settings>
<!--开启延时加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
级联查询:一对多
实际开发过程中,也可能遇到一对多的情况,比如要查找该部门下的所有员工,稍微修改一个部门类,添加员工集合
public class Department {
private Integer id;
private String d_name;
private List<Employee> emps;
}
第一种方案:使用collection
<!--Department getDeptfotAllEmp(Integer id);-->
<resultMap id="getDeptforAllEmp" type="com.yellowstar.mybatis.bean.Department">
<id column="did" property="id"/>
<result column="department_name" property="d_name"/>
<!--也相当于resultMap的嵌套,用法跟association类似-->
<collection property="emps" ofType="com.yellowstar.mybatis.bean.Employee">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
</collection>
</resultMap>
<select id="getDeptforAllEmp" resultMap="getDeptforAllEmp">
SELECT d.id did,d.department_name department_name, e.id id,e.last_name last_name,e.gender gender,e.email email FROM `department` d
left join `employee` e
on d.id = e.d_id
where d.id = #{id}
</select>
第二种方案:分步查询&延迟加载
<!--Department getDeptByStep(Integer id);-->
<resultMap id="getDeptByStep" type="com.yellowstar.mybatis.bean.Department">
<id column="id" property="id"/>
<result column="department_name" property="d_name"/>
<collection property="emps" select="com.yellowstar.mybatis.dao.EmployeePlusDao.getEmployeeStep"
column="id"/>
</resultMap>
<select id="getDeptByStep" resultMap="getDeptByStep">
select id,department_name from department where id = #{id}
</select>
<!--Employee getEmployeeStep(Integer id);-->
<select id="getEmployeeStep" resultType="com.yellowstar.mybatis.bean.Employee">
select * from employee where d_id = #{id}
</select>
discriminator鉴别器
我个人将他理解为if判断器,通过一个例子来看一下,同样是使用上述的查找一个员工带出他的部门信息
要求:如果该员工性别为女(gender=0)带出她的部门信息,如果性别为男,不查找部门信息并且将他的email用姓名来赋值
<!--Employee getEmployeeById(Integer id);-->
<resultMap id="getEmpStep" type="com.yellowstar.mybatis.bean.Employee">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
<discriminator javaType="string" column="gender">
<case value="0">
<association property="department" select="com.yellowstar.mybatis.dao.DepartmentDao.getDepartmentById" column="d_id"></association>
</case>
<case value="1">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="last_name" property="email"/>
</case>
</discriminator>
</resultMap>
<select id="getEmployeePlusById" resultMap="getEmpStep">
select * from employee where id = #{id}
</select>