在MyBatis中,Mapper中的namespace用于绑定Dao接口的,即面向接口编程。namespace属性的值要和对应的Mapper接口的全限定名保持一致
它的好处在于当使用了namespace之后就可以不用写接口实现类,业务逻辑会直接通过这个绑定寻找到相对应的SQL语句进行对应的数据处理
insert,update,delete,select,sql,resultMap
sql:可被其它语句引用的可重用语句块;
resultMap:确定实体类属性与表中字段对应关系;
在,,,标签中,可以通过parameterType指定输入参数的类型,类型可以是简单类型、hashmap、pojo的包装类型。
parameterType属性是可以省略的.MyBatis框架可以根据SqlSession接口中方法的参数来判断输入参数的实际数据类型.
4.1、resultType属性存在标签.负责将查询结果进行映射.
4.2、resultType属性可以指定一个基本类型也可以是一个实体类类型
4.3、resultType属性无法与resultMap属性同时出现.
MyBatis框架中是根据表中字段名到实体类定位同名属性的.如果出现了实体类属性名与表中字段名不一致的情况,则无法自动进行对应.此时可以使用resultMap来重新建立实体类与字段名之间对应关系.
<!--mybatis-config文件配置,可以为一些类定义别名-->
<!--<typeAliases>
<typeAlias type="com.xyxy.pojo.Emp" alias="emp"></typeAlias>
</typeAliases>-->
<!-- type属性里面使用别名就可以不用全路径 -->
<resultMap id="empMapper" type="emp">
<id property="empId" column="emp_id"></id>
<result property="empName" column="emp_name"></result>
<result property="empSalary" column="emp_salary"></result>
</resultMap>
<select id="findAll" resultMap="empMapper">
select * from t_emp
</select>
sql:里面可以写入一个共同的sql代码,用于提取重复的代码。 要使用该代码的时候就直接使用标签 id:为提取的sql代码,取一个id,起标识作用
<!-- 公共代码 -->
<sql id="select">
<!-- select * from t_emp -->
select emp_id empId,emp_name empName,emp_salary empSalary from t_emp
</sql>
<!--select标签的id与接口的方法名对应,resultType和返回结果封装的Bean一致-->
<select id="findById" resultType="com.xyxy.pojo.Emp">
<!-- include:用于加载提取公共的sql语句,与<sql>标签对应
refid:填写<sql>标签中的id属性-->
<include refid="select"></include>
where emp_id=#{empId}
</select>
<select id="findByIdOrName" parameterType="Emp" resultType="emp">
<!-- 引入公共代码 -->
<include refid="select"></include>
<!-- where标签:一个where条件语句,通常和<if>标签混合使用 -->
<where>
<!-- if标签:执行一个判断语句,成立才会执行标签体内的sql语句,test:写上条件判断语句
注意:这里每一个if前面都尽量加上and,如果你是第一个条件,框架会自动帮你把and截取,
如果是第二个if就不能省略and-->
<if test="empId != null and empId != ''">
and emp_id=#{empId}
</if>
<if test="empName != null and empName != ''">
and emp_name like '%${empName}%'
</if>
</where>
</select>
Mybatis会在运行过程中,把配置文件中的SQL语句里面的**#{}转换为“?**”占位符,发送给数据库执行。
配置文件中的SQL:
<delete id="deleteEmployeeById" parameterType="int">
delete from t_emp where emp_id=#{empId}
delete>
实际执行的SQL:
delete from t_emp where emp_id=?
Mybatis会在运行过程中, 将来会根据${}拼字符串
配置文件中的SQL语句
<select id="selectEmployeeByName" resultType="com.xyxy.pojo.Employee">
select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_name like '%${empName}%'
select>
Mapper接口:
注意:由于Mapper接口中方法名是作为SQL语句标签的id,不能重复,所以Mapper接口中不能出现重名的方法,不允许重载!
#{} : 采用预编译方式,可以防止SQL注入
${}: 采用直接赋值方式,无法阻止SQL注入攻击
Mapper接口中的抽象方法
Employee selectEmployee(Integer empId);
映射配置文件:此时SQL语句中获取参数#{任意字符串}
<select id="selectEmployee" resultType="com.xyxy.pojo.Employee">
select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_id=#{empId}
select>
Mapper接口中抽象方法: 此时每个方法需要使用Param注解命名
int updateEmployee(@Param("empId") Integer empId,@Param("empSalary") Double empSalary);
映射配置文件:此时SQL语句中获取参数#{Param注解命的名}
<update id="updateEmployee">
update t_emp set emp_salary=#{empSalary} where emp_id=#{empId}
update>
Mapper接口中抽象方法:
int insertEmployee(Employee employee);
映射配置文件:此时SQL语句获取参数#{getXXX方法对应的名字,首字母改小写}
<insert id="insertEmployee">
insert into t_emp(emp_name,emp_salary) values(#{empName},#{empSalary})
insert>
Mybatis会根据#{}中传入的数据,加工成getXxx()方法,通过反射在实体类对象中调用这个方法,从而获取到对应的数据。填充到#{}这个位置。
Mapper接口中抽象方法:
int updateEmployeeByMap(Map<String, Object> paramMap);
映射配置文件: 此时SQL语句获取参数#{Map的key}
<update id="updateEmployeeByMap">
update t_emp set emp_salary=#{empSalaryKey} where emp_id=#{empIdKey}
update>
数据输出是针对查询数据的方法返回查询结果
Mapper接口中的抽象方法:方法的返回值是简单数据类型
/**
* 统计员工数量
* @return
*/
Long selectEmployeeCount();
映射配置文件: 此时标签的resultType的类型对应抽象方法的返回值类型
<select id="selectEmployeeCount" resultType="long">
select count(emp_id) from t_emp
select>
Mapper接口中的抽象方法:方法的返回值是POJO类型
Employee selectEmployee(Integer empId);
映射配置文件: 此时标签的resultType的类型对应抽象方法的返回值类型的全限定名
<select id="selectEmployee" resultType="com.xyxy.pojo.Employee">
select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_id=#{maomi}
select>
通过给数据库表字段加别名,让查询结果的每一列都和Java实体类中属性对应起来。
在Mybatis的核心配置文件中做下面的配置,select语句中可以不给字段设置别名
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
settings>
适用于SQL查询返回的各个字段综合起来并不和任何一个现有的实体类对应,没法封装到实体类对象中。能够封装成实体类类型的,就不使用Map类型。
Mapper接口中的抽象方法:方法的返回值是Map类型
/**
* 根据empId查询员工信息,并且将结果集封装到Map中
* @param empId
* @return
*/
Map selectEmployeeMapByEmpId(Integer empId);
映射配置文件: 此时标签的resultType的类型为map
<select id="selectEmployeeMapByEmpId" resultType="map">
select * from t_emp where emp_id=#{empId}
select>
查询结果返回多个实体类对象,希望把多个实体类对象放在List集合中返回。此时不需要任何特殊处理,在resultType属性中还是设置实体类类型即可。
Mapper接口中的抽象方法:方法的返回值是List
List<Employee> selectAll();
映射配置文件: 此时标签的resultType的类型为POJO类的全限定名
<select id="selectAll" resultType="com.xyxy.pojo.Employee">
select emp_id empId,emp_name empName,emp_salary empSalary from t_emp
select>
查询结果返回多个Map对象,希望把多个Map对象放在List集合中返回。此时不需要任何特殊处理,在resultType属性中还是设置map
即可。
Mapper接口中的抽象方法:方法的返回值是List
List<Map> selectAllMap();
映射配置文件: 此时标签的resultType的类型为map
<select id="selectAllMap" resultType="map">
select emp_id empId,emp_name empName,emp_salary empSalary
from t_emp
select>
例如:保存订单信息。需要保存Order对象和List
在保存List
insert into t_order_item(item_name,item_price,item_count,order_id) values(...)
这里需要用到的order_id,是在保存Order对象时,数据库表以自增方式产生的,需要特殊办法拿到这个自增的主键值。
Mapper接口中的抽象方法:
int insertEmployee(Employee employee);
映射配置文件:
<insert id="insertEmployee" useGeneratedKeys="true" keyProperty="empId">
insert into t_emp(emp_name,emp_salary)
values(#{empName},#{empSalary})
insert>
junit测试代码:
@Test
public void testSaveEmp() {
EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);
Employee employee = new Employee();
employee.setEmpName("john");
employee.setEmpSalary(666.66);
employeeMapper.insertEmployee(employee);
//打印自动返回存入对象的主键值
System.out.println("employee.getEmpId() = " + employee.getEmpId());
}
注意:
Mybatis是将自增主键的值设置到实体类对象中,而不是以Mapper接口方法返回值的形式返回。
另一种做法
<insert id="insertEmployee">
insert into t_emp (emp_name,emp_salary) values (#{empName},#{empSalary})
<selectKey keyColumn="emp_id" keyProperty="empId" resultType="int" order="AFTER">
select last_insert_id()
selectKey>
insert>
而对于不支持自增型主键的数据库(例如 Oracle),则可以使用 selectKey 子元素:selectKey元素将会首先运行,id 会被设置,然后插入语句会被调用
<insert id="insertEmployee"
parameterType="com.atguigu.mybatis.beans.Employee"
databaseId="oracle">
<selectKey order="BEFORE" keyProperty="id"
resultType="integer">
select employee_seq.nextval from dual
selectKey>
insert into orcl_employee(id,last_name,email,gender) values(#{id},#{lastName},#{email},#{gender})
insert>
或者:
<insert id="insertEmployee"
parameterType="com.atguigu.mybatis.beans.Employee"
databaseId="oracle">
<selectKey order="AFTER" keyProperty="id"
resultType="integer">
select employee_seq.currval from dual
selectKey>
insert into orcl_employee(id,last_name,email,gender) values(employee_seq.nextval,#{lastName},#{email},#{gender})
insert>
Mybatis在做结果集与POJO类的映射关系的时候,会自动将结果集的字段名与POJO的属性名(其实是和getXXX方法)进行对应映射,结果集的数据会自动映射给POJO对象中同名的属性; 所以当我们遇到表的字段名和POJO属性名不一致的情况,我们可以在编写查询语句的时候,给结果集的字段取别名,让别名与POJO的属性名一致以保证结果集的正确映射
因为我们表中字段的命名规则采用"_",而POJO的属性名命名规则采用驼峰命名法,所以导致我们在执行查询语句的时候总是要对查询的字段取别名,以确保正确地进行结果集映射
Mybatis框架当然也注意到了这个问题,所以它提供了一种自动识别驼峰命名规则的配置,我们只要做了该配置,那么全局的所有查询语句的执行都会自动识别驼峰命名规则
在Mybatis全局配置文件加入如下配置:
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
settings>
SQL语句中可以不使用别名:
<select id="selectEmployee" resultType="com.atguigu.mybatis.entity.Employee">
select emp_id,emp_name,emp_salary from t_emp where emp_id=#{empId}
select>
使用resultMap标签手动指定结果集字段与POJO属性的映射关系,可以非常灵活地进行结果集的映射
<resultMap id="EmployeeInfoMap" type="com.xyxy.pojo.EmployeeInfo">
<id column="emp_id" property="id"/>
<result column="emp_name" property="name"/>
<result column="emp_salary" property="salary"/>
resultMap>
<select id="selectEmployeeInfoByEmpId" resultMap="EmployeeInfoMap">
select * from t_emp where emp_id=#{empId}
select>
物理建模
CREATE TABLE `t_customer` (
`customer_id` INT NOT NULL AUTO_INCREMENT,
`customer_name` CHAR(100),
PRIMARY KEY (`customer_id`)
);
CREATE TABLE `t_order` (
`order_id` INT NOT NULL AUTO_INCREMENT,
`order_name` CHAR(100),
`customer_id` INT,
PRIMARY KEY (`order_id`)
);
INSERT INTO `t_customer` (`customer_name`) VALUES ('c01');
INSERT INTO `t_customer` (`customer_name`) VALUES ('c02');
INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o1', '1');
INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o2', '1');
INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o3', '1');
INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o4', '2');
INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o5', '2');
t_customer
表和t_order
表示一对多关系,反之t_order
表和t_customer
表可以看成一对一或者多对一关系
根据订单ID查询出订单信息,并且查询出该订单所属的顾客信息,将查询到的结果集封装到Order对象中
public class Order {
private Integer orderId;
private String orderName;
//表示Order和Customer的对一关系
private Customer customer;
}
public class Customer {
private Integer customerId;
private String customerName;
}
public interface OrderMapper{
Order findByOrderId(Integer orderId);
}
<!--全局配置自动映射驼峰命名-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!--实体类取别名-->
<typeAliases>
<package name="com.xyxy.pojo"/>
</typeAliases>
<!-- Mapper注册:指定Mybatis映射文件的具体位置 -->
<!-- mappers标签:配置一个具体的Mapper映射文件 -->
<!-- resource属性:指定Mapper映射文件的实际存储位置,这里需要使用一个以类路径根目录
<mappers>
<!-- 包下的所有Mapper配置文件将被自动加载、注册,比较方便。 -->
<package name="com.xyxy.mapper"/>
</mappers>
<?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="com.xyxy.mapper.OrderMapper">
<resultMap id="order" type="Order" autoMapping="true">
<!-- autoMapping属性设置为true开启自动映射 -->
<association property="customer" javaType="Customer">
<id column="customer_id" property="customerId"></id>
<result column="customer_name" property="customerName"></result>
</association>
</resultMap>
<select id="findByOrderId" resultMap="order">
select * from t_order o left join t_customer c on o.customer_id=c.customer_id where order_id=#{orderId}
</select>
</mapper>
根据客户的ID查询客户信息,并且查询出该客户的所有订单信息,将查询的订单信息结果集封装到Customer对象中
public class Customer {
private Integer customerId;
private String customerName;
//客户和订单的一对多关系
private List<Order> orderList;
}
public class Order {
private Integer orderId;
private String orderName;
}
public interface CustomerMapper {
Customer findCustomerAndOrders(Integer customerId);
}
<?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="com.xyxy.mapper.CustomerMapper">
<resultMap id="customer" type="Customer" >
<id column="customer_id" property="customerId"/>
<result column="customer_name" property="customerName"/>
<collection property="orderList" ofType="Order" autoMapping="true">
<!--
进行一对多映射,使用collection标签
ofType属性指的是orderList的泛型
-->
</collection>
</resultMap>
<select id="findCustomerAndOrders" resultMap="customer">
SELECT * FROM t_customer c,t_order o WHERE o.customer_id=c.customer_id AND c.customer_id=#{customerId}
</select>
</mapper>