1、SSM开发实战教程第第2章章 单表的增删改查单表的增删改查 通过上一章的学习可了解到,MyBatis可以方便的对数据库进行操作,而数据库表可能是一个相对独立的表(这里谓之单表),也可能两个或多个有密切联系的表(这里谓之多表),本章先来学习单表的增删改查操作,下一章将学习多表关联查询 先来认识一下SqlSession的若干方法,这些方法被用来执行定义在mapper映射文件中的SELECT、INSERT、UPDATE和DELETE语句。以下是一些常用的方法。第第2章章 单表的增删改查单表的增删改查 T selectOne(String statement,Object parameter)查询单条
2、记录 List selectList(String statement,Object parameter)查询记录集合 Map selectMap(String statement,Object parameter,String mapKey)查询记录封装成Map集合 int insert(String statement,Object parameter)插入一条记录 int update(String statement,Object parameter)修改一条记录 int delete(String statement,Object parameter)删除一条记录SqlSession
3、的若干方法 以上方法可以重载,第二个参数不是必要的,重载方法如下。T selectOne(String statement)List selectList(String statement)Map selectMap(String statement,String mapKey)int insert(String statement)int update(String statement)int delete(String statement)SqlSession的若干方法 所谓结果映射就是让数据表的字段名称(即列名)与Java实体类的属性名称一一进行关联匹配的一种机制,以便MyBatis查完
4、数据库后能将关系型查询结果正确地封装为Java对象。如果数据库的字段名称跟相应的类的属性名称不一样,不作处理的话,mybatis运行后相应的字段处的查询结果就为空2.1 结果映射结果映射ResultMap SELECT id,studentname,gender,ageFROM STUDENT 使用结果映射前 发现除了年龄,其他全变成了null。这是因为数据库表student的字段与对象Student的属性名称不一致造成的。数据库查出来的数据无法封装到对应的对象属性中去,除了age名称相同,其他三个都不同,所以只有age的数据,没有其他三个的数据。解决办法有两个,其中一个就是在SQL语句中用别
5、名,让查询结果字段与对象属性一致,项目mybatis11用的正是这个办法,还有一个解决办法就是利用结果映射使用结果映射 DEBUG main-=Preparing:SELECT id,studentname,gender,age FROM STUDENT DEBUG main-=Parameters:DEBUG main-=Total:5 学生编号:1 学生姓名:张飞 学生性别:男 学生年龄:18 学生编号:2 学生姓名:李白 学生性别:男 学生年龄:20 学生编号:3 学生姓名:张无忌 学生性别:男 学生年龄:19 学生编号:4 学生姓名:赵敏 学生性别:女 学生年龄:17使用结果映射后 s
6、electList方法:适用于查询结果是多条的情形。selectOne方法:适用于查询结果是单条的情形。(1)将项目mybatis21复制成mybatis22,在接口类IStudentDao中新增一个方法。public Student findStudentById(int id);(2)在映射文件StudentMapper.xml中新增一段select语句。SELECT*FROM STUDENT where id=#id2.2 使用使用selectOne方法查询单条记录。方法查询单条记录。(3)在接口IStudentDao的实现类StudentDaoImpl中新增方法。Overridepub
7、lic Student findStudentById(int id)Student student=new Student();try session=MyBatisUtil.getSession();student=session.selectOne(com.lifeng.dao.IStudentDao.findStudentById,id);catch(Exception e)e.printStackTrace();return student;(4)在测试类TestStudent1中添加一个方法findStudentById(int id)。public static void mai
8、n(String args)findStudentById(1);public static void findStudentById(int id)IStudentDao studentDao=new StudentDaoImpl();Student student=studentDao.findStudentById(id);student.show();结果如下:DEBUG main-=Preparing:SELECT id,studentname,gender,age FROM STUDENT where id=?DEBUG main-=Parameters:1(Integer)DEB
9、UG main-=Total:1学生编号:1 学生姓名:张飞 学生性别男 学生年龄:182.3.1 主键非自增长主键非自增长(1)检查数据库student表,确认主键非自增长,在接口类IStudentDao中新增一个方法。public int insertStudent(Student student);(2)在映射文件StdentMapper.xml中新增一段insert语句。INSERT INTO student(id,studentname,gender,age)VALUES(#sid,#sname,#sex,#age)parameterType=“Student”也可不加,MyBati
10、s会自动判断传进来的参数类型。#sid是占位符,传进来的值是参数类型Student对应的同名属性值,即Student对象的sid属性的值,其他类推。2.3 使用使用insert方法添加记录方法添加记录(3)在接口IStudentDao的实现类StudentDaoImpl中新增insertStudent方法。Overridepublic int insertStudent(Student student)SqlSession session=null;int count=0;try session=MyBatisUtil.getSession();count=session.insert(com
11、.lifeng.dao.IStudentDao.insertStudent,student);mit();提交事务 catch(Exception e)e.printStackTrace();finally session.close();return count;这里用到了insert方法,用于添加记录,返回受影响的行数。(4)测试类中新增方法。public static void main(String args)insertStudent();/添加学生(给定主键值)public static void insertStudent()IStudentDao studentDao=new
12、StudentDaoImpl();Student student=new Student();student.setSid(6);student.setSname(武松);student.setSex(男);student.setAge(23);int count=studentDao.insertStudent(student);if(count0)System.out.println(添加成功!);(1)修改MySQL数据库,设置Student表的主键sid自动增长。(2)在StudentMapper.xml中新增一条insert语句。INSERT INTO student(student
13、name,gender,age)VALUES(#sname,#sex,#age)注意,这里的SQL语句没有用到sid主键。2.3.2 主键自增长主键自增长(3)在接口IStudentDao中添加方法insertStudentAutoIncrement。public int insertStudentAutoIncrement(Student student);(4)接口的实现类StudentDaoImpl中重写方法insertStudentAutoIncrement。Overridepublic int insertStudentAutoIncrement(Student student)Sq
14、lSession session=null;int count=0;try session=MyBatisUtil.getSession();count=session.insert(com.lifeng.dao.IStudentDao.insertStudentAutoIncrement,student);mit();catch(Exception e)e.printStackTrace();finally session.close();return count;(5)第一次测试。public static void main(String args)insertStudentAutoIn
15、crement();/添加学生(主键自增长)public static void insertStudentAutoIncrement()IStudentDao studentDao=new StudentDaoImpl();Student student=new Student();/第一次测试(不能获得主键值)student.setSname(林冲);student.setSex(男);student.setAge(22);int count=studentDao.insertStudentAutoIncrement(student);if(count0)System.out.printl
16、n(添加成功!);System.out.println(新添加的学生的编号是:+student.getSid();结果如下:DEBUG main-=Preparing:INSERT INTO student(studentname,gender,age)VALUES(?,?,?)DEBUG main-=Parameters:林冲(String),男(String),22(Integer)DEBUG main-=Updates:1添加成功!新添加的学生的编号是:null 可见添加是成功的,但添加后学生编号为null,即无法在添加完数据后立即获取到主键的值,有没办法实现添加成功后即可获取到这个自增
17、长的主键值呢,请看下一个步骤。(6)修改映射文件。INSERT INTO student(studentname,gender,age)VALUES(#sname,#sex,#age)(7)第二次测试。public static void main(String args)insertStudentAutoIncrement();/添加学生(主键自增长)public static void insertStudentAutoIncrement()IStudentDao studentDao=new StudentDaoImpl();Student student=new Student();/
18、第二次测试(可以获得主键值)student.setSname(苏东坡);student.setSex(男);student.setAge(21);int count=studentDao.insertStudentAutoIncrement(student);if(count0)System.out.println(添加成功!);System.out.println(新添加的学生的编号是:+student.getSid();第二次测试结果是 DEBUG main-=Preparing:INSERT INTO student(studentname,gender,age)VALUES(?,?,?
19、)DEBUG main-=Parameters:苏东坡(String),男(String),21(Integer)DEBUG main-=Updates:1 添加成功!新添加的学生的编号是:7(1)接口IStudentDao中添加一个方法deleteStudentById(int id)。public int deleteStudentById(int id);(2)在映射文件StudentMapper中添加一条delete语句。delete from student where id=#id 2.4 使用使用delete方法删除记录方法删除记录(3)实现类StudentDaoImpl。Ove
20、rridepublic int deleteStudentById(int sid)SqlSession session=null;int count=0;try session=MyBatisUtil.getSession();count=session.insert(com.lifeng.dao.IStudentDao.deleteStudentById,sid);mit();catch(Exception e)e.printStackTrace();finally session.close();return count;(4)测试类中添加deleteStudentById方法如下。pu
21、blic static void main(String args)deleteStudentById();public static void deleteStudentById()IStudentDao studentDao=new StudentDaoImpl();Scanner input=new Scanner(System.in);System.out.println(请输入你要删除的学生的学号:);int sid=input.nextInt();Student student=studentDao.findStudentById(sid);if(student!=null)int
22、 count=studentDao.deleteStudentById(sid);if(count0)System.out.println(删除成功!);elseSystem.out.println(该学生不存在!);结果如下。请输入你要删除的学生的学号:4DEBUG main-=Preparing:delete from student where id=?DEBUG main-=Parameters:4(Integer)DEBUG main-=Updates:1删除成功!(1)接口IStudentDao中添加一个方法updateStudent(Student student)。public
23、 int updateStudent(Student student);(2)在映射文件StudentMapper中添加一条update语句。UPDATE Student SET studentname=#sname,gender=#sex,age=#age WHERE id=#sid 2.5 使用使用update方法修改记录方法修改记录(3)实现类StudentDaoImpl添加如下方法。public int updateStudent(Student student)SqlSession session=null;int count=0;try session=MyBatisUtil.ge
24、tSession();count=session.update(com.lifeng.dao.IStudentDao.updateStudent,student);mit();catch(Exception e)e.printStackTrace();finally session.close();return count;(4)测试类中添加updateStudent方法。public static void updateStudent()IStudentDao studentDao=new StudentDaoImpl();Scanner input=new Scanner(System.i
25、n);System.out.println(请输入你要修改的学生的学号:);int sid=input.nextInt();Student student=studentDao.findStudentById(sid);if(student!=null)System.out.println(该学生原有信息如下);student.show();System.out.print(请输入学生的新的姓名:);String sname=input.next();System.out.print(请输入学生的新的性别:);String sex=input.next();System.out.print(请
26、输入学生的新的年龄:);int age=input.nextInt();student=new Student(sname,sex,age);studentDao.updateStudent(student);elseSystem.out.println(该学生不存在);结果如下。请输入你要修改的学生的学号:4DEBUG main-=Preparing:SELECT id,studentname,gender,age FROM STUDENT where id=?DEBUG main-=Parameters:4(Integer)DEBUG main-Preparing:UPDATE stude
27、nt SET studentname=?,gender=?,age=?WHERE id=?DEBUG main-=Parameters:赵敏(String),女(String),18(Integer),4(String)DEBUG main-=Updates:1项目案例项目案例:mybatis23项目中实现查询姓张的学生。(项目源码见本书配套源码:第2章/模糊查询动态查询/mybatis23)(1)将项目mybatis22拷贝为项目mybatis23,在接口IStudentDao中添加一个方法findStudentsByName(String name)。public List findStu
28、dentsByName(String name);2.6 模糊查询模糊查询(2)在映射文件StudentMapper中添加一条select语句。SELECT*FROM STUDENT where studentname like%#name%(3)实现类中添加findStudentsByName方法。Overridepublic List findStudentsByName(String name)SqlSession session=null;List list=new ArrayList();try session=MyBatisUtil.getSession();list=sessio
29、n.selectList(com.lifeng.dao.IStudentDao.findStudentsByName,name);catch(Exception e)e.printStackTrace();return list;(4)在测试类中添加方法findStudentByName。public static void main(String args)findStudentsByName();public static void findStudentsByName()Scanner input=new Scanner(System.in);System.out.print(请输入要查
30、询的学生姓氏:);String name=input.next();IStudentDao studentDao=new StudentDaoImpl();List list=studentDao.findStudentsByName(name);for(int i=0;i Preparing:SELECT*FROM STUDENT where studentname like%?%DEBUG main-=Parameters:张(String)DEBUG main-=Total:2学生编号:1 学生姓名:张飞 学生性别男 学生年龄:18学生编号:3 学生姓名:张无忌 学生性别男 学生年龄:1
31、92.7.1 标签标签项目项目案例案例:查询出满足用户提交查询条件的所有学生。(项目源码见本书配套源码:第2章/模糊查询动态查询/mybatis23)(1)接口IStudentDao中添加方法searchStudentsIf(Student student)。public List searchStudentsIf(Student student);2.7 动态查询动态查询(2)映射文件StudentMapper.xml中添加一条select语句,内容如下。SELECT*FROM STUDENT where 1=1 and studentname like%#sname%and gender=
32、#sex 0 and age=#age (3)实现类中添加如下方法。Overridepublic List searchStudentsIf(Student student)SqlSession session=null;List list=new ArrayList();try session=MyBatisUtil.getSession();list=session.selectList(com.lifeng.dao.IStudentDao.searchStudentsIf,student);catch(Exception e)e.printStackTrace();return list
33、;(4)测试类中添加searchStudentIf方法。public static void main(String args)searchStudentsIf();public static void searchStudentsIf()Scanner input=new Scanner(System.in);Student student=new Student();System.out.print(请输入要查询的学生姓名(也可回车跳过):);student.setSname(input.nextLine();System.out.print(请输入要查询的学生性别(也可回车跳过):);s
34、tudent.setSex(input.nextLine();System.out.print(请输入要查询的学生年龄(也可输入0跳过):);student.setAge(input.nextInt();IStudentDao studentDao=new StudentDaoImpl();List list=studentDao.searchStudentsIf(student);for(int i=0;i Preparing:SELECT*FROM STUDENT where 1=1 and studentname like%?%and gender=?DEBUG main-=Parame
35、ters:张(String),男(String)DEBUG main-Preparing:SELECT*FROM STUDENT where 1=1 and gender=?and age=?DEBUG main-=Parameters:女(String),18(Integer)DEBUG main-=Total:1学生编号:4 学生姓名:赵敏 学生性别:女 学生年龄:18可以发现,查询SQL语句动态地改变了。2.7.2 标签标签标签的中存在一个比较麻烦的地方,需要在where后手工添加“1=1”的子句。因为,若where 后的所有条件均为false,where后没有1=1子句的话,则SQL中
36、就会只剩下一个空的where,SQL出错。可以使用标签实现同样的效果,而无须“1=1”这个看起来额外的东西。项目项目案例案例:使用where标签查询出满足用户提交查询条件的所有学生。(项目源码见本书配套源码:第2章/模糊查询动态查询/mybatis23)(1)接口IStudentDao中添加方法searchStudentsWhere(Student student)。public List searchStudentsWhere(Student student);(2)映射文件StudentMapper.xml中添加一条select语句,内容如下。SELECT*FROM STUDENT and
37、 studentname like%#sname%and gender=#sex 0 and age=#age (3)实现类中添加如下方法。/动态查询where标签Overridepublic List searchStudentsWhere(Student student)SqlSession session=null;List list=new ArrayList();try session=MyBatisUtil.getSession();list=session.selectList(com.lifeng.dao.IStudentDao.searchStudentsWhere,stud
38、ent);catch(Exception e)e.printStackTrace();finally session.close();return list;(4)测试类中添加如下方法。public static void searchStudentsWhere()Scanner input=new Scanner(System.in);Student student=new Student();System.out.print(请输入要查询的学生姓名(也可回车跳过):);student.setSname(input.nextLine();System.out.print(请输入要查询的学生性
39、别(也可回车跳过):);student.setSex(input.nextLine();System.out.print(请输入要查询的学生年龄(也可输入0跳过):);student.setAge(input.nextInt();IStudentDao studentDao=new StudentDaoImpl();List list=studentDao.searchStudentsWhere(student);for(int i=0;i Preparing:SELECT*FROM STUDENT WHERE studentname like%?%and gender=?DEBUG main
40、-=Parameters:张(String),男(String)DEBUG main-=Total:2 学生编号:1 学生姓名:张飞 学生性别男 学生年龄:18 学生编号:3 学生姓名:张无忌 学生性别男 学生年龄:19 上面两个案例,多个查询条件都封装到一个Student对象的属性中去了,但实际开发中,可能有些条件无法封装到同一个对象中去,这时可以采用Map类型进行封装和传递参数。项目案例项目案例:除上面查询条件外,年龄改为查询一定年龄范围内的学生。(项目源码见本书配套源码:第2章/模糊查询动态查询/mybatis23)问题分析问题分析:这里有起始年龄,结束年龄,两个年龄无法封装到同一个St
41、udent对象中去。解决办法就是封装到一个Map集合中去。2.7.3 使用使用Map封装查询条件封装查询条件SELECT*FROM STUDENT and studentname like%#sname%and gender=#sex 0 and ageEnd0 and age between#ageStart and#ageEnd 关键代码public static void main(String args)searchStudentsMap();public static void searchStudentsMap()Scanner input=new Scanner(System.i
42、n);Map map=new HashMap();System.out.print(请输入要查询的学生姓名(也可回车跳过):);map.put(sname,input.nextLine();System.out.print(请输入要查询的学生性别(也可回车跳过):);map.put(sex,input.nextLine();System.out.print(请输入要查询的学生起始年龄(也可输入0跳过):);map.put(ageStart,input.nextInt();System.out.print(请输入要查询的学生结束年龄(也可输入0跳过):);map.put(ageEnd,input
43、.nextInt();IStudentDao studentDao=new StudentDaoImpl();List list=studentDao.searchStudentsMap(map);for(int i=0;ilist.size();i+)list.get(i).show();封装为 Map 该标签里面包含两个子标签,可以包含多个与一个。它们联合使用,完成类似Java中的开关语句switchcase功能。项目案例项目案例:若姓名不空,则按照姓名查询;若姓名为空,则按照年龄查询;若没有查询条件,则没有查询结果。(项目源码见本书配套源码:第2章/模糊查询动态查询/mybatis23)
44、2.7.4 标签标签 映射文件StudentMapper.xml中添加一条select语句,内容如下。SELECT*FROM STUDENT and studentname like%#sname%0 and age=#age and 1!=1 关键代码 标签用于实现对数组与集合类型的输入参数的遍历。其collection属性表示要遍历的集合类型,如果是数组,其值就用array。open、close、separator这些属性用于对遍历内容进行SQL拼接。具体用法请看下面的实例。项目案例项目案例:查询出 id 为与1,3,4的学生信息,利用数组作为参数存储1,3,4。(项目源码见本书配套源码:
45、第2章/模糊查询动态查询/mybatis23)2.7.5 标签遍历数组标签遍历数组 映射文件StudentMapper.xml中添加一条select语句,内容如下。SELECT*FROM STUDENT0 where id in#myid 关键代码 项目案例项目案例:查询出 id 为 2与 4 的学生信息。利用基本类型泛型集合作为参数存储2,4。(项目源码见本书配套源码:第2章/模糊查询动态查询/mybatis23)2.7.6 标签遍历泛型为基本标签遍历泛型为基本类型的类型的List 映射文件StudentMapper.xml中添加一条select语句,内容如下。SELECT*FROM STU
46、DENT0 where id in#myid 其中list表示泛型,匹配传进来的参数是泛型类型,上面的意思是用for each循环遍历泛型,拼接出类似in(2,4)这样格式的SQL语句。映射文件StudentMapper.xml中添加一条select语句,内容如下。SELECT*FROM STUDENT0 where id in#stu.sid 2.7.7 标签遍历泛型为自定标签遍历泛型为自定义类型的义类型的List 标签用于定义SQL片断供其它SQL 标签复用。其它标签要使用该SQL片断,需要使用子标签。该标签可以定义SQL语句中的任何部分,所以子标签可以放在动态SQL的任何位置,最终拼接出
47、需要的SQL语句。2.7.8 标签标签 标签在映射文件的使用过程如下面代码所示(项目源码见本书配套源码:第2章/模糊查询动态查询/mybatis23)。SQL片段 SELECT*FROM STUDENT 调用SQL片段 0 where id in#stu.sid 分页查询的原理在于两个参数,一个是页码pageno,一个是页面大小pagesize,只要这两个参数确定就可传递给下面这个伪SQL语句查询出第pageno页的数据。select *from student limit(pageno-1)*pagesize,pagesize 如果直接以pageno和pagesize作为参数传递,则MySQ
48、L数据库无法识别表达式(pageno-1)*pagesize,所以通常把它综合为一个参数startRow,让表达式(pageno-1)*pagesize在传参前先计算好,赋值给参数startRow。这样,最终使用的SQL语句是:2.8 分页查询基础分页查询基础 select *from student limit startRow,pagesize 体现在映射文件中是这样的形式:select *from student limit#startRow,#pagesize 项目案例:项目案例:分页查询出第1页,第2页的学生表数据,页面大小自定。(项目源码见本书配套源码:第2章/模糊查询动态查询/m
49、ybatis23)具体步骤见教材 在前面例子中自定义Dao层接口实现类时发现一个问题:Dao层的实现类其实并没有干什么实质性的工作,它仅仅就是通过 SqlSession 的相关 API 定位到映射文件 mapper 中相应id的SQL语句,真正对数据进行操作的工作其实是由框架通过映射文件mapper中的SQL完成的。那么,MyBatis 框架可以抛开Dao的实现类,直接定位到映射文件mapper中的相应SQL语句,对数据库进行操作吗?答案是肯定的。可以通过Mapper的动态代理方式实现2.9 getMapper面向接口编程面向接口编程项目案例项目案例:无接口的实现类,通过动态代理,实现查找全部
50、学生,以及查找id=”1”的学生。(项目源码见本书配套源码:第2章/现向接口编程/mybatis24)思路分析:思路分析:上面项目各种基本操作都有了,只需按下面步骤简单修改一下就可以了。(1)拷贝项目mybatis23为mybatis24。删除实现类StudentDaoImpl。(2)确保映射文件的namespace与接口类的全路径名称一致。接口全路径名:com.lifeng.dao.IStudentDao映射文件命名空间namespace:(3)新建测试类TestStudent1。Mapper动态代理方式无需程序员实现Dao接口,Dao实现对象是由 JDK 的 Proxy 动态代理自动生成的