学习来源 通过自动回复机器人学Mybatis—基础版
MyBatis
的基础知识,暂时放弃了就业班的课程,追源生活老师的课程去了
关于项目分层 大体上为MVC,但是这里需要对M层,也就是操作数据库的模块在分层,分为 dao
和 db
层,前者不用说了,用来向 service
提供操作数据的方法,而新加进来的后者则是专门负责和数据库交互,使用了 MyBatis 之后,这个模块主要负责向 dao
提供 SqlSession
以及 MyBatis 相关文件配置,包括数据库连接配置文件,Sql配置文件,这样子层次就更加分明了。
对MyBatis的印象 一些基础功能用下来,感觉就像是对sql功能的加强,为其添加了判断,循环等特性,就像是css的增强版scss一样;不同于在java中对sql的拼接,它可以更加智能地处理sql语句,比如没有数据时自动 WHERE
,SET
,自动加逗号等分隔符,同时提供直接向查询结果和java bean映射的功能,甚至包括一些复杂的数据结构。其实吧,这些功能都可以自己封装的,但是毕竟都要花时间,有工具帮我们做了岂不美哉。
同时直接将sql语句写在文件中,和java逻辑代码解耦,有利有弊吧,不方便调试,程序自动组装sql时也会出现各种奇葩的错误,要调试好半天。
这里使用的版本是3.4.6
基础配置 主配置文件 主配置文件 Configuration.xml(文件名字随便写)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration > <environments default ="development" > <environment id ="development" > <transactionManager type ="JDBC" > </transactionManager > <dataSource type ="UNPOOLED" > <property name ="driver" value ="com.mysql.jdbc.Driver" /> <property name ="url" value ="" /> <property name ="username" value ="" /> <property name ="password" value ="" /> </dataSource > </environment > </environments > </configuration >
对于 dataSource
标签内的参数就是在配JDBC时需要填的连接数据库的一些参数,驱动,url,用户名和密码,至于它的属性 type
是然你选择是否使用连接池,这里不使用
读入配置文件 在 db
中获取SqlSession的类中编写载入配置文件的程序(目前不是很清楚 SqlSessionFactory
是否要做成单例,这里没有使用单例模式),为了图方便把测试用例也写在一起了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class ListMessageDb { public static ListMessageDb getInstance () { return new ListMessageDb(); } public SqlSession getSqlSession () throws Exception { Reader reader = Resources.getResourceAsReader("com/example/microapp/db/Configuration.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); SqlSession sqlSession = sqlSessionFactory.openSession(); return sqlSession; } @Test public void testSqlSession () { try { if (ListMessageDb.getInstance().getSqlSession() == null ) { System.out.println("打开session失败" ); } else { System.out.println("打开session成功" ); } } catch (Exception e) { e.printStackTrace(); System.out.println("打开session失败" ); } } }
注意, Resources.getResourceAsReader
中的值就配置文件在classpath下的路径。
添加日志打印 因为是读入下载配置文件中的sql,不方便断点调试,幸好它原生支持log4j日志打印功能,在classpath的根路径,也就是你工程package的根路径下添加log4j配置文件就行了,同时将它的jar包加入到classpath中,在官网下载的压缩文件中就有;需要熟悉log4j的相关配置,这个是我直接复制老师的代码的
1 2 3 4 5 6 7 8 log4j.rootLogger=DEBUG,Console log4j.appender.Console=org.apache.log4j.ConsoleAppender log4j.appender.Console.layout=org.apache.log4j.PatternLayout log4j.appender.Console.layout.ConversionPattern=%d[%t]%-5p[%c]-%m/%n log4j.appender.Console.encoding=UTF-8 log4j.appender.A1.Encoding=UTF-8 log4j.logger.org.apache=INFO
打印的日志级别的DEBUG
,是最详细的,打印到Conole
中,倒数第二三行的功能是防止出现乱码,出现乱码有几种情况,但都是业务流程流程中编码不一致导致的,请同意编码格式
request传入数据的编码,请设置 setCharacterEncoding()
为相关编码
IDE的console的编码
java编译器对编码设置,设置 -Dfile.encoding=
为相关编码
IDE的编码格式
打印程序输出的编码,上面的就是对此的设置
数据库的编码,包括database,table的相关编码
一些基础sql 准备 建立数据表
1 2 3 4 5 6 7 CREATE TABLE `message` ( `id` int (11 ) NOT NULL AUTO_INCREMENT COMMENT '主键' , `command` varchar (16 ) DEFAULT NULL COMMENT '指令名称' , `description` varchar (32 ) DEFAULT NULL COMMENT '描述' , `comment` varchar (2048 ) DEFAULT NULL COMMENT '内容' , PRIMARY KEY (`ID` ) ) ENGINE =InnoDB AUTO_INCREMENT=26 DEFAULT CHARSET =utf8
测试数据的创建这里就不列出来了
建立对于的javabean,其中的注释,toString,getter和setter就都不列出了,太占位置,注意和列名尽量保持一致,或者把注释写好,方便人阅读;这是专门为数据库处理封装的对象,在service层和view层可以使用DTO对象
1 2 3 4 5 6 7 8 9 public class Message { private int id; private String command; private String description; private String content; public Message () { } }
在 db
层中为其创建相应的xml文件,用于配置映射对象以及填写相应的sql语句,文件起名为 Message.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?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 ="Message" > <resultMap type ="com.example.microapp.bean.Message" id ="MessageResult" > <id column ="id" jdbcType ="INTEGER" property ="id" /> <result column ="command" jdbcType ="VARCHAR" property ="command" /> <result column ="description" jdbcType ="VARCHAR" property ="description" /> <result column ="content" jdbcType ="VARCHAR" property ="content" /> </resultMap > </mapper >
resultMap
用于对查询结果的映射,MyBatis将查询结果ResultSet直接映射为 resultMap
中配置的内容,返回一个列表对象,resultMap
的属性 id
用于最为其唯一的表示,和html中的id标签差不都,必须唯一,但是这里,它的外层标签的属性为 nampspace
,属性c++的人应该都不会陌生,这是作用域,所以之前的 id
只要在这个作用域中唯一就可以了,再别的命名空间中调用的话可以这样 Message.MessageResult
,而 type
属性就是需要映射的类的引用包名。
内部标签id
代表数据表中的主键,而result
就是别的列,他们的属性常用属性如下
column
数据表列名,注意,这里指的是查询返回的那个table的列名,默认使用table的列名,但也可以取别名,也就是说,你也可以用别的名字,但是在sql语句中要给相应的列取相同的别名
jdbcType
数据库数据类型,正如注释中写的那样,就是 java.sql.Types.*
中的值
property
映射类的属性名称
编写完成之后需要在之前的主配置文件 Configuration.xml 中声明一下
1 2 3 4 5 6 7 8 9 10 11 .... <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration > ...... <mappers > <mapper resource ="com/example/microapp/db/Message.xml" /> </mappers > </configuration >
填写的也是该配置文件在classpath中的路径,千万别忘记注册了,配置文件一多起来可能就会忘记,如果开发过Angular2的人一定会记得每次添加一个component之后都要在一个配置文件中注册,否则会报错,幸好ng有完善的命令行自动化工具,使用它创建component可以自动帮你注册,但是可就MyBatis没有那么方便了…
select语句 普通的select语句谁不会写,这里写一个模糊匹配,需求是:
根据传入的 javabean
中的 command
和 description
查询相应的结果
如果 command
或 description
为空字串或为null,则不作为查询条件
使用 ‘%’ 进行模糊匹配
在Message.xml中这样书写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?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 ="Message" > ...... <select id ="queryMessageList" parameterType ="com.example.microapp.bean.Message" resultMap ="MessageResult" > SELECT id, command, description, content FROM message <where > <if test ="command != null and command.length() != 0" > AND command LIKE CONCAT( '%', #{command}, '%' ) </if > <if test ="description != null and description.length() != 0" > AND description LIKE CONCAT( '%', #{description}, '%' ) </if > </where > </select > </mapper >
查询语句是在 select
总书写的,使用到的属性如下
id
在java中调用此功能时名字,和之前的resultMap
的id
一样,保证在命名空间中保持唯一
parameterType
在java中调用此功能是传入的参数的类的引用包名,注意,参数只能传入一个,在必要的时候进行封装,传入list或者map时就写它的引用包名就行了,详细请参考官方文档
resultMap
返回结果的映射,之前已经配置过,添写对应的id号就行了
而在标签中就写sql语句就行了。
但是,根据需求需要动态拼接sql语句,出现了是否需要 ‘WHERE’ 和 ‘AND’ 的问题,因为如果传入的参数都是null的话就不要‘WHERE’,那也就不要 ‘AND’,反正逻辑很复杂,但是MyBatis提供的标签就解决了这个问题
使用 where
标签,既可以帮我们自动判断是否要加‘WHERE’,是否要加‘AND’
使用 #{}
可以直接从传入的bean对象中获取其属性,它和 ${}
的区别是前者支持预编译,而后者的功能和JDBC中直接拼接sql一样,类似于es6中的同名记号
使用模糊匹配是用别的方法拼接都会出现奇怪的错误,只有使用sql中的 CONCAT
是正常的,反正我是很不能理解,明明和老师的代码一样却会报错,昨天调试了好久都没弄出来
if
标签就不同解释了,和jstl中一样
在java代码中调用如下
1 2 3 4 5 6 public List<Message> queryMessageList (Message message) throws Exception { SqlSession sqlSession = ListMessageDb.getInstance().getSqlSession(); List<Message> result = (ArrayList)sqlSession.selectList("Message.queryMessageList" , message); sqlSession.close(); return result; }
首先获取 SqlSession
,在调用它的 selectList
,传入的参数第一个就是Message.xml配置文件中写的那个 select
标签所在的命名空间和id号
注意,这样的异常不能抛出,必须处理,否则出现异常能会出现sqlSession没有关闭的情况,就像是JDBC中connection没有关闭一样,问题很严重。因为这是个demo,图方便就这样子写了
编写测试文件
1 2 3 4 5 6 7 8 9 10 11 @Test public void queryMessageList () throws Exception { Message message = new Message(); message.setCommand("" ); message.setDescription("精彩" ); List<Message> resultList= MessageListDaoImpl.getInstance().queryMessageList(message); resultList.forEach((v) -> { System.out.println(v); }); }
打印如下
1 2 3 4 5 2019-01-10 10:40:56,179[main]DEBUG[Message.queryMessageList]-==> Preparing: SELECT id, command, description, content FROM message WHERE description LIKE CONCAT( '%', ?, '%' ) / 2019-01-10 10:40:56,230[main]DEBUG[Message.queryMessageList]-==> Parameters: 精彩(String)/ 2019-01-10 10:40:56,272[main]DEBUG[Message.queryMessageList]-<== Total: 2/ Message{id=1, command='查看', description='精彩内容', content='精彩内容'} Message{id=2, command='段子', description='精彩段子', content='如果你..'}
第一行为prepare好的sql语句,相当于是JDBC中从connection中获取preparestatement,第二行是传入的参数,相当于是JDBC中的preparestatement的set方法,第三行是获取的结果,相当于是JDBC中的executeQuery,获取ResultSet,最后打印结果。
那么什么command和description都不设置值呢?
1 2 3 4 5 6 7 8 9 10 11 @Test public void queryMessageList () throws Exception { Message message = new Message(); message.setCommand("" ); message.setDescription("" ); List<Message> resultList= MessageListDaoImpl.getInstance().queryMessageList(message); resultList.forEach((v) -> { System.out.println(v); }); }
结果如下:
1 2 3 4 5 2019-01-10 10:48:14,935[main]DEBUG[Message.queryMessageList]-==> Preparing: SELECT id, command, description, content FROM message / 2019-01-10 10:48:14,980[main]DEBUG[Message.queryMessageList]-==> Parameters: / 2019-01-10 10:48:15,026[main]DEBUG[Message.queryMessageList]-<== Total: 15/ Message{id=1, command='查看', description='精彩内容', content='精彩内容'} ......
从第一行可以看到,并没有 WHERE
语句了,而最终查到了所有的15条数据,这里就不全部粘贴出来了
delete语句 和之前一样,普通的delete语句写的没意思,需求是根据id号批量删除Message,要使用到sql中的 IN
语句
在Message.xml中这样写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?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 ="Message" > <select id ="deleteMessagesById" parameterType ="java.util.List" > DELETE FROM message WHERE id IN <foreach collection ="list" item ="item" open ="(" separator ="," close =")" > #{item} </foreach > </select > </mapper >
使用 parameterType
不用在xml中写映射,标签 foreach
可以对传入的list或者map进行遍历,其中 item
就是每一个的值,这里我们传入的是存有id号的list,而 IN
语句有开闭括号,每个数值之间还有逗号作为分割,如果用普通的JDBC,会要加多个判断,比如最后一个值后面不能有逗号;这里这个标签一起为我们打包解决了
编写java代码调用
1 2 3 4 5 6 7 8 public int deleteMessageByIds (List<Integer> ids) throws Exception { SqlSession sqlSession = null ; sqlSession = ListMessageDb.getInstance().getSqlSession(); int result = sqlSession.delete("Message.deleteMessagesById" , ids); sqlSession.commit(); sqlSession.close(); return result; }
注意,对于像是 UPDATE
, INSERT
, DELETE
这样的DML语句,Mybatis和JDBC不同,需要我们自己commit,当然,这里的异常也是需要捕获的。
编写测试用例
1 2 3 4 5 6 7 8 9 @Test public void deleteMessageByIds () throws Exception { List<Integer> ids = new ArrayList<>(); ids.add(22 ); ids.add(23 ); ids.add(24 ); int result = MessageListDaoImpl.getInstance().deleteMessageByIds(ids); System.out.println(result); }
输出结果如下
1 2 3 4 2019-01-10 11:02:11,486[main]DEBUG[Message.deleteMessagesById]-==> Preparing: DELETE FROM message WHERE id IN ( ? , ? , ? ) / 2019-01-10 11:02:11,541[main]DEBUG[Message.deleteMessagesById]-==> Parameters: 22(Integer), 23(Integer), 24(Integer)/ 2019-01-10 11:02:11,542[main]DEBUG[Message.deleteMessagesById]-<== Updates: 3/ 3
可以看出,成功删除了三行数据,当要删除的id不存在时会自动忽略,不过感觉最好在业务逻辑里先验证一下
insert 语句 这里有一个需求,要求存入数据库的Message的值的id使用的是数据库auto_increment的值
1 2 3 4 5 6 7 8 9 10 11 12 <?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 ="Message" > <insert id ="insertMessage" useGeneratedKeys ="true" keyProperty ="id" parameterType ="java.util.List" > INSERT INTO message(command, description, content) VALUES <foreach collection ="list" item ="item" separator ="," > (#{item.command}, #{item.description}, #{item.content}) </foreach > </insert > </mapper >
这里 insert
标签多了一些属性
useGeneratedKeys
使用数据库主键自动生成的数据
keyProperty
主键的列名
这样子的话需要插入的就是非主键的列了
对于 foreach
标签里面每个item如果是javabean的话可以想上面的代码一样直接取它的属性
编写java程序调用
1 2 3 4 5 6 7 public int insertMessages (List<Message> lists) throws Exception { SqlSession sqlSession = ListMessageDb.getInstance().getSqlSession(); int rows = sqlSession.insert("Message.insertMessage" , lists); sqlSession.commit(); sqlSession.close(); return rows; }
别忘记 commit 了啊
编写测试用例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @Test public void insertMessages () throws Exception { List<Message> messages = new ArrayList<>(); Message msg1 = new Message(); msg1.setContent("测试用例一" ); msg1.setDescription("测试用例一aaaaa" ); msg1.setCommand("测试用例一aaaaa" ); Message msg2 = new Message(); msg2.setContent("测试用例二" ); msg2.setDescription("测试用例二bbbbbb" ); msg2.setCommand("测试用例二bbbbbb" ); Message msg3 = new Message(); msg3.setContent("测试用例三" ); msg3.setDescription("测试用例三cccccc" ); msg3.setCommand("测试用例三cccccc" ); messages.add(msg1); messages.add(msg2); messages.add(msg3); int result = MessageListDaoImpl.getInstance().insertMessages(messages); System.out.println(result); }
答应结果如下
1 2 3 4 2019-01-10 11:45:51,751[main]DEBUG[Message.insertMessage]-==> Preparing: INSERT INTO message(command, description, content) VALUES (?, ?, ?) , (?, ?, ?) , (?, ?, ?) / 2019-01-10 11:45:51,813[main]DEBUG[Message.insertMessage]-==> Parameters: 测试用例一aaaaa(String), 测试用例一aaaaa(String), 测试用例一(String), 测试用例二bbbbbb(String), 测试用例二bbbbbb(String), 测试用例二(String), 测试用例三cccccc(String), 测试用例三cccccc(String), 测试用例三(String)/ 2019-01-10 11:45:51,817[main]DEBUG[Message.insertMessage]-<== Updates: 3/ 3
查询数据库,结果如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ....... *************************** 13. row *************************** ID: 26 COMMAND: 测试用例一aaaaa DESCRIPTION: 测试用例一aaaaa CONTENT: 测试用例一 *************************** 14. row *************************** ID: 27 COMMAND: 测试用例二bbbbbb DESCRIPTION: 测试用例二bbbbbb CONTENT: 测试用例二 *************************** 15. row *************************** ID: 28 COMMAND: 测试用例三cccccc DESCRIPTION: 测试用例三cccccc CONTENT: 测试用例三
UPDATE大同小异,这里就不写了
一对多查询 建立ResultMap映射时这里需要使用到 collection
准备 两个表,tag与content,其中content中存有其对于的tag编号
1 2 3 4 5 CREATE TABLE `tag` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id号', `name` varchar(255) NOT NULL COMMENT '标签名称', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
1 2 3 4 5 6 CREATE TABLE `content` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id号', `content` varchar(255) NOT NULL COMMENT '内容', `tagid` int(11) NOT NULL COMMENT '对应的标签号', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;
两个javabean
Tag.java
1 2 3 4 5 6 public class Tag { private int id; private String name; private List<Content> contents; }
Content.java
1 2 3 4 5 6 public class Content { private int id; private String content; private int tagId; }
对应的XML配置文件
Content.xml
1 2 3 4 5 6 7 8 9 10 11 12 <?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 ="Content" > <resultMap id ="ContentResult" type ="com.example.microapp.bean.Content" > <id column ="id" jdbcType ="INTEGER" property ="id" /> <result column ="content" jdbcType ="VARCHAR" property ="content" /> <result column ="tagid" jdbcType ="INTEGER" property ="tagId" /> </resultMap > </mapper >
Tag.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 <?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 ="Tag" > <resultMap id ="TagResult" type ="com.example.microapp.bean.Tag" > <id column ="a_id" jdbcType ="INTEGER" property ="id" /> <result column ="name" jdbcType ="VARCHAR" property ="name" /> <collection property ="contents" resultMap ="Content.ContentResult" /> </resultMap > </mapper >
主要要在主配置文件总注册,还要编写测试数据,这里就不再写了
代码编写 需求是希望根据传入的tag的名字列表查询其所有值,包括对于的content,存放到类型为Tag的列表中
这里就要对 resultMap
做特殊处理了,主要到前面的 Tag.xml
中有一行注释,下面用到的 collection
标签就是用来处理一对多关系的,properties
对应其javabean中的值,而 resultMap
对应的值就是在 Content.xml
中配置的content的ResultMap
当然,在配置文件中id对应的列名写成了a_id
,这是因为tag和content表有相同的列名 id
,而映射的值需要唯一,所以这里是用了别名,当然在下面的sql语句中也要体现,a.id取了别名 a_id
;
sql语句当然是用 LEFT JOIN
喽
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <?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 ="Tag" > .... <select id ="queryTagList" parameterType ="java.util.List" resultMap ="TagResult" > SELECT a.id a_id, a.name, b.id, b.content, b.tagid FROM tag a LEFT JOIN content b ON a.id = b.tagid <where > a.id IN <foreach collection ="list" item ="item" open ="(" separator ="," close =")" > #{item} </foreach > </where > </select > </mapper >
编写方法调用
1 2 3 4 5 6 7 public List<Tag> queryTagList (List<Integer> tagIds) throws Exception { SqlSession sqlSession = ListMessageDb.getInstance().getSqlSession(); List<Tag> tags = new ArrayList<>(); tags = sqlSession.selectList("Tag.queryTagList" , tagIds); sqlSession.close(); return tags; }
编写测试用例,查询编号为1与2的tag数据
1 2 3 4 5 6 7 8 9 10 11 @Test public void queryTagList () throws Exception { List<Integer> ids = new ArrayList<>(); ids.add(1 ); ids.add(2 ); List<Tag> result = MessageListDaoImpl.getInstance().queryTagList(ids); System.out.println(result); }
输出结果如下
1 2 3 4 5 6 7 8 9 10 11 12 2019-01-10 12:07:14,395[main]DEBUG[Tag.queryTagList]-==> Preparing: SELECT a.id a_id, a.name, b.id, b.content, b.tagid FROM tag a LEFT JOIN content b ON a.id = b.tagid WHERE a.id IN ( ? , ? ) / 2019-01-10 12:07:14,437[main]DEBUG[Tag.queryTagList]-==> Parameters: 1(Integer), 2(Integer)/ 2019-01-10 12:07:14,466[main]DEBUG[Tag.queryTagList]-<== Total: 8/ [Tag{id=1, name=休闲}Content{id=1, content='1aaa', tagId=1} Content{id=2, content='1bbb', tagId=1} Content{id=3, content='1ccc', tagId=1} Content{id=4, content='1ddd', tagId=1} , Tag{id=2, name=游戏}Content{id=5, content='2aaa', tagId=2} Content{id=6, content='2bbb', tagId=2} Content{id=7, content='2ccc', tagId=2} Content{id=8, content='2ddd', tagId=2} ]
可以看到,Tag中的列表被成功填写了
多对一查询 建立ResultMap映射时这里需要使用到 association
准备 数据库的content和tag表不变,但是javabean需要改一改了,和之前的反过来,这次是Content里存放Tag数据,具体如下,新建两个文件
TagAss.java
1 2 3 4 5 public class TagAss { private int id; private String name; ..... }
ContentAss.java
1 2 3 4 5 6 public class ContentAss { private int id; private String content; private TagAss tag; ..... }
xml配置还是在原来的对应的文件这种写,但是记住 resultMap
的 id
千万不能重复
Tag.xml
1 2 3 4 5 6 7 8 9 10 11 12 <?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 ="Tag" > .... <resultMap id ="TagAssResult" type ="com.example.microapp.bean.TagAss" > <id column ="id" jdbcType ="INTEGER" property ="id" /> <result column ="name" jdbcType ="VARCHAR" property ="name" /> </resultMap > .... </mapper >
Content.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <?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 ="Tag" > ...... <resultMap id ="ContentResultAss" type ="com.example.microapp.bean.ContentAss" > <id column ="content_id" jdbcType ="INTEGER" property ="id" /> <result column ="content" jdbcType ="VARCHAR" property ="content" /> <association property ="tag" resultMap ="Tag.TagAssResult" /> </resultMap > <select id ="queryContentList" parameterType ="java.util.List" resultMap ="ContentResultAss" > SELECT a.id content_id, a.content, b.id, b.name FROM content a LEFT JOIN tag b ON a.tagid = b.id <where > a.id IN <foreach collection ="list" item ="item" open ="(" separator ="," close =")" > #{item} </foreach > </where > </select > </mapper >
association
和 前面的collection
非常类似
代码编写 编写dao方法
1 2 3 4 5 public List<ContentAss> queryContentList (List<Integer> contentId) throws Exception { SqlSession sqlSession = ListMessageDb.getInstance().getSqlSession(); List<ContentAss> result = (ArrayList)sqlSession.selectList("Content.QueryContentList" , contentId); return result; }
编写测试用例:查询序号为1,3,6,10,11的content
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Test public void queryContentList () throws Exception { List<Integer> contentId = new ArrayList<>(); contentId.add(1 ); contentId.add(3 ); contentId.add(6 ); contentId.add(10 ); contentId.add(11 ); List<ContentAss> resultList= MessageListDaoImpl.getInstance().queryContentList(contentId); resultList.forEach((v) -> { System.out.println(v); }); }
输出结果如下
1 2 3 4 5 6 7 8 2019-01-10 12:29:52,075[main]DEBUG[Content.QueryContentList]-==> Preparing: SELECT a.id content_id, a.content, b.id, b.name FROM content a LEFT JOIN tag b ON a.tagid = b.id WHERE a.id IN ( ? , ? , ? , ? , ? ) / 2019-01-10 12:29:52,121[main]DEBUG[Content.QueryContentList]-==> Parameters: 1(Integer), 3(Integer), 6(Integer), 10(Integer), 11(Integer)/ 2019-01-10 12:29:52,148[main]DEBUG[Content.QueryContentList]-<== Total: 5/ ContentAss{id=1, content='1aaa', tag=TagAss{id=1, name='休闲'}} ContentAss{id=3, content='1ccc', tag=TagAss{id=1, name='休闲'}} ContentAss{id=6, content='2bbb', tag=TagAss{id=2, name='游戏'}} ContentAss{id=10, content='3bbb', tag=TagAss{id=3, name='体育'}} ContentAss{id=11, content='3ccc', tag=TagAss{id=3, name='体育'}}
可以看到,ContentAss中的属性TagAss也被成功填写了