前言
最近有点学累了,很久没有学习新东西了,花了一个晚上的时间归纳和整理了Mybatis对表的操作,主要是对单表进行操作,包括基本的单表操作CRUD,以及模糊查询、分页查询及动态SQL等 。后面会陆续把一对多,多对一查询及多表查询整理一下。
1、构建数据库
在学习之前,你首先要先建一个数据库,本文用Navicat建了一个数据库ssmbuild,里面放了一张books表。然后,把核心配置文件、实体类、工具类等等先写好,该部分可以参考博主以往的博客
CREATE DATABASE `ssmbuild`; USE `ssmbuild`; DROP TABLE IF EXISTS `books`; CREATE TABLE `books` ( `bookID` INT(10) NOT NULL COMMENT '书id', `bookName` VARCHAR(100) NOT NULL COMMENT '书名', `bookCounts` INT(11) NOT NULL COMMENT '数量', `detail` VARCHAR(200) NOT NULL COMMENT '描述', PRIMARY KEY `bookID` (`bookID`) ) ENGINE=INNODB DEFAULT CHARSET=utf8 INSERT INTO `books`(`bookID`,`bookName`,`bookCounts`,`detail`)VALUES (1,'Java',1,'从入门到放弃'), (2,'MySQL',10,'从删库到跑路'), (3,'Linux',5,'从进门到进牢'), (4,'Java',15,'Java修仙手册'), (5,'C++',25,'C++致命宝典');
2、 普通操作
(1) 查询书籍
查询书籍,既可以查询全部书籍,也可以根据id去查询,当然后面会用到动态查询(可以根据某个字段进行模糊查询)。由于SQL语句较简单,所以这里用注解的方式实现 ,因为查询所有书籍会返回若干条记录,所以用集合来保存,最后用增强for去打印集合
//查询所有的书籍 @Select("select *from ssmbuild.books") List<Books> queryAllBooks(); //根据BookId查询相应的书籍 @Select("select *from ssmbuild.books where bookID=#{id}") Books queryBooksById(@Param("id") int BookID);
@Test public void test1() { SqlSession sqlSession = MybatisUtils.getSqlSession(); BookMapper mapper = sqlSession.getMapper(BookMapper.class); List<Books> allBooks = mapper.queryAllBooks(); for (Books books : allBooks) { System.out.println(books); } sqlSession.close(); } @Test public void test5(){ SqlSession sqlSession=MybatisUtils.getSqlSession(); BookMapper mapper = sqlSession.getMapper(BookMapper.class); Books books = mapper.queryBooksById(5); System.out.println(books); sqlSession.close(); }
(2) 增加书籍
可以用带参构造方法和map键值对来增加书籍,返回类型void和int均可,注意实体类的成员变量属性要与数据库字段名一致,否者用resultmap将column与properties对应起来也是可以滴
//用带参构造方法增加书籍 int addBooks(Books books); //用map键值对增加书籍 int addBooks2(Map<String, Object> map);
<insert id="addBooks" parameterType="books"> insert into ssmbuild.books (bookID, bookName, bookCounts, detail) VALUES (#{bookID}, #{bookName}, #{bookCounts}, #{detail}) </insert> <insert id="addBooks2" parameterType="map"> insert into ssmbuild.books (bookID, bookName, bookCounts, detail) VALUES (#{bookID}, #{bookName}, #{bookCounts}, #{detail}) </insert>
@Test public void test6(){ SqlSession sqlSession=MybatisUtils.getSqlSession(); BookMapper mapper = sqlSession.getMapper(BookMapper.class); int res = mapper.addBooks(new Books(8, "kali", 1000, "kali学的好,号子蹲的早")); if (res>0){ System.out.println("增加书籍成功!"); } sqlSession.commit(); sqlSession.close(); } @Test public void test7(){ SqlSession sqlSession=MybatisUtils.getSqlSession(); BookMapper mapper = sqlSession.getMapper(BookMapper.class); Map<String,Object> map=new HashMap<>(); map.put("bookID",9); map.put("bookName","一支红杏出墙来"); map.put("bookCounts",300); map.put("detail","小王老师又一巨作"); int res = mapper.addBooks2(map); if (res>0){ System.out.println("插入成功!"); } sqlSession.commit(); sqlSession.close(); }
注:增删改, 均变动了数据库记录,需要提交事务,有两种方式:上面代码commit方式是一种,还有一种一劳永逸的方式,即将工具类里调用openSession的方法改为true。
(3) 删除书籍
这里是根据id去删除书籍,后面可以用动态sql之foreach去批量删除书籍
int deleteBooks(@Param("id") int BookID);
<delete id="deleteBooks" parameterType="books"> delete from ssmbuild.books where bookID = #{id} </delete>
(4)更新书籍
普通的更新,除了主键外,所有的字段都需要进行更新,后面可以用动态更新,哪里想更,更哪里,妈妈再也不要担心我的学习了。
int updateBooks(Books books);
<update id="updateBooks" parameterType="books"> update ssmbuild.books set bookName = #{bookName}, bookCounts=#{bookCounts}, detail=#{detail} where bookID = #{bookID} </update>
@Test public void test10(){ SqlSession sqlSession=MybatisUtils.getSqlSession(); BookMapper mapper = sqlSession.getMapper(BookMapper.class); int res = mapper.updateBooks(new Books(7, "数据库", 1000, "从入库到跑路")); if (res>0){ System.out.println("更新成功!"); } sqlSession.commit(); sqlSession.close(); }
3、模糊查询
(1)模糊查询"%",可匹配任意类型和长度的字符
@Select("select *from ssmbuild.books where detail like '%小王老师%'") List<Books> queryLikeBooks();
查询结果:
(2)模糊查询"_",可匹配单个任意字符,它常用来限制表达式的字符长度
@Select("select *from ssmbuild.books where detail like '从_门到__'") List<Books> queryLikeBooks1();
查询结果:
注:模糊查询有很多种,常用的有这两种,博主偷懒,所以用的注解的方式
4、分页查询
分页查询有limit和RowBounds分页,limit分页用的较多,limit a,b,a表示起始索引,从0开始,表示第一条记录, b表示是指从第a+1条开始,取b条。
@Select("select *from ssmbuild.books limit 5,2") List<Books> queryLimitBooks();
查询结果:上述表示从第六条记录开始,取两条记录,所以查询结果的id应该为6,7
5、动态SQL
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过JDBC或其它类似的框架,你应该就能理解根据不同条件拼接SQL语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。上述的CRUD都比较简单,当面对复杂的业务时,就需要写一些复杂的 SQL 语句,这时候往往需要拼接,而拼接 SQL ,稍微不注意,由于引号,空格等缺失可能都会导致错误。mybatis 利用动态SQL,通过 if, choose,when,otherwise, trim,where, set,foreach等标签,可组合成非常灵活的SQL语句,从而在提高 SQL 语句的准确性的同时,也大大提高了开发人员的效率。
(1)动态SQL之IF
//动态SQL之IF选择查询 List<Books> queryIfBooks(Map<String, Object> map);
<select id="queryIfBooks" parameterType="map" resultType="books"> select *from ssmbuild.books where 1=1 <if test="bookName!=null"> bookName like #{bookName} </if> <if test="detail!=null"> and detail like #{detail} </if> </select>
@Test public void test11() { SqlSession sqlSession = MybatisUtils.getSqlSession(); BookMapper mapper = sqlSession.getMapper(BookMapper.class); HashMap<String,Object> map =new HashMap<String,Object>(); map.put("detail","%小王%"); //map.put("bookName","%Java%"); List<Books> allBooks = mapper.queryIfBooks(map); for (Books books : allBooks) { System.out.println(books); } sqlSession.close(); }
为啥这里要用sql注入(where1=1)的方式 ?这里“where 1”,“where true”也都可以 ,去掉1=1,你就能发现问题所在了。只要跳过bookName字段,对detail字段进行查询时,系统就拼接不起来了,正常语句where后面都要加上判断的条件。所以where1=1的方式能保证对查询条件进行拼接并保证查询能进行下去。当然这里推荐使用where标签,where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句,什么意思呢?就是说在匹配不到合适的字段条件时,where字段不加上去,就成了“select *from ssmbuild.books”了,变成查询所有字段了。若子句的开头为 “AND” 或 “OR”,where 元素会将它们去除。像下面的错误,用上where标签,就会舍弃and,成了“select *from ssmbuild.books where detail like 条件”,能查询出与detail标签相关的内容了。
加上where标签,用IF同时对bookName和detail进行模糊查询时,你会发现什么都查不出来,也许你会说博主这不是在胡说八道吗,但我想在这先卖个关子,这就是上面为啥要先注释掉一条了。
(2) 动态Sql之set
set 元素可以用于动态包含需要更新的列,忽略其它不更新的列。set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。
int updateSetBooks(Map<String,Object> map);
<update id="updateSetBooks" parameterType="map"> update ssmbuild.books <set> <if test="bookName!=null">bookName=#{bookName},</if> <if test="bookCounts!=null">bookCounts=#{bookCounts},</if> <if test="detail!=null">detail=#{detail}</if> </set> where bookID=#{bookID} </update>
@Test public void test12() { SqlSession sqlSession = MybatisUtils.getSqlSession(); BookMapper mapper = sqlSession.getMapper(BookMapper.class); HashMap<String,Object> map =new HashMap<String,Object>(); map.put("bookID",7); map.put("bookName","大数据"); int res = mapper.updateSetBooks(map); if (res>0){ System.out.println("更新成功!"); } sqlSession.commit(); sqlSession.close(); }
这里用set对7号书籍的书籍名称进行修改,原表是数据库,现在应该是改成大数据了。
(3)动态SQL之Choose
动态SQL之Choose,跟if查询类似,但if需要同时满足多个查询条件,而choose满足一个就会查出,现在就解开上面卖的关子。
List<Books> queryChooseBooks(Map<String,Object> map);
<select id="queryChooseBooks" parameterType="map" resultType="books"> select *from ssmbuild.books <where> <choose> <when test="bookName!=null"> bookName like #{bookName} </when> <when test="bookCounts!=null"> AND bookCounts like #{bookCounts} </when> <when test="detail!=null"> AND detail like #{detail} </when> <otherwise>AND featured = 1 </otherwise> </choose> </where> </select>
@Test public void test13() { SqlSession sqlSession = MybatisUtils.getSqlSession(); BookMapper mapper = sqlSession.getMapper(BookMapper.class); HashMap<String,Object> map =new HashMap<String,Object>(); map.put("detail","%小王%"); map.put("bookName","%Java%"); List<Books> allBooks = mapper.queryChooseBooks(map); for (Books books : allBooks) { System.out.println(books); } sqlSession.close(); }
这里的junit测试单元是不是在哪见过?对的,跟if查询不要说一毛一样,简直就是双胞胎,也就方法名不同而已。那结果呢?话不多说,看图。if要条件全部满足才能查出,而choose查到一个就不会继续查了,有点类似多分支Switch语句。
(4) 动态SQL之foreach
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值(官方文档说的很清楚)。我们这用foreach实现批量删除,当然还可以用来实现批量增加记录。
//使用foreach批量删除书籍 void deleteSomeBooks(List<Integer> list);
注:尽量不要像博主一样在配置文件中加中文 ,有时会报错,具体怎么解决,参考以往文章
<delete id="deleteSomeBooks" parameterType="map"> <!--collection: 表示类型,如果参数是数组,就写成array,如果是集合,就写成list item: 表示集合中每一个元素进行迭代时的别名,自己随便取名 index,指定一个名字,用于表示在迭代过程中,每次迭代到的位置 --> delete from ssmbuild.books where bookID in <foreach collection="list" item="id" open="(" separator="," close=")"> #{id} </foreach> </delete>
这里是删除id=9到id=18之间的书籍记录
@Test public void test8(){ SqlSession sqlSession=MybatisUtils.getSqlSession(); BookMapper mapper = sqlSession.getMapper(BookMapper.class); ArrayList<Integer> list = new ArrayList<>(); for (int num=9;num<19;num++){ list.add(num); } mapper.deleteSomeBooks(list); sqlSession.commit(); sqlSession.close(); }
(5)动态SQL之trim
自定义trim元素,trim可以定制set和where功能,即不用手动去写"and","or",","等。其实set里的“,”及where里的“and”不加也不会报错,但为了书写规范还是加吧。所以感觉trim有点鸡肋。
int updateTrimBooks(Map<String,Object> map);
<update id="updateTrimBooks" parameterType="map"> update ssmbuild.books <trim prefix="SET" suffixOverrides=","> <if test="bookName!=null">bookName=#{bookName}</if> <if test="bookCounts!=null">bookCounts=#{bookCounts}</if> <if test="detail!=null">detail=#{detail}</if> </trim> where bookID=#{bookID} </update>
@Test public void test14() { SqlSession sqlSession = MybatisUtils.getSqlSession(); BookMapper mapper = sqlSession.getMapper(BookMapper.class); HashMap<String,Object> map =new HashMap<String,Object>(); map.put("bookID",7); map.put("bookName","数据库"); int res = mapper.updateTrimBooks(map); if (res>0){ System.out.println("更新成功!"); } sqlSession.close(); }
我又把大数据改为数据库了,有点强迫症,结果如下:
所有的项目文件结构如下所示: