在实际项目的开发中,有时候需要对两个表以及多个表中的数据处理,这时处理方法其实也不只一种,我们可以分开好几次来查询,然后再整合数据,也可以用mybatis中的高级功能通过处理配置文件和pojo层的数据实现一次性的多表联查。
1.其实在实现时,sql语句并不是很复杂,难点在于mybatis中对于ResultMap中配置文件的设置和pojo层数据的设置,这需要了解mybatis实例化pojo层对象的原理及过程。
2.当我们通过mybatis将需要的数据筛选出来之后,最重要的还是将数据封装成json数据返回到前端。但是在实际的项目需求中,往往一条返回的数据来自好几个数据库中表的数据整合,但又不全部包括,这时需要再建立一个专门返回到前端的对象bean供Controller层调用。
理解这三种不同的javabean,首先要理解java分层思想中的Controller+Service +Dao三层的功能划分。
这三层的功能分别为:
Controller: 用来处理业务(Service)调度和管理跳转。执行的过程为:引用对应的Service层,结合SpringMVC注释,跳转到指定的页面,当然也能接受页面传递的请求数据,也可以做些计算处理.
Service: 用来管理具体功能。执行的过程为:引用对应的dao层的数据库操作。
Dao: 只完成数据库的增删改查,可以1对多,多对多,一对一(多对一)关联,模糊,动态,子查询。但是无论多么复杂的查询,dao只负责封装增删改查。对于具体如何增删改查,dao层是不管的。
了解了这三层的执行流程后,就可以分别对应理解vo,bo,pojo三者之间的关系:
当业务处理较为复杂时,需要分别创建vo,bo和pojo来处理数据,但是当数据controller层和service层需要的数据差别不大时,有时候可以将vo供controller层和service层公用。而pojo则是一直在持久层被封装的数据,可以根据业务需,截取部分pojo中对于前端有意义的数据封装,也可以集合好几个pojo中有意义的数据封装起来一并返给前端。
不管是微博还是pyq,我们经常会查看一个人的主页,比如现在正在开发一个社交类型的app,有一个需求是查看用户的主页。
以微博为例,一个人的主页信息包括些什么?
可以看到,一个用户的主页包含:用户的基本信息+用户发过的所有动态信息列表。
用户的基本信息需要对应到用户表来反映,用户的每一条动态信息包含:用户头像,用户昵称,发表时间,动态内容,点赞数,评论数(转发数这里不予实现)。而动态内容包含:动态的文字内容,图片内容/视频内容。这里又涉及到两个表:动态信息表和动态内容表。
所以,这一个页面的呈现涉及到了是哪个表中的信息,首先要明确三个表之间的关联信息:一个用户对应多条动态(一对多),用户一条动态信息对应一个内容(一对一)。
方法一:可以分别对用户表,动态信息表,动态内容表进行查询,这需要调用三个dao层的方法,然后自行将数据关联起来。该方法的优点是:不需要进行较麻烦的文件配置,可以直接查询并得到结果;缺点:需要多次调用dao层的方法,当业务逻辑更麻烦时,会比较费力。
方法二: 配置resultMap以及pojo层数据,进行三表联查。
该方法的优点: 只需调用一次dao层,并且得到的数据不需要在进行整合,数据之间的联系更加紧密;缺点: 配置文件的时候容易出错,resultMap中的数据必须和pojo层的数据一一对应,这样才能正确封装数据,不然很容易出错。(我就出错了好多次。。)
方法一不予讨论,这里主要给出方法二的过程:
1.User
说明:因为一个user对应多条动态(message),所以在User类中应加上List的属性。
public class User {
private Integer id;
private String username;
private String headSculpture;
private String signature;
private Integer messageCount;
private Integer fans;
private Integer concern;
private Integer readCount;
private String studentId;
private List messages;
public List getMessages() {
return messages;
}
public void setMessages(List messages) {
this.messages = messages;
}
public User(Integer id, String username, String headSculpture, String signature, Integer messageCount, Integer fans, Integer concern, Integer readCount, String studentId) {
this.id = id;
this.username = username;
this.headSculpture = headSculpture;
this.signature = signature;
this.messageCount = messageCount;
this.fans = fans;
this.concern = concern;
this.readCount = readCount;
this.studentId = studentId;
}
public User() {
}
}
2.Message(动态信息表)
说明:每个动态都一对一对应一个内容,所以应该在Message类中加上Content(内容)属性
public class Message {
private Integer id;
private Integer userId;
private Integer pageviews;
private Integer praisePoints;
private Integer commentCount;
private Date time;
private Boolean isDeleted;
public Message(Integer id, Integer userId, Integer pageviews, Integer praisePoints, Integer commentCount, Date time) {
this.id = id;
this.userId = userId;
this.pageviews = pageviews;
this.praisePoints = praisePoints;
this.commentCount = commentCount;
this.time = time;
}
//导入一对一的对象类型
private Content content;
public Message(Integer id, Integer userId, Integer pageviews, Integer praisePoints, Integer commentCount, Date time, Boolean isDeleted) {
this.id = id;
this.userId = userId;
this.pageviews = pageviews;
this.praisePoints = praisePoints;
this.commentCount = commentCount;
this.time = time;
this.isDeleted = isDeleted;
}
public Message() {
}
}
3.Content(动态内容类)
public class Content {
private Integer id;
private Integer messageId;
private String contentText;
private String contentImages;
private String contentVideos;
public Content(Integer id, Integer messageId, String contentText, String contentImages, String contentVideos) {
this.id = id;
this.messageId = messageId;
this.contentText = contentText;
this.contentImages = contentImages;
this.contentVideos = contentVideos;
}
public Content() {
}
}
一些注意事项:
在编写pojo层的类时,一定要加上无参构造器,因为mybatis在实例化对象时,需要通过先通过无参构造器来实例化对象,不然运行会报:NoSuchMethod异常。
: 一对多标签,用来表示集合
:一对一标签
resultMap中的配置:
UserMapper:
MessageMapper:
ContentMapper:
**注意:**在配置resultMap时,map中的属性必须和pojo层的属性一一对应!不能缺少也不能多,否则实例化对象会报错。
u.id,u.username, u.head_sculpture, u.signature,
u. message_count,u. fans, u.concern, u.read_count,u.student_id
m.id as mid, m.user_id,m.pageviews,m.praise_points, m.comment_count, m.time,m.is_deleted
c.id,c.message_id,c.content_text,c.content_images,c.content_videos
注意:在编写sql语句时,由于涉及到了表之间额关联关系,在这个问题中,因为查看用户的主页时,某个用户可能存在动态,也可能不存在动态,但是用户的基本信息是必须展示的,所以,在关联用户表和动态表时需要进行左外连接。左外连接也就是返回主表的全部信息以及从表中符合要求的数据。
补充:
还有需要注意的是,因为动态信息和动态内容是高度关联的,即每个动态信息必然对应一个动态内容,因为一个人不能发送内容为空的动态。所以,二者是“共存亡的”,某一条动态一旦删除,则动态内容也应删除,所以这里可以设置外键来限制他们数据的更新。
首先需要创建一个vo类来封装主页信息的数据:
public class UserMainPageVo {
private Integer userId;
private String username;
private String headSculpture;
private String signature;
private Integer messageCount;
private Integer fans;
private Integer concern;
// private Integer readCount;
private String studentId;
private List messageVos;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getHeadSculpture() {
return headSculpture;
}
public void setHeadSculpture(String headSculpture) {
this.headSculpture = headSculpture;
}
public String getSignature() {
return signature;
}
public void setSignature(String signature) {
this.signature = signature;
}
public Integer getMessageCount() {
return messageCount;
}
public void setMessageCount(Integer messageCount) {
this.messageCount = messageCount;
}
public Integer getFans() {
return fans;
}
public void setFans(Integer fans) {
this.fans = fans;
}
public Integer getConcern() {
return concern;
}
public void setConcern(Integer concern) {
this.concern = concern;
}
public String getStudentId() {
return studentId;
}
public void setStudentId(String studentId) {
this.studentId = studentId;
}
public List getMessageVos() {
return messageVos;
}
public void setMessageVos(List messageVos) {
this.messageVos = messageVos;
}
public UserMainPageVo(Integer userId, String username, String headSculpture, String signature, Integer messageCount, Integer fans, Integer concern, String studentId, List messageVos) {
this.userId = userId;
this.username = username;
this.headSculpture = headSculpture;
this.signature = signature;
this.messageCount = messageCount;
this.fans = fans;
this.concern = concern;
this.studentId = studentId;
this.messageVos = messageVos;
}
public UserMainPageVo(){
super();
}
}
private UserMainPageVo assembleUserVo(User user){
UserMainPageVo userMainPageVo = new UserMainPageVo();
List list = new ArrayList<>();
MessageVo messageVo = null;
//填充主页下的动态信息
for(Message m: user.getMessages()){
messageVo = new MessageVo();
messageVo.setUserId(user.getId());
messageVo.setUsername(user.getUsername());
messageVo.setHeader(user.getHeadSculpture());
messageVo.setTime(DateTimeUtil.dateToStr(m.getTime(),DateTimeUtil.STANDARD_FORMAT));
messageVo.setCommentCount(m.getCommentCount());
messageVo.setPraiseCount(m.getPraisePoints());
messageVo.setMessageId(m.getId());
if(m.getContent().getContentText()!=null){
messageVo.setContentText(m.getContent().getContentText());
}
if(m.getContent().getContentImages()!=null){
List imagesVo = new ArrayList<>();
String[] images = m.getContent().getContentImages().split(",");
for(int i =1;i
@ResponseBody
@RequestMapping("get_info_and_message.do")
public ServletResponse getUserInfoAndMessages(String studentId){
if(StringUtils.isBlank(studentId)){
return ServletResponse.createByErrorMessage("无效参数");
}
User user = iUserService.getUserInfoAndMessages(studentId).getData();
UserMainPageVo userMainPageVo = assembleUserVo(user);
return ServletResponse.createBySuccess(userMainPageVo);
}
当处理较为复杂的数据时,应该根据情况选择处理的方法,不管是多表联查还是分别查询,争取用最简单的方法将问题得到解决。