目录
一、MyBatis单表开发 - Mapper代理接口方式 (@Before、@After)
二、给SQL语句传参的两种方式 #{} ${} 、模糊查询(Like)
1.单个简单参数(Integer、Double、String)
4.多个简单参数(Integer、Double、String) @Param()注解
一、MyBatis单表开发 - Mapper代理接口方式 (@Before、@After)
MyBatis中提供了名为 Mapper代理(或称为接口绑定)的操作方式。在实际开发中也使用该方式。
Mybatis中的Mapper接口相当于以前的Dao。但是区别在于,Mapper仅仅是接口,我们不需要提供实现类。
好处:可以提供多个参数,自定义返回值类型,利于后期扩展维护,符合JDBC开发习惯。
1、定义接口 相当于使用JDBC时的DAO接口,但是不需要写实现类
public interface EmployeeMapper {
List<Employee> findAll();
Employee findById(Integer empId);
int insertEmp(Employee emp);
int updateEmp(Employee emp);
int deleteEmp(Integer empId);
//可以指定查询参数
List<Employee> findEmp(String ename,Double minSalary);
}
2、映射文件 (注意命名空间,为接口的全类名)
<?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接口的完整路径-->
<mapper namespace="com.atguigu.mapper.EmployeeMapper">
<!--id必须是接口中方法的名字-->
<select id="findAll" resultType="com.atguigu.pojo.Employee">
SELECT emp_id empId,emp_name empName,emp_salary empSalary
FROM t_emp
</select>
<select id="findById" resultType="com.atguigu.pojo.Employee">
SELECT emp_id empId,emp_name empName,emp_salary empSalary
FROM t_emp
WHERE emp_id = #{empId}
</select>
<insert id="insertEmp">
INSERT INTO t_emp(emp_name,emp_salary)
VALUES (#{empName},#{empSalary})
</insert>
<update id="updateEmp">
DELETE FROM t_emp WHERE emp_id = #{empId}
</update>
<delete id="deleteEmp">
UPDATE t_emp SET emp_name= #{empName},emp_salary = #{empSalary}
WHERE emp_id = #{empId}
</delete>
</mapper>
3、测试
public class TestMapperCRUD {
SqlSession sqlSession;
//所有方法执行前会先执行Before
@Before
public void before() throws IOException {
//读取配置文件,创建SqlSessionFactory,获取sqlSession
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
sqlSession = factory.openSession();
}
//所有方法执行后会执行After
@After
public void after(){
//提交事务
sqlSession.commit();
//关闭资源
sqlSession.close();
}
@Test
public void findAll(){
//获取一个Mapper对象
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
List<Employee> empList = mapper.findAll();
empList.forEach(emp -> System.out.println(emp));
}
@Test
public void findById(){
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee result = mapper.findById(1);
System.out.println(result);
}
@Test
public void insertEmp(){
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
int result = mapper.insertEmp(new Employee(null, "古力娜扎", 888.0));
System.out.println(result);
}
@Test
public void updateEmp(){
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
int result = mapper.updateEmp(new Employee(1, "小白", 130.0));
System.out.println(result);
}
@Test
public void deleteEmp(){
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
int result = mapper.deleteEmp(11);
System.out.println(result);
}
}
底层使用了动态代理来创建接口的实现类:
二、给SQL语句传参的两种方式 #{} ${} 、模糊查询(Like)
结论:只要能用#{}肯定不用${},避免SQL注入。
1、#{} 底层使用PreparedStatement。安全,速度快,避免了注入、字符串拼接。
<select id="findById" resultType="com.atguigu.pojo.Employee">
SELECT emp_id empId,emp_name empName,emp_salary empSalary
FROM t_emp
WHERE emp_id = #{empId}
</select>
@Test
public void findById(){
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee result = mapper.findById(2);
System.out.println(result);
}
2、${} 底层使用Statement
在SQL语句中,数据库表的表名不确定,需要外部动态传入,此时不能使用#{},因为数据库不允许表名位置使用问号占位符,此时只能使用${}。
<select id="findById2" resultType="com.atguigu.pojo.Employee">
SELECT emp_id empId,emp_name empName,emp_salary empSalary
FROM t_emp
WHERE emp_id = ${empId}
</select>
@Test
public void findById2(){
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee result = mapper.findById2(2);
System.out.println(result);
}
使用 #{} ${} 实现模糊查询
#{} 推荐
<select id="findEmp2" resultType="com.atguigu.pojo.Employee">
SELECT emp_id empId,emp_name empName,emp_salary empSalary
FROM t_emp
WHERE emp_name LIKE "%"#{ename}"%"
</select>
${} 不推荐
<select id="findEmp" resultType="com.atguigu.pojo.Employee">
SELECT emp_id empId,emp_name empName,emp_salary empSalary
FROM t_emp
WHERE emp_name LIKE '%${ename}%'
</select>
三、给实体类起别名,方便resultType使用
方法1:给每一个类起别名,别名任意 (不推荐)
mybatis-config.xml
<typeAliases>
<typeAlias type="com.atguigu.entity.Employee" alias="employee"></typeAlias>
<typeAlias type="com.atguigu.entity.Dept" alias="dept"></typeAlias>
</typeAliases>
方法2:给一个包下的所有类起别名,别名为类名的首字母小写,其他字母不变 (推荐使用)
mybatis-config.xml
<typeAliases>
<package name="com.atguigu.pojo"/>
</typeAliases>
别名不区分大小写;别名任意,一般取类名;
注意:<typeAliases>标签声明位置,要在<properties>之后
标签顺序 逗号","表示有顺序"|"表示子元素没有顺序要求
mybatis-3-config.tdt 源码 有顺序:
<!ELEMENT configuration (properties?, settings?, typeAliases?,
typeHandlers?, objectFactory?, objectWrapperFactory?,
reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?)>
映射文件mybatis-3-mapper.dtd 源码 无顺序
<!ELEMENT mapper (cache-ref | cache | resultMap* | parameterMap* |
sql* | insert* | update* | delete* | select* )+>
四、数据输入
1.单个简单参数(Integer、Double、String)
#{} 中名称任意,建议和形参相同
//一个简单类型的参数 基本数据类型、包装类、String类
public Employee findById(Integer empId);
public List<Employee> findEmp(String empName);
//简单参数
@Test
public void testFindById(){
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee emp = mapper.findById(1);
System.out.println(emp);
}
@Test
public void testFindEmp(){
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
List<Employee> emps = mapper.findEmp("李四");
emps.forEach(emp-> System.out.println(emp));
}
2.单个引用参数(Employee)
底层调用的是getter方法,如果没有getter方法,可以直接找同名属性
//一个引用类型的参数
public int insertEmp(Employee emp);
<!--单个引用类型-->
<insert id="insertEmp">
INSERT INTO t_emp VALUES (null,#{empName},#{empSalary})
</insert>
3.单个引用参数(Map)参数较多时使用
参数是map,#{},中需要与 map的 key对应。
public List<Employee> findEmp2(Map map);
<select id="findEmp2" resultType="employee">
SELECT emp_id empId,emp_name empName,emp_salary empSalary
FROM t_emp
WHERE emp_name LIKE "%"#{ename}"%" AND emp_salary >= #{minSalary}
</select>
@Test
public void findEmp2(){
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Map map = new HashMap<>();
map.put("ename","李");
map.put("minSalary",1000);
List<Employee> empList = mapper.findEmp2(map);
empList.forEach(employee -> System.out.println(employee));
}
4.多个简单参数(Integer、Double、String) @Param()注解
推荐使用注解 @param
//多个简单类型的参数
List<Employee> findEmp3(String ename,Double minSalary);
List<Employee> findEmp4(@Param("ename") String ename ,@Param("minSal")Double minSalary);
<!--多个简单参数-->
<select id="findEmp3" resultType="employee">
SELECT emp_id empId,emp_name empName,emp_salary empSalary
FROM t_emp
WHERE emp_name LIKE "%"#{param1}"%" AND emp_salary >= #{param2}
</select>
<select id="findEmp4" resultType="employee">
SELECT emp_id empId,emp_name empName,emp_salary empSalary
FROM t_emp
WHERE emp_name LIKE "%"#{ename}"%" AND emp_salary >= #{minSal}
</select>
@Test
public void findEmp3(){
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
List<Employee> empList = mapper.findEmp3("李", 1000.0);
empList.forEach(employee -> System.out.println(employee));
}
@Test
public void findEmp4(){
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
List<Employee> empList = mapper.findEmp3("李", 1000.0);
empList.forEach(employee -> System.out.println(employee));
}
5.多个引用参数(Employee,Dept)
使用@Param 注解
//多个引用类型的参数
int insertEmp2(Employee emp, Dept dept);
int insertEmp3(@Param("emp")Employee emp, @Param("dept")Dept dept);
<insert id="insertEmp2">
INSERT INTO t_emp VALUES(null,#{param1.empName},#{param1.empSalary},${param2.deptno})
</insert>
<insert id="insertEmp3">
INSERT INTO t_emp VALUES(null,#{emp.empName},#{emp.empSalary},${dept.deptno})
</insert>
@Test
public void testInsertEmp2(){
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee emp = new Employee(null, "润发", 301.0);
Dept dept = new Dept(10, "教学部");
mapper.insertEmp2(emp,dept);
}
@Test
public void testInsertEmp3(){
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee emp = new Employee(null, "琉华", 301.0);
Dept dept = new Dept(10, "心理部");
mapper.insertEmp3(emp,dept);
}
五、数据输出 (resultType)
1.基本数据类型
统计条目数
int selectEmpCount();
<select id="selectEmpCount" resultType="int">
SELECT COUNT(*)
FROM t_emp
</select>
@Test
public void testSelectEmpCount(){
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
int n = mapper.selectEmpCount();
System.out.println("共有记录: " + n + "条");
}
2.实体类型(pojo)
可以写类的完整路径,也可以写别名
Employee findById(Integer empId);
<select id="findById" resultType="employee"> 此处为别名 包
SELECT emp_id empId,emp_name empName,emp_salary empSalary
FROM t_emp
WHERE emp_id = #{ename}
</select>
@Test
public void testFindById(){
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee emp = mapper.findById(1);
System.out.println(emp);
}
3.List类型
List<Employee> findAll();
<select id="findAll" resultType="employee">
SELECT emp_id empId,emp_name empName,emp_salary empSalary
FROM t_emp
</select>
@Test
public void testFindAll(){
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
List<Employee> empList = mapper.findAll();
empList.forEach(employee -> System.out.println(employee));
}
4.Map类型、List<Map>类型
当没有对应的实体类,如多表查询时使用。MAP相当于一个 key value对象,Sql 语句中别名就是key。
单条记录时:Map<key,value>
Map<String,Object> selectEmpNameAndMaxSalary();
<!-- 返回工资最高的员工的姓名和他的工资 -->
<select id="selectEmpNameAndMaxSalary" resultType="map">
SELECT
emp_name 员工姓名,
emp_salary 员工工资,
(SELECT AVG(emp_salary) FROM t_emp) 部门平均工资
FROM t_emp WHERE emp_salary=(
SELECT MAX(emp_salary) FROM t_emp
)
</select>
@Test
public void testQueryEmpNameAndSalary() {
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Map<String, Object> resultMap = mapper.selectEmpNameAndMaxSalary();
resultMap.forEach((key,value)-> System.out.println(key +"-->"+value));
}
多条记录时:List<Map<key,value>>
List<Map<String,Object>> findAll2();
<select id="findAll2" resultType="map">
SELECT emp_id empId,emp_name empName,emp_salary empSalary
FROM t_emp
</select>
public void testFindAll2(){
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
List<Map<String, Object>> list = mapper.findAll2();
for (Map<String, Object> map : list) {
System.out.println(map);
}
}
5.返回自增主键
添加参数:useGeneratedKeys="true" keyProperty="empId"
返回自增主键,封装到实体类属性 empId 中。
//添加员工,返回自增的员工编号(主键)
public int insertEmpGeneratedKey(Employee emp);
<!--添加员工,获取自增的员工编号(主键),赋值给empId-->
<insert id="insertEmpGeneratedKey" useGeneratedKeys="true" keyProperty="empId">
INSERT INTO t_emp VALUES (null,#{empName},#{empSalary})
</insert>
@Test
public void insertEmpGeneratedKey() {
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee emp = new Employee(null, "萌白", 1200.0);
int n = mapper.insertEmpGeneratedKey(emp);
System.out.println("添加了几条记录:" + n);
System.out.println("当前自增主键:" + emp.getEmpId());
}
注:Orecle 需要别的方式获取主键 (调用序列的值,Orecle不支持自增)。
六、数据库表字段和实体类属性对应关系
1.自动映射、手动映射 (起别名 alias)
如果数据库字段和实体类的属性一致,会自动映射到同名的成员变量。
否则需要起别名,没有映射成功属性值会为null。
//表字段名与实体类属性名不对应,需要起别名
<select id="findAll" resultType="employee">
SELECT emp_id empId,emp_name empName,emp_salary empSalary
FROM t_emp
</select>
2.驼峰命名映射(自动识别驼峰式命名规则)
添加全局配置 参数标签,mapUnderscoreToCamelCase 设置为 true 开启驼峰命名映射。
<!--驼峰命名映射-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!--驼峰命名映射-->
<select id="findAllCamel" resultType="employee">
SELECT * FROM t_emp
</select>
3.手动映射ResultMap
多表查询时一般都会使用
单表查询时,不满足驼峰命名,使用ResultMap,只映射一次,也有好处
<!--手动映射 ResultMap-->
<select id="findAllEmp3" resultMap="empMap">
SELECT * FROM t_emp
</select>
<resultMap id="empMap" type="employee"><!--实体类的别名-->
<!--字段不区分大小写,成员变量区分大小写-->
<id column="emp_id" property="empId"></id><!--主键列-->
<result column="emp_name" property="empName"></result><!--普通列-->
<result column="emp_salary" property="empSalary"></result>
</resultMap>
字段不区分大小写,成员变量区分大小写.