栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

2021-10-28 MyBatis学习

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

2021-10-28 MyBatis学习

Mybatis 框架

框架( framework )是一个框子——指其约束性,也是一个架子——指其支撑性。是一个基本概念上的结构,用于去解决或者处理复杂的问题。

框架( framework )是构成一类特定软件可复用设计的一组相互协作的类。框架规定了你的应用的体系结构。它定义了整体结构,类和对象的分割,各部分的主要责任,类和对象怎么协作,以及控制流程。框架预定义了这些设计参数,以便于应用设计者或实现者能集中精力于应用本身的特定细节。

ORM

ORM,Object-Relational Mapping,对象关系映射,它的作用是在关系型数据库和对象之间作一个映射处理。

JDBC的缺点:需要手动的完成面向对象的Java语言、面向关系的数据库之间数据的转换,代码繁琐无技术含量,影响了开发效率。

ORM框架就是在面向对象语言和关系数据库之间搭建一个桥梁。这样我们在具体的操作数据库的时候,只要像平时操作对象一样操作它就可以了,ORM框架会根据映射完成对数据库的操作,就不需要再去和复杂的SQL语句打交道了。

Mybatis简介

Mybatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。Mybatis是一个优秀的基于java的持久层框架。

Mybatis的主要设计目的就是让我们对执行SQL语句时对输入输出的数据管理更加方便,所以方便地写出SQL和获取SQL的执行结果才是Mybatis的核心竞争力。

MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。MyBatis可以使用简单的XML或注解来配置和映射原生信息,将接口和Java的 POJO(Plain Ordinary Java Object,普通的Java对象)映射成数据库中的记录。

简单的说:MyBatis是一个半自动ORM框架,其本质是对JDBC的封装。使用MyBatis重点需要程序员编写SQL命令,不需要写一行JDBC代码。

Mybatis的功能架构分为三层:

1、API接口层:提供给外部使用的接口API

2、数据处理层:根据调用的请求完成一次数据库操作

3、基础支撑层:负责最基础的功能支撑

Mybatis与 Hibernate的比较

Hibernate是一个全自动的ORM框架。因为 Hibernate创建了Java对象和数据库表之间的完整映射,可以完全以面向对象的思想来操作数据库,程序员不需要手写SQL语句,而MyBatis中还需要手写SQL语句,所以是半自动化的,工作量要大于Hibernate。

半自动化的 Mybatis 更受欢迎的原因

MyBatis需要手写SQL语句,其工作量要大于Hibernate。但是也正是由于自定义SQL语句,所以其灵活性、可优化性就超过了Hibernate。

Hibernate封装了SQL语句,由开发者对对象操作,Hibernate来生成SQL语句。虽然也可以通过映射配置来控制生成的SQL语句,但是对于要生成复杂的SQL语句,很难实现,或者实现后导致性能的丢失。

而MyBatis将手写SQL语句的工作丢给开发者,可以更加精确的定义SQL,更加灵活,也便于优化性能。完成同样功能的两条SQL语句的性能可能相差十几倍到几十倍,在高并发、快响应要求下的互联网系统中,对性能的影响更明显。

MyBatis对存储过程可提供很好的支持。MyBatis的开发工作量大不意味着学习成本大。对于新手,学习 Hibernate时间成本比Mybatis大很多,Mybatis很快就上手了。

总之,因为MyBatis具有封装少、映射多样化、支持存储过程、可以进行SQL语句优化等特点,符合互联网高并发、大数据、高性能、高响应的要求,使它取代Hibernate成为了Java互联网中首选的持久框架。而对于对性能要求不高的比如内部管理系统、ERP(Enterprise Resource Planning)等可以使用Hibernate。

Mybatis的基本使用 1、jar包介绍

2、核心API介绍
  • SqlSessionFactoryBuilder
    作用:使用构建者模式创建SqlSessionFactory 接口对象

  • SqlSessionFactory
    可以被认为是一个数据库连接池,作用:创建SqlSession接口对象

  • SqlSession
    SqlSession就相当于一个数据库连接(Connection对象),可以在一个事务里面执行多条SQL,然后通过它的commit、rollback 方法提交或者回滚事务

  • Mapper

    映射器。由一个Java接口和XML文件(或者注解构成),需要给出对应的SQL和映射规则,负责发送SQL去执行并返回结果

3、生命周期
  • SqlSessionFactoryBuilder:
    该类用来创建SqlSessionFactory对象,当SqlSessionFactory对象被创建后,SqlSessionFactoryBuilder就失去了作用,所以它只能存在于创建SqlSessionFactory的方法中,而不要让其长期存在。因此SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域

  • SqlSessionFactory:
    SqlSessionFactory的生命周期存在于整个MyBatis的应用之中,所以一旦创建了SqlSessionFactory,就要长期保存它,直至不再使用MyBatis应用,可以认为SqlSessionFactory 的生命周期就等同于MyBatis的应用周期。由于SqlSessionFactory被认为数据库连接池,所以它占据着数据库的连接资源。如果创建多个SqlSessionFactory,那么就存在多个数据库连接池,这样不利于对数据库资源的控制,也会导致数据库连接资源被消耗光,出现系统宕机等情况,所以尽量避免发生这样的情况。因此SqlSessionFactory是一个单例,让它在应用中被共享

  • SqlSession:
    SqlSession应该存活在一个业务请求中,处理完整个请求后,应该关闭连接,让它归还SqlSessionFactory,否则数据库资源就很快被耗费精光,系统就会瘫痪,所以try…catch…finally.….语句来保证其正确关闭。所以SqlSession 的最佳的作用域是请求或方法作用域

  • Mapper:
    由于SqlSession 的关闭,它的数据库连接资源也会消失,所以它的生命周期应该小于等于SqlSession的生命周期。Mapper代表的是一个请求中的业务处理,所以它应该在一个请求中,一旦处理完了相关的业务,就应该废弃它。

4、配置文件

https://mybatis.org/mybatis-3/zh/configuration.html

  • 全局配置文件
  • 映射配置文件

全局配置文件

全局配置文件的名称是自定义的,在JavaProject 项目中需要放到src目录下。
全局配置文件的作用是完成一些全局性的配置,如:对Mybatis框架的设置、别名设置、环境设置、指定映射配置文件等相关配置。

  • properties标签

  • settings标签

  • typeAliases标签 给POJO类起别名、指定包名等

  • environments标签 配置多环境

    • transactionManager标签 配置事务 JDBC/MANAGED不做事务处理
    • dataSource标签 配置数据源 UNPOOLED直连/POOLED池连/JNDI
  • mapper标签

    • 相对于类路径指定映射配置文件

      
          
      
      
    • 使用 file:///协议指定映射文件

      
          
      
      
    • 指定映射接口

      
          
      
      
    • 通过包名指定映射接口

      
          
      
      

映射配置文件

映射配置文件主要是用来编写Sql语句的,结果集的映射关系的指定,以及缓存的一些配置等等。

Mybatis入门案例 1、搭建项目和配置环境 1、创建表与添加DTD约束文件
CREATE TABLE `users` (
  `userid` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(20) DEFAULT NULL,
  `usersex` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`userid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

dtd文件下载地址

2、创建普通java项目

3、添加14个jar包

4、创建实体
public class Users {
    private int userid;
    private String username;
    private String usersex;

    public int getUserid() {
        return userid;
    }

    public void setUserid(int userid) {
        this.userid = userid;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getUsersex() {
        return usersex;
    }

    public void setUsersex(String usersex) {
        this.usersex = usersex;
    }
}
5、创建db.properties文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/bjsxt?useSSL=false
jdbc.username=root
jdbc.password=1615
6、创建全局配置文件



    
    
    
    
        
            
            
            
            
                
                
                
                
            
        
    
    
    
        
        
    

7、创建映射配置文件




2、查询数据

修改映射配置文件




    
    
        select * from users
    

创建UsersDao接口

public interface UsersDao {
    List selectUsersAll() throws IOException;
}

创建UsersDao接口实现类

public class UsersDaoImpl implements UsersDao {
    
    @Override
    public List selectUsersAll() throws IOException {
        // 创建SqlSessionFactory对象
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 创建SqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 通过SqlSession对象下的API完成对数据库的操作
        List list = sqlSession.selectList("com.bjsxt.mapper.UsersMapper.selectUsersAll");
        // 关闭SqlSession对象
        sqlSession.close();
        return list;
    }
}

创建测试类

package com.bjsxt.test;

import com.bjsxt.dao.impl.UsersDaoImpl;
import com.bjsxt.pojo.Users;

import java.io.IOException;
import java.util.List;

public class Test {
    public static void main(String[] args) throws IOException {
        UsersDaoImpl usersDao = new UsersDaoImpl();
        List list = usersDao.selectUsersAll();
        for(Users user:list){
            System.out.println( user.getUserid()+"t"+user.getUsername()+"t"+user.getUsersex());
        }
    }
}

【注意事项】某些版本mysql要求安全连接,通过useSSL=false忽略

如果jdbc.url=jdbc:mysql://localhost:3306/bjsxt,会报错无法查询到结果;

如果jdbc.url=jdbc:mysql://localhost:3306/bjsxt?useSSL=false,能够查询到结果,仍然有警告信息。

3、根据用户ID查询数据

修改映射配置文件


    select * from users where userid = #{suibian}

方式二:使用package指定某个包下所有类的默认别名

忽略包名,以类名为别名,不区分大小写


    


    select * from users where username = #{name} and usersex = #{sex}

Map selectUsersByNameAndSex(String username,String usersex);
@Override
public Map selectUsersByNameAndSex(String username, String usersex) {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    Map param = new HashMap<>();
    // param中给定的key的值,必须和映射配置文件中select语句中给的占位符的名字一致
    param.put("name",username);
    param.put("sex",usersex);
    Map users = sqlSession.selectMap("com.bjsxt.mapper.UsersMapper.selectUsersByNameAndSex", param, "userid");
    return users;
}
Map findUsersByNameAndSex(String username,String usersex);
@Override
public Map findUsersByNameAndSex(String username, String usersex) {
    Map users = null;
    try{
        UsersDaoImpl usersDao = new UsersDaoImpl();
        users = usersDao.selectUsersByNameAndSex(username, usersex);
    }catch(Exception e){
        e.printStackTrace();
    }finally{
        MybatisUtils.closeSqlSession();
    }
    return users;
}
public class SelectMapTest {
    public static void main(String[] args) {
        UsersServiceImpl usersService = new UsersServiceImpl();
        Map map = usersService.findUsersByNameAndSex("lisi", "male");
        Set set = map.keySet();
        for(Integer key:set){
            Users user = map.get(key);
            System.out.println(key);
            System.out.println(user.getUsername()+"t"+user.getUsersex());
        }
    }
}

DML操作

  • insert
  • update
  • delete
Mapper动态代理 1、基于Mybatis的Dao层设计

在Mybatis 中对于Dao层的设计提供了两种方式:

Dao层不使用Mapper动态代理

所谓不使用动态代理是指在Dao层需要我们自己来创建Dao层的接口与接口实现类。在接口实现类的方法中我们自己通过调用SqlSession对象的方法完成数据库的操作。目前我们的入门案例就是通过这种方式完成了对users表的CRUD操作。

Dao层不使用Mapper动态代理缺点:

  • 在SqlSession对象的常用方法中只能向SQL语句中传递一个参数。如果要多个参数,需要封装到POJO或者Map 中
  • 调用SqlSession对象的方法时会有硬编码现象namespace + id

Dao层使用Mapper动态代理

在MyBatis 中提供了另外一种Dao层的实现方式,即Mapper动态代理(或称为接口绑定)的操作方式。这种方式下程序员只需要写Dao接口,不需要创建Dao的接口实现类,Mybatis会自动生成接口实现类的代理对象。在Dao层我们只要创建接口与映射配置文件即可。这种方式可以大大简化Dao层的代码结构,是在开发中最常见的使用方式。

2、Mapper动态代理规范
  • 接口名和映射配置文件名必须相同
  • 映射配置文件中namespace必须是接口全名
  • 接口中方法名和映射配置文件中表示SQL语句的标签的 id的值必须相同
  • 接口中方法返回值类型和映射配置文件中resultType指定的类型一致(单个指类型,集合指其中元素的类型)
动态代理的使用 1、搭建项目

创建项目

添加jar包

创建实体类

public class Users {
    private int userid;
    private String username;
    private String usersex;

    public int getUserid() {
        return userid;
    }

    public void setUserid(int userid) {
        this.userid = userid;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getUsersex() {
        return usersex;
    }

    public void setUsersex(String usersex) {
        this.usersex = usersex;
    }

    @Override
    public String toString() {
        return "Users{" +
                "userid=" + userid +
                ", username='" + username + ''' +
                ", usersex='" + usersex + ''' +
                '}';
    }
}

创建Mybatis工具类

public class MybatisUtils {
    private static ThreadLocal threadLocal = new ThreadLocal<>();
    private static SqlSessionFactory sqlSessionFactory = null;
    static{
        // 创建SqlSessionFactory对象
        InputStream is = null;
        try{
            is = Resources.getResourceAsStream("mybatis-config.xml");
        }catch(IOException e){
            e.printStackTrace();
        }
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
    }

    // 获取sqlSession
    public static SqlSession getSqlSession(){
        SqlSession sqlSession = threadLocal.get();
        if(sqlSession == null){
            sqlSession = sqlSessionFactory.openSession();
            threadLocal.set(sqlSession);
        }
        return sqlSession;
    }

    // 关闭sqlSession
    public static void closeSqlSession(){
        SqlSession sqlSession = threadLocal.get();
        if(sqlSession != null){
            sqlSession.close();
            threadLocal.set(null);
        }
    }
}
2、配置Mybatis框架

添加db.properties文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/bjsxt?useSSL=false
jdbc.username=root
jdbc.password=1615

添加log4j.properties文件

log4j.rootLogger=debug,console,logfile

### appender.console?????? ###
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=<%d> %5p (%F:%L) [%t] (%c) - %m%n
log4j.appender.console.Target=System.out

### appender.logfile??????? ###
log4j.appender.logfile=org.apache.log4j.RollingFileAppender
log4j.appender.logfile.File=SysLog.log
log4j.appender.logfile.MaxFileSize=500KB
log4j.appender.logfile.MaxBackupIndex=7
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=<%d> %p (%F:%L) [%t] %c - %m%n

添加全局配置文件




    
    
    
    
        
        
    
    
    
        
        
    
    
    
        
            
            
            
            
                
                
                
                
            
        
    
    
    
        
        
    

添加UsersMapper接口

public interface UsersMapper {
}

添加UsersMapper映射配置文件






3、实现查询所有用户

修改映射配置文件




    
    
    select * from users where userid = #{userid}

修改UsersMapper接口添加抽象方法

Users selectUsersById(int userid);

修改业务层接口添加方法

Users findUsersById(int userid);

修改业务层接口实现类添加方法

@Override
public Users findUsersById(int userid) {
    Users user = null;
    try{
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UsersMapper mapper = sqlSession.getMapper(UsersMapper.class);
        user = mapper.selectUsersById(userid);
    }catch(Exception e){
        e.printStackTrace();
    }finally{
        MybatisUtils.closeSqlSession();
    }
    return user;
}

创建测试类

public class SelectUsersByIdTest {
    public static void main(String[] args) {
        UsersServiceImpl usersService = new UsersServiceImpl();
        Users user = usersService.findUsersById(1);
        System.out.println(user);
    }
}
5、动态代理的多参数处理 顺序传参法

在映射文件中,SQL语句中的参数需要使用arg0,arg1…或者param1,param2…表示参数的顺序。此方法可读性低,且要求参数的顺序不能出错,在开发中不建议使用。

修改映射配置文件



    select * from users where username = #{name} and usersex = #{sex}

修改UsersMapper接口添加方法

List selectUsersAnnParam(@Param("name") String username,@Param("sex") String usersex);

创建测试类

public class SelectUsersAnnParamTest {
    public static void main(String[] args) {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UsersMapper mapper = sqlSession.getMapper(UsersMapper.class);
        List list = mapper.selectUsersAnnParam("huangzi","female");
        list.forEach(System.out::println);
    }
}
POJO传参法

在SQL语句中绑定参数时使用POJO的属性名作为参数名即可。此方式推荐使用。

修改映射配置文件


    select * from users where username = #{key_name} and usersex = #{key_sex}

修改UsersMapper接口添加方法

List selectUsersMapParam(Map map);

创建测试类

public class SelectUsersMapParamTest {
    public static void main(String[] args) {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UsersMapper mapper = sqlSession.getMapper(UsersMapper.class);

        Map map = new HashMap<>();
        // 【注意】传入map的key的值,必须和映射配置文件中给的占位符完全一致
        map.put("key_name","huangzi");
        map.put("key_sex","female");

        List list = mapper.selectUsersMapParam(map);
        list.forEach(System.out::println);
        MybatisUtils.closeSqlSession();
    }
}
6、映射配置文件中的特殊字符处理

在Mybatis的映射配置文件中不可以使用一些特殊字符;经测试,新版本已解决该问题。

对于旧版本,有两种方式可解决该问题:

使用符号实体


    select * from users where userid ]]> #{userid}

7、Mybatis分页 1、使用RowBounds分页

RowBounds是 Mybatis提供的一个专门处理分页的对象。在RowBounds对象中有两个成员变量:

offset:偏移量,从О开始计数,从哪一行开始
limit:限制条数

使用RowBounds进行分页,非常方便,不需要在SQL语句中写limit,即可完成分页功能。但是由于它是在SQL查询出所有结果的基础上截取数据的,所以在数据量大的SQL中并不适用,它更适合在返回数据结果较少的查询中使用。

修改映射配置文件


    select * from users limit #{offset},#{limit}

修改UsersMapper接口添加方法

List selectUsersLimit(@Param("offset") int offset,@Param("limit") int limit);

创建测试类

public class SelectUsersLimitTest {
    public static void main(String[] args) {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UsersMapper mapper = sqlSession.getMapper(UsersMapper.class);
        // 从第一行数据开始,返回五行数据
        List list = mapper.selectUsersLimit(0,5);
        list.forEach(System.out::println);
        MybatisUtils.closeSqlSession();
    }
}
8、Mapper动态代理模式下的DML操作 实现添加用户业务

修改映射配置文件


    insert into users values(default,#{username},#{usersex})

修改UsersMapper接口

void insertUsers(Users user);

创建测试类

public class InsertUsersTest {
    public static void main(String[] args) {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UsersMapper mapper = sqlSession.getMapper(UsersMapper.class);
        Users users = new Users();
        users.setUsername("xitu");
        users.setUsersex("robot");
        mapper.insertUsers(users);
        // 【注意】手动提交事务,负责不能添加成功
        sqlSession.commit();
        MybatisUtils.closeSqlSession();
    }
}
9、主键值回填

在数据库中插入数据时,有时我们是需要获取新数据的主键值。在Mybatis中支持主键值回填。

Mybatis 中支持两种方法获取主键:

  • 获取自增主键的值。如:MySQL、sqlServer
  • 获取非自增主键的值。如Oracle
1、获取自增主键值——开启自动获取主键值

局部配置

在映射配置文件的insert标签中,指定属性和对应的值



    insert into users values(default,#{username},#{usersex})

全局配置

在mybatis-config.xml文件的settings标签中,添加一个setting标签


    
    
    

2、获取非自增主键

修改映射配置文件


    
        select seq.nextval from dual
    
    insert into users values(userid,#{username},#{usersex})





    
        select last_insert_id()
    
    insert into users values(default,#{username},#{usersex})

动态SQL

MyBatis 提供了动态SQL功能。在XML映射文件中使用标签拼接SQL语句。
MyBatis中动态SQL是编写在mapper.xml中的,其语法和JSTL类似,但是却是基于强大的OGNL表达式实现的。

1、if 单分支判断语句

修改映射配置文件


    select * from users where 1=1
    
        
            and username = #{username}
        
        
            and usersex = #{usersex}
        
        
            and userid = 1
        
    

修改UsersMapper接口添加方法

List selectUsersByChoose(Users users);

修改UsersService接口添加方法

List findUsersByChoose(Users users);

修改UsersService接口实现类添加方法

@Override
public List findUsersByChoose(Users users) {
    List list = null;
    try{
        SqlSession sqlSession  = MybatisUtils.getSqlSession();
        UsersMapper mapper = sqlSession.getMapper(UsersMapper.class);
        list = mapper.selectUsersByChoose(users);
    }catch(Exception e){
        e.printStackTrace();
    }finally{
        MybatisUtils.closeSqlSession();
    }
    return list;
}

创建测试类

public class FindUsersByChooseTest {
    public static void main(String[] args) {
        UsersServiceImpl usersService = new UsersServiceImpl();
        Users users = new Users();
        users.setUsername("xitu");
        List list = usersService.findUsersByChoose(users);
        list.forEach(System.out::println);
        MybatisUtils.closeSqlSession();
    }
}
3、where标签

使用where标签,就不需要提供where 1=1这样的条件了。如果判断条件不为空则自动添加where关键字,并且会自动去掉第一个条件前面的and或 or。

修改映射配置文件


    select * from users where username like concat('%',#{name},'%')





    select * from users where userid in
    
        #{userid}
    

修改UsersMapper接口添加方法

List selectUsersByIdUseCollection(List list);

创建测试类

    public static void main(String[] args) {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UsersMapper mapper = sqlSession.getMapper(UsersMapper.class);

        List list = new ArrayList<>();
        list.add(20);
        list.add(23);
        list.add(27);
        List list1 = mapper.selectUsersByIdUseCollection(list);
        list1.forEach(System.out::println);
        MybatisUtils.closeSqlSession();
    }
}
7、forEach 迭代数组

类型:

collection="array"

名称:

collection="suibian"

void example(@Param("suibian") int[] arr)

修改映射配置文件


        select userid as id,username as name,usersex as sex from users
    

创建测试类

public class SelectUsersAllTest {
    public static void main(String[] args) {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UsersMapper mapper = sqlSession.getMapper(UsersMapper.class);
        List list = mapper.selectUsersAll();

        
        
        list.forEach(System.out::println);
    }
}
3、一对一的关联查询 3.1 标签

标签是处理单一的关联对象(处理单一属性的关联关系)。

  • property:指定关联对象的属性
  • javaType:关联对象的类型(可以省略)
  • select:执行一个新的查询
  • column:在新的查询中用哪个列的值作为查询条件
3.2 需求

完成用户与角色查询。要求一个用户只能对应一个角色。

3.3 实现

创建roles表

CREATE TABLE `roles` (
    `roleid` int(11) NOT NULL,
    `rolename` varchar(30) DEFAULT NULL,
    `user_id` int(11) DEFAULT NULL,
    PRIMARY KEY (`roleid`),
    UNIQUE KEY `role_fk` (`user_id`) USING BTREE,
    CONSTRAINT `role_fk` FOREIGN KEY (`user_id`) REFERENCES `users` (`userid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

创建Roles实体

public class Roles {
    private int roleid;
    private String rolename;

    public int getRoleid() {
        return roleid;
    }

    public void setRoleid(int roleid) {
        this.roleid = roleid;
    }

    public String getRolename() {
        return rolename;
    }

    public void setRolename(String rolename) {
        this.rolename = rolename;
    }

    @Override
    public String toString() {
        return "Roles{" +
                "roleid=" + roleid +
                ", rolename='" + rolename + ''' +
                '}';
    }
}

修改Users实体

public class Users {
    private int userid;
    private String username;
    private String usersex;
    // 添加Roles类型的属性roles
    private Roles roles;

    public Roles getRoles() {
        return roles;
    }

    public void setRoles(Roles roles) {
        this.roles = roles;
    }

    public int getUserid() {
        return userid;
    }

    public void setUserid(int userid) {
        this.userid = userid;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getUsersex() {
        return usersex;
    }

    public void setUsersex(String usersex) {
        this.usersex = usersex;
    }

    @Override
    public String toString() {
        return "Users{" +
                "userid=" + userid +
                ", username='" + username + ''' +
                ", usersex='" + usersex + ''' +
                '}';
    }
}

修改映射配置文件


    
    
    
    
    
    
        
        
    




    select * from users as u,orders as o where u.userid = o.user_id and userid = #{userid}

创建测试类

public class UsersAndOrdersMapperTest {
    public static void main(String[] args) {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UsersMapper mapper = sqlSession.getMapper(UsersMapper.class);
        Users user = mapper.selectUsersAndOrdersById(1);
        System.out.println(user);
        List orders = user.getOrders();
        for(Orders o:orders){
            System.out.println(o);
        }
    }
}
5、多对多关联查询 5.1 需求

根据ID查询用户以及订单中所包含的所有商品。

5.2 实现

创建items表

CREATE TABLE `items` (
  `itemid` int(11) NOT NULL AUTO_INCREMENT,
  `itemname` varchar(255) DEFAULT NULL,
  `itemprice` double DEFAULT NULL,
  PRIMARY KEY (`itemid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

创建orders_items中间表

CREATE TABLE `orders_items` (
  `order_id` int(11) NOT NULL,
  `item_id` int(11) NOT NULL,
  PRIMARY KEY (`order_id`,`item_id`),
  KEY `orders_items_fk2` (`item_id`),
  CONSTRAINT `orders_items_fk1` FOREIGN KEY (`order_id`) REFERENCES `orders` (`orderid`),
  CONSTRAINT `orders_items_fk2` FOREIGN KEY (`item_id`) REFERENCES `items` (`itemid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

创建Items实体

public class Items {
    private int itemid;
    private String itemname;
    private double itemprice;

    @Override
    public String toString() {
        return "Items{" +
                "itemid=" + itemid +
                ", itemname='" + itemname + ''' +
                ", itemprice=" + itemprice +
                '}';
    }

    public int getItemid() {
        return itemid;
    }

    public void setItemid(int itemid) {
        this.itemid = itemid;
    }

    public String getItemname() {
        return itemname;
    }

    public void setItemname(String itemname) {
        this.itemname = itemname;
    }

    public double getItemprice() {
        return itemprice;
    }

    public void setItemprice(double itemprice) {
        this.itemprice = itemprice;
    }
}

修改Orders实体

public class Orders {
    private int orderid;
    private double orderprice;
    private List items;

    public List getItems() {
        return items;
    }

    public void setItems(List items) {
        this.items = items;
    }

    public int getOrderid() {
        return orderid;
    }

    public void setOrderid(int orderid) {
        this.orderid = orderid;
    }

    public double getOrderprice() {
        return orderprice;
    }

    public void setOrderprice(double orderprice) {
        this.orderprice = orderprice;
    }

    @Override
    public String toString() {
        return "Orders{" +
                "orderid=" + orderid +
                ", orderprice=" + orderprice +
                '}';
    }
}

修改映射配置文件



    
    
    
    
        
        
        
            
            
            
            
        
    


    select * from users where userid = #{userid}



        用户姓名:
用户性别:

业务层接口

package com.bjsxt.service;

import com.bjsxt.pojo.Users;

public interface UsersService {
    void addUsers(Users users);
}

业务层接口实现类

public class UsersServiceImpl implements UsersService {
    @Override
    public void addUsers(Users users) {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UsersMapper mapper = sqlSession.getMapper(UsersMapper.class);
        mapper.insertSelective(users);
        // 事务处理和关闭sqlSession的try-catch已经挪到了OpenSqlSessionInViewFilter中
    }
}

处理请求

@WebServlet("/usersServlet.do")
public class UsersServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String flag = req.getParameter("flag");
        if("addUsers".equals(flag)){
            this.addUsers(req,resp);
        }
    }

    
    private void addUsers(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
        Users users = this.createUsers(req);
        UsersServiceImpl usersService = new UsersServiceImpl();
        usersService.addUsers(users);
        // 为避免表单重复提交,使用重定向方式反馈信息
        resp.sendRedirect("ok.jsp");
    }

    
    private Users createUsers(HttpServletRequest req){
        String username = req.getParameter("username");
        String usersex = req.getParameter("usersex");
        Users users = new Users();
        users.setUsername(username);
        users.setUsersex(usersex);
        return users;
    }
}

反馈页面,提示操作成功

<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    操作成功


    操作成功!


两次异常的原因:

  • MybatisUtils工具类获取配置文件,文件名错误 is = Resources.getResourceAsStream(“mybatis-config.xml”);
  • 处理请求的Servlet中createUsers()方法的返回值没改,return users,而不是return null
4、实现分页查询用户的业务

【效果】

修改index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

  
    $Title$
  
  

  添加用户    
  查询用户
  
  

创建findUsers.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    查询用户



    
用户姓名:
用户性别:

修改业务层接口添加方法

public interface UsersService {
    void addUsers(Users users);
    // 分页查询的结果:整页的数据、当前页数、总页数、总行数、每页行数等信息,所以返回值类型为PageInfo
    PageInfo findUsers(int page,Users users);
}

修改业务层接口实现类添加方法

@Override
public PageInfo findUsers(int page, Users users) {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UsersMapper mapper = sqlSession.getMapper(UsersMapper.class);
    UsersExample usersExample = this.createUsersExample(users);

    PageHelper.startPage(page,3);
    // 需要的信息不只是结果集,所以需要PageInfo类型的对象
    List list = mapper.selectByExample(usersExample);
    PageInfo pageInfo = new PageInfo<>(list);
    return pageInfo;
}


private UsersExample createUsersExample(Users users){
    UsersExample usersExample = new UsersExample();
    UsersExample.Criteria criteria = usersExample.createCriteria();
    if(users.getUsername() != null && users.getUsername().length() > 0){
        criteria.andUsernameEqualTo(users.getUsername());
    }
    if(users.getUsersex() != null && users.getUsersex().length() > 0){
        criteria.andUsersexEqualTo(users.getUsersex());
    }
    return usersExample;
}

修改Servlet实现处理查询用户的请求

private void findUsers(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
    Users users = this.createUsers(req);
    String pageIndex = req.getParameter("pageIndex");
    int page = 1;
    // 如果从findUsers.jsp页面发送的请求,显然pageIndex为空,就显示第一页内容;如果是上下翻页的操作,pageIndex肯定不为空
    if(pageIndex != null && pageIndex.length() > 0){
        page = Integer.parseInt(pageIndex);
    }

    UsersService usersService = new UsersServiceImpl();
    PageInfo pageInfo = usersService.findUsers(page, users);

    // 使用请求转发
    req.setAttribute("pageInfo",pageInfo);
    // 同时,还必须传递users,为了维持查询条件的状态
    req.setAttribute("users",users);
    req.getRequestDispatcher("showUsers.jsp").forward(req,resp);
}

在查询结果显示页面实现分页功能

【异常原因】未导入jstl相关jar包,只是给了uri和c.tld文件的路径

使用jstl标签库的准备工作:

  1. 添加两个jar包
  2. 给定c.tld文件的uri和路径


<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>


    显示查询结果
    



    <%--获取响应回来的分页信息和查询条件的状态,并再次提交--%>
    
用户ID 用户姓名 用户性别
${users.userid} ${users.username} ${users.usersex}
<%--为什么不能直接跳转?因为当前页面还保存着查询条件的状态--%> 上一页 ${st.count} ${st.count} 下一页
5、【分析】实现了哪些功能?如何实现?有哪些关键点? 5.1 展示分页查询的结果
关于为什么`PageInfo findUsers(int page,Users users);`需要传入users?

PageInfo类的父类PageSerializable有一个List类型的属性list,获取pageInfo对象就要传入List

resp对象响应回的结果:pageInfo对象和users对象,pageInfo对象包含结果集和分页信息,users对象包含查询条件。使用EL表达式获得结果集包含的数据,使用jstl核心标签库c:forEach标签遍历展示。

5.2 翻页

在表中新建一行,合并为一列,创建两个超链接,分别是上一页和下一页;href属性中给#,不直接跳转,而是绑定单击事件,函数中传递的参数为页码,上一页=当前页码-1,下一页=当前页码+1;使用c:if标签将翻页按钮包裹,设置条件,如果页码 >1就有上一页,如果页码 <总页数就有下一页;subFrom函数的作用:将要查询的页码赋值给表单要提交信息中的pageIndex属性,document.forms[0].submit();实现了隐藏表单( flag = “findUsers”)的提交和对usersServlet.do的请求访问,该属性的值将通过请求传递给Servlet,实现新的一次分页查询。

5.3 定位当前页码

使用c:forEach标签遍历展示从1-总页数的数字,这些数字表示的页码用a标签包裹,绑定单击事件将当前点击的页码传入subFrom函数中实现跳转。使用c:choose``c:when``c:otherwise标签进行判断,如果当前页码变色

5.4 查询状态的保持

第一次查询请求回的响应信息中包含Users对象,通过EL表达式可以获取其中属性username、usersex的值,点击页码或翻页再次发出请求同时提交属性username、usersex的值,保持了查询条件的状态。

【测试】注释掉form表单中的username、usersex

  • 不指定查询条件,查询、翻页和点击页码均正常
  • 指定姓名和性别查询,可查询,翻页或点击页码返回不指定查询条件返回的结果集
  • 指定性别查询,可查询,翻页或点击页码返回不指定查询条件返回的结果集

说明查询条件的状态未保持,所以再次提交username、usersex是必须的。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/356113.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号