在前面例子中 (Dao接口实现类) 自定义 Dao 接口实现类时发现一个问题: Dao 的实现类其实并没有干什么实质性的工作,它仅仅就是通过 SqlSession 的相关 API 定位到映射文件 mapper 中相应 id 的 SQL 语句,真正对 DB 进行操作的工作其实是由框架通过 mapper 中的 SQL 完成的。
所以, MyBatis 框架就抛开了 Dao 的实现类,直接定位到映射文件 mapper 中的相应 SQL语句,对 DB 进行操作。这种对 Dao 的实现方式称为 Mapper 的动态代理方式。Mapper 动态代理方式无需程序员实现 Dao 接口。接口是由 MyBatis 结合映射文件自动生成的动态代理实现的。
一般情况下,一个 Dao 接口的实现类方法使用的是同一个 SQL 映射文件中的 SQL 映射 id。 所以, MyBatis 框架要求,将映射文件中
简单来说,通过接口名即可定位到映射文件 mapper。
<mapper namespace="com.huang.dao.IStudentDao">
mapper 的 namespace 修改了,则需要将日志输出控制文件中 logger 的输出对象进行修改。
##define a logger
log4j.logger.com.huang.dao.IStudentDao=trace,console
MyBatis 框架要求, 接口中的方法名,与映射文件中相应的 SQL 标签的 id 值相同。 系统会自动根据方法名到相应的映射文件中查找同名的 SQL 映射 id。简单来说,通过方法名就可定位到映射文件 mapper 中相应的 SQL 语句。
public interface IStudentDao {
void insertStudent(Student student);
void deleteStudentById(int id);
void updateStudent(Student student);
List<Student> selectAllStudents();
Student selectStudentById(int id);
List<Student> selectStudentsByName(String name);
}
由于通过调用 Dao 接口的方法,不仅可以从 SQL 映射文件中找到所要执行 SQL 语句,还可通过方法参数及返回值,将 SQL 语句的动态参数传入,将查询结果返回。所以, Dao 的实现工作,完全可以由 MyBatis 系统自动根据映射文件完成。
public class MyTest {
private IStudentDao dao;
private SqlSession session;
@Before
public void setUp() {
session = MyBatisUtils.getSqlSession();
dao = session.getMapper(IStudentDao.class);
}
@After
public void tearDown() {
if (session != null) {
session.close();
}
}
@Test
public void test01() {
Student student = new Student("张三", 23, 93.5);
System.out.println("插入前:student = " + student);
dao.insertStudent(student);
System.out.println("插入后:student = " + student);
//添加SqlSession的提交方法
session.commit();
}
/*@Test
public void test02() {
Student student = new Student("张三", 23, 93.5);
System.out.println("插入前:student = " + student);
dao.insertStudentCacheId(student);
System.out.println("插入后:student = " + student);
}*/
@Test
public void test03() {
dao.deleteStudentById(22);
//添加SqlSession的提交方法
session.commit();
}
@Test
public void test04() {
Student student = new Student("张大三", 23, 93.5);
student.setId(8);
dao.updateStudent(student);
//添加SqlSession的提交方法
session.commit();
}
在实际工作中,表单中所给出的查询条件有时是无法将其封装为一个对象的,也就是说,查询方法只能携带多个参数,而不能携带将这多个参数进行封装的一个对象。对于这个问题,有两种解决方案。
(1)修改 Dao 接口
public interface IStudentDao {
List<Student> selectStudentsByCondition(Map<String, Object> map);
}
(2)修改映射文件
<select id="selectStudentsByCondition" resultType="Student">
select id,name,age,score
from student
where name like '%' #{nameCon} '%'
and age > #{ageCon}
and score > #{scoreCon}
select>
(3)修改测试类
//根据 map 查询
@Test
public void test01() {
Student stu = new Student("王小五", 20, 96.5);
Map<String, Object> map = new HashMap<String, Object>();
map.put("nameCon", "王");
map.put("ageCon", 23);
map.put("stu", stu);
List<Student> students = dao.selectStudentsByCondition(map);
for (Student student : students) {
System.out.println(student);
}
}
对于 mapper 中的 SQL 语句,可以通过参数索引 #{index} 的方式逐个接收每个参数。
(1)修改 Dao 接口
public interface IStudentDao {
List<Student> selectStudentsByCondition(String name, int age);
}
(2)修改映射文件
<select id="selectStudentsByCondition" resultType="Student">
select id,name,age,score
from student
where name like '%' #{0} '%'
and age > #{1}
select>
(3)修改测试类
@Test
public void test01() {
List<Student> students = dao.selectStudentsByCondition("张", 23);
for (Student student : students) {
System.out.println(student);
}
}
问题:
#{} 中可以放什么内容?
1)参数对象的属性;
2)随意内容,此时的 #{} 是个占位符;
3)参数为 map 时的 key;
4)参数为 map 时,若 key 所对应的 value 为对象,则可将该对象的属性放入;
5)参数的索引号。