Mybatis

(一) 框架概述

1. 什么是框架

框架(Framework)是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法;另一种定义认为,框架是可被应用开发者定制的应用骨架。

前者是从应用方面而后者是从目的方面给出的定义。 简而言之,框架其实就是某种应用的半成品,就是一组组件,供你选用完成你自己的系统。简单说就是使用别人搭好的舞台,你来做表演。

而且,框架一般是成熟的,不断升级的软件。

2. 框架要解决的问题

框架要解决的最重要的一个问题是技术整合的问题.

在J2EE的 框架中,有着各种各样的技术,不同的软件企业需要从J2EE中选择不同的技术,这就使得软件企业最终的应用依赖于这些技术,技术自身的复杂性和技术的风险性将会直接对应用造成冲击。

而应用是软件企业的核心,是竞争力的关键所在,因此应该将应用自身的设计和具体的实现技术解耦。

这样,软件企业的研发将集中在应用的设计上,而不是具体的技术实现,技术实现是应用的底层支撑,它不应该直接对应用产生影响。

框架一般处在低层应用平台(如J2EE)和高层业务逻辑之间的中间层。

3. 软件开发分层的重要性

框架的重要性在于它实现了部分功能,并且能够很好的将低层应用平台和高层业务逻辑进行了缓和。

为了实现软件工程中的“高内聚、低耦合”。把问题划分开来各个解决,易于控制,易于延展,易于分配资源。

我们常见的MVC软件设计思想就是很好的分层思想。

通过分层更好的实现了各个部分的职责,在每一层将再细化出不同的框架,分别解决各层关注的问题。

分层开发下的常见框架
  • WEB层:SpringMVC框架

  • 数据访问层:Mybatis框架

  • 解决技术整合问题的框架:Spring框架

Mybatis框架概述
mybatis是一个优秀的基于java的持久层框架,它内部封装了jdbc,使开发者只需要关注sql语句本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。

mybatis通过xml或注解的方式将要执行的各种statement配置起来
并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句
最后由mybatis框架执行sql并将结果映射为java对象并返回。

采用ORM思想解决了实体和数据库映射的问题,对jdbc进行了封装,屏蔽了jdbc api底层访问细节,使我们不用与jdbc api打交道,就可以完成对数据库的持久化操作。

4. JDBC编程存在的问题

1. 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。
2. sql语句在代码中硬编码,造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。
3. 使用preparedStatement向占有位符号传参数存在硬编码,因为sql语句的where条件不一定,可能多也可能少,修改sql还要修改代码,系统不易维护。
4. 对结果集解析存在硬编码(查询列名),sql变化导致解析代码变化,系统不易维护,如果能将数据库记录封装成pojo对象解析比较方便。

(二) 快速入门

mybatis开发文档 : http://www.mybatis.org/mybatis-3/zh/index.html

1. 开发环境准备

1.1 创建maven项目

1.2 创建数据库及表
见资料文件夹中 d1.sql文件

2. 搭建开发环境

2.1 导入mybatis及依赖坐标
<dependencies>
        <!--mybatis依赖-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>
        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.41</version>
        </dependency>
        <!--junit单元测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <!--log4j日志:程序运行的时候会在控制台展示很详细的执行信息-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
    </dependencies>
2.1 创建实体类

编写实体类, 实体类的属性和数据库的字段保持一致

/**
 * CREATE TABLE `user` (
 *   `id` int(11) NOT NULL auto_increment,
 *   `username` varchar(32) NOT NULL COMMENT '用户名称',
 *   `birthday` datetime default NULL COMMENT '生日',
 *   `sex` char(1) default NULL COMMENT '性别',
 *   `address` varchar(256) default NULL COMMENT '地址',
 *   PRIMARY KEY  (`id`)
 * ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 */
public class User {
    private int id ;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public Date getBirthday() {
        return birthday;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}
2.3 编写数据访问层接口

创建数据访问层接口类 UserDao

/**
 * 用户的持久层操作
 */
public interface UserDao {

    public List<User> findAll();

}
2.4 编写核心配置文件

在项目的resources目录下创建一个配置文件 SqlMapConfig.xml

<?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>
    <!--配置mybatis环境-->
    <environments default="mysql">
        <!--配置mysql的环境-->
        <environment id="mysql">
            <!--配置事物管理类型-->
            <transactionManager type="JDBC"></transactionManager>
            <!--配置数据库连接池-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/day16"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

    <!--引入映射文件-->
    <mappers>
        <mapper resource="com/yll/dao/UserDao.xml"></mapper>
    </mappers>

</configuration>
2.4 编写映射文件

在项目的 resources目录下, 创建和dao层一模一样的包结构, 创建映射配置文件 UserDao.xml

<?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="com.yll.dao.UserDao">
    <select id="findAll" resultType="com.yll.domain.User">
        select * from user
    </select>
</mapper>
注意:
    创建位置:必须和持久层接口在相同的包中。
    名称:必须以持久层接口名称命名文件名,扩展名是.xml

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fZ3q6zNm-1649528615225)(C:\Users\Administrator\Desktop\二期\框架阶段\day16_Mybatis\笔记\01-Mybatis框架一.assets\image-20210701164801606.png)]

2.6 编写测试类

编写一个测试类, 编写Mybatis测试代码 , 测试代码的基本步骤如下:

1. 读取配置文件,通过mybatis提供的Resources对象读取
2. 获取sqlSession工厂对象  sqlSessionFactory
3. 获取sqlSession对象
4. 使用SqlSession创建dao接口的代理对象
5. 使用代理对象执行查询所有方法并打印结果
6. 释放资源
@Test
public void test1() throws IOException {
    //1. 读取配置文件,通过mybatis提供的Resources对象读取
    //InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
    InputStream is = MybatisTest.class.getClassLoader().getResourceAsStream("SqlMapConfig.xml");
    //2. 获取sqlSession工厂对象  sqlSessionFactory
    //2.1 获取SqlSessionFactory的构建者对象
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    //2.2. 获取sqlSession工厂对象
    SqlSessionFactory sf = builder.build(is);
    //3. 获取sqlSession对象
    SqlSession sqlSession = sf.openSession();
    //4. 使用SqlSession创建dao接口的代理对象
    UserDao userDao = sqlSession.getMapper(UserDao.class);
    //5. 使用代理对象执行查询所有方法
    List<User> users = userDao.findAll();
    System.out.println(users);
    //6. 释放资源
    sqlSession.close();
    is.close();
} 
2.7 注意事项
  1. mapper映射文件要和对应的dao接口类同名,且在同一路径下

  2. mapper映射文件的namespace要标识到具体的dao接口类

  3. mapper映射文件中的sql节点id要与dao接口类中的方法名相同

  4. mapper映射文件中的sql节点要有返回值类型才能得到操作后的数据

  5. 在使用IDEA工具开发时(eclipse无此问题)如果在resources中创建映射文件目录时只能一层一层的创建,不会自动生成。

3. Mybatis注解开发

在接口中添加注解
/**
 * 用户的持久层操作
 */
public interface UserDao {

    @Select("select * from user")
    public List<User> findAll();

}
修改核心配置文件

修改核心配置文件 SqlMapConfig.xml配置文件,配置扫描注解

<!--引入映射文件-->
<mappers>
    <!--
	<mapper resource="com/yll/dao/UserDao.xml"></mapper>
	-->
    <mapper class="com.yll.dao.UserDao"></mapper>
</mappers>
注意:删除映射配置文件

在使用基于注解的Mybatis配置时,请移除xml的映射配置(UserDao.xml)。

测试:
public class Mybatis_Test01{
    public static void main(String[] args) throws Exception{
        //1.读取配置文件
        InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.创建SqlSessionFactory工厂
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        //3.使用工厂创建SqlSession对象
        SqlSession session = factory.openSession();
        //4.使用SqlSession创建UserDao的代理对象
        UserDao userDao = session.getMapper(UserDao.class);
        //5.使用代理对象去执行方法
        List<User> list = userDao.findAll();
        System.out.println(list);
    }
}

(三) CRUD操作

1. 环境搭建

Mybatis开发环境搭建
1. 创建项目导坐标
    mysql驱动   mybatis的坐标   log4j   junit4.10
2. 创建pojo实体类,也叫domain实体类
3. 创建mapper接口
4. 创建mybatis核心配置文件`SqlMapConfig.xml`
5. 创建mybatis的`mapper.xml`映射文件
测试类模板编写
/**
 * UserDao测试类
 */
public class UserDaoTest {

    private SqlSession session ;
    private InputStream is ;
    private UserDao userDao ;

    @Before
    public void init()  throws IOException {
        //1. 创建sqlSessionBuilder构造者对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

        //2. 创建SqlSessionFactory对象
        //2.1 加载配置文件
        is = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.2 构建SqlSession工厂
        SqlSessionFactory sf = builder.build(is);

        //3. 获取SqlSession对象
        session = sf.openSession();

        //4. 获取mapper类的代理对象
        userDao = session.getMapper(UserDao.class);
    }

    @After
    public  void destroy()throws IOException{
        //提交事物
        session.commit();

        //6. 释放资源
        session.close();
        is.close();
    }

    @Test
    public void testFindAll(){
        //5. 调用执行方法
        List<User> users = userDao.findAll();
        System.out.println(users);
    }
}

2. 保存操作©

编写接口方法

UserDao接口中编写方法

/**
 * 添加用户
 * @param user
 */
public void add(User user);
编写映射配置

UserDao.xml中编写添加SQL语句

<insert id="add" parameterType="com.yll.domain.User"><!-- parameterType可以不写-->
    insert into user values (null,#{username},#{birthday},#{sex},#{address})
</insert>
返回主键配置
新增用户后,同时还要返回当前新增用户的id值,因为id是由数据库的自动增长来实现的,所以就相当于我们要在新增后将自动增长auto_increment的值返回.

配置

<insert id="add" parameterType="com.yll.domain.User">
    <!-- 配置保存时获取插入的id,order="AFTER"可以不写,默认值是AFTER -->
    <selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER">
        select last_insert_id();
    </selectKey>

    insert into user values (null,#{username},#{birthday},#{sex},#{address})
</insert>
编写测试方法
@Test
public void testAdd(){
    //创建用户对象
    User user = new User();
    user.setUsername("zhangsan");
    user.setAddress("金融港");
    user.setSex("男");
    user.setBirthday(new Date());

    //调用执行方法
    userDao.add(user);
    //获取自动生成的主键值(第14个视频讲解完之后再获取id)
    int id = user.getId();
    System.out.println(id);
}

3. 更新操作(U)

编写接口方法

UserDao接口中编写更新方法

/**
 * 更新--根据用户id进行更新所有字段
 * 注意:如果字段没有值,将会被更新为null
 * @param user
 */
public void update(User user);
编写映射配置

UserDao.xml中编写更新SQL语句

<update id="update" parameterType="com.yll.domain.User"><!-- parameterType可以不写-->
    update  user  set username = #{username },birthday = #{birthday},sex = #{sex},address=#{address} where id = #{id}
</update>
编写测试方法
@Test
public void testUpdate(){
    //创建用户对象
    User user = new User();
    user.setId(51);
    user.setUsername("洪七公");
    user.setAddress("金融港");
    user.setSex("男");
    user.setBirthday(new Date());

    //调用执行方法
    userDao.update(user);

}

4. 删除操作(D)

编写接口方法

UserDao接口中编写删除方法

/**
 * 根据id删除用户
 * @param id
 */
public void delete(Integer id);
编写映射配置

UserDao.xml中编写删除的SQL语句

<delete id="delete" parameterType="java.lang.Integer"><!-- parameterType可以不写-->
    <!-- 此处的id占位符可以随意写,但是不推荐,跟参数名保持一致-->
    delete  from user where id = #{id } 
</delete>
编写测试方法
@Test
public void testDelete(){
    //调用执行方法
    userDao.delete(51);
}

5. 查询操作®

查询一条数据
编写接口方法

UserDao接口中编写方法

/**
 * 查询一条数据
 * @param id
 */
public User findById(Integer id);
编写映射配置

UserDao.xml中编写查询SQL语句

<select id="findById" resultType="com.yll.domain.User" parameterType="java.lang.Integer">
  select * from user where id = #{id }
</select>
编写测试方法
@Test
public void testFindById(){
    //调用执行方法
    User user = userDao.findById(48);
    System.out.println(user);
}
模糊查询
编写接口方法

UserDao接口中编写方法

/**
 * 根据用户名,模糊查询用户信息
 * @param name
 * @return
 */
public List<User>  findLikeName(String name);
编写映射配置

UserDao.xml中编写查询SQL语句

<select id="findLikeName" resultType="com.yll.domain.User" parameterType="java.lang.String">
  <!--底层使用字符串拼接,不建议使用-->
  <!--select * from user where username like '%${value }%'-->

  <!--底层使用预编译sql语句,建议使用-->
  select * from user where username like #{username }
</select>
编写测试方法
@Test
public void testFindLikeName(){
    //调用执行方法
    //List<User> list = userDao.findLikeName("王"); //底层使用字符串拼接,不建议使用
    List<User> list = userDao.findLikeName("%王%"); //底层使用预编译sql语句,建议使用
    System.out.println(list);
}
查询返回单行单列数据
编写接口方法

UserDao接口中编写方法

/**
 * 查询所有用户数量
 * @return
 */
public int findTotalCount();
编写映射配置

UserDao.xml中编写查询SQL语句

<select id="findTotalCount" resultType="java.lang.Integer" >
  select count(*) from user
</select>

编写测试方法
@Test
public void testFindTotalCount(){
    //调用执行方法
    int count = userDao.findTotalCount();
    System.out.println(count);
}

@Test
public void testFindTotalCount(){
    //调用执行方法
    int count = userDao.findTotalCount();
    System.out.println(count);
}

(四) 参数与返回结果

1. 传入参数

我们在上一章节中已经介绍了SQL语句传参,使用标签的parameterType属性来设定.

该属性的取值可以是基本类型,引用类型(例如:String类型),还可以是实体类类型(POJO类),同时也可以使用实体类的包装类.

单值类型

所谓的单值类型指的是 基本数据类型及其保证类以及字符串类型

基本类型和String我们可以直接写类型名称,也可以使用包名.类名的方式,例如:java.lang.String.

原因是因为mybaits在加载时已经把常用的数据类型注册了别名,从而我们在使用时可以不写包名.
实体类型
<insert id="addUserFindId" parameterType="com.yll.domain.User">
    <selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
        SELECT last_insert_id()
    </selectKey>
    INSERT INTO USER(username,birthday,sex,address) VALUES(#{username},#{birthday},#{sex},#{address})
</insert>
实体类类型,目前我们只能使用全限定类名.

mybaits在加载时已经把常用的数据类型注册了别名,从而我们在使用时可以不写包名.

而实体类并没有注册别名,所以必须写全限定类名.

2. 返回结果

我们在上一章节中已经介绍了SQL语句返回结果类型,resultType属性可以指定结果集的类型,它支持基本类型和实体类类型.

需要注意的是,它和parameterType一样,如果注册过类型别名的,可以直接使用别名.没有注册过的必须使用全限定类名.

简单类型

实体类型
 <select id="findAll" resultType="com.yll.domain.User">
     SELECT * FROM USER
</select>
resultMap类型
当使用pojo类型和pojo列表类型作为输出类型的时候有个前提条件就是pojo中的属性名称必须和数据表中的字段名一致.

如果我们查询出来的数据是由多张表组合而来的,或查询出来的数据有别名和pojo中的名称不对应,此时要怎样输出结果?
 方式一:修改pojo类中的属性与数据表查询出来属性一致,但此种方式改动较大如果在其他地方使用了该pojo将会报错(不推荐)

 方式二:使用resultMap建立对应关系

     resultMap标签可以建立查询的列名和实体类的属性名称不一致时建立对应关系.从而实现封装.

     在select标签中使用resultMap属性指定引用即可.

     同时resultMap可以实现将查询结果映射为复杂类型的pojo,比如在查询结果映射对象中包括pojo和list实现一对一查询和一对多查询.

创建封装数据的实体类UserVo.java
//定义UserVo封装从user表中查询出来的数据  用于测验当封装对象中的字段和数据库中字段不一致时应如何处理
public class UserVo implements Serializable{
    private Integer userId;
    private String userName;
    private Date  userBirthday;
    private Character  userSex;
    private String  userAddress;

    public UserVo() {
    }

    public UserVo(Integer userId) {
        this.userId = userId;
    }

    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 Date getUserBirthday() {
        return userBirthday;
    }

    public void setUserBirthday(Date userBirthday) {
        this.userBirthday = userBirthday;
    }

    public Character getUserSex() {
        return userSex;
    }

    public void setUserSex(Character userSex) {
        this.userSex = userSex;
    }

    public String getUserAddress() {
        return userAddress;
    }

    public void setUserAddress(String userAddress) {
        this.userAddress = userAddress;
    }

    @Override
    public String toString() {
        return "UserVo{" +
                "userId=" + userId +
                ", username='" + userName + '\'' +
                ", userBirthday=" + userBirthday +
                ", userSex=" + userSex +
                ", userAddress='" + userAddress + '\'' +
                '}';
    }
}


UserDao接口中编写方法
//当封装对象UserVo中的字段和数据库中字段不一致时,编写resultMap做字段的映射处理 将数据库中的字段映射到实体类中的字段上去
List<UserVo> findAllToUserVo();
UserDao.xml中编写添加resultMap标签
<!--
建立实体和查询结果集的对应关系
    type属性:指定实体类的全限定类名
    id属性:给定一个唯一标识,是给查询select标签引用用的.

子标签:
    id标签:用于指定主键字段
    result标签:用于指定非主键字段
        column属性:用于指定数据库列名
        property属性:用于指定实体类属性名称
-->
<resultMap id="userMap" type="com.yll.domain.UserVo">
    <id property="userId" column="id"></id>
    <result property="userName" column="username"></result>
    <result property="userBirthday" column="birthday"></result>
    <result property="userSex" column="sex"></result>
    <result property="userAddress" column="address"></result>
</resultMap>
UserDao.xml中编写添加SQL语句
 <select id="findAllToUserVo" resultMap="userMap">
     SELECT * FROM USER
</select>
编写测试代码
 @Test
public void findAllToUserVo(){
    List<UserVo> list = userDao.findAllToUserVo();
    for (UserVo userVo : list) {
        System.out.println(userVo);
    }
}
总结
以上方式需要重新定一个封装数据的pojo类,显的比较麻烦,我们如果不希望新建封装数据的实体类,那么可以使用关系映射进行数据封装

3. @Param注解

@Param注解的作用是给参数命名,参数命名后就能根据名字得到参数值,正确的将参数传入sql语句中(一般通过#{}的方式,${}会有sql注入的问题)

 //根据姓名,地址进行模糊查询
//因为传入两个参数,在XML中系统无法识别那个对应那个, 于是要加上@Param注解,配置好名字,在XML中我们直接使用配置的名字就行了
List<User> findByNameAndAddress(@Param("username") String username, @Param("address") String address);
 <!--根据姓名,地址进行模糊查询  因为有两个条件,并且加了@Param, 那么这里直接就可以使用注解中配置的名字,系统会自动识别-->
<select id="findByNameAndAddress" resultType="user">
    SELECT * FROM user WHERE username LIKE #{username} and address LIKE #{address};
</select>

(五) 核心配置文件

MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置(settings)和属性(properties)信息.文档的顶层结构如下:

configuration 配置
    properties 属性
    settings 设置
    typeAliases 类型别名
    typeHandlers 类型处理器
    objectFactory 对象工厂
    plugins 插件
    environments 环境
        environment 环境变量
            transactionManager 事务管理器
            dataSource 数据源
    databaseIdProvider 数据库厂商标识
    mappers 映射器

1. properties标签

在使用properties标签配置时,我们可以采用两种方式指定属性配置

第一种:静态属性值
<properties>
  <property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
  <property name="jdbc.url" value="jdbc:mysql://localhost:3306/eesy"/>
  <property name="jdbc.username" value="root"/>
  <property name="jdbc.password" value="1234"/>
</properties>
第二种:动态属性值

1. 在classpath下定义db.properties文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis_01
jdbc.username=root
jdbc.password=1234

2. properties标签配置,引用db.properties中的数据

<properties resource="db.properties"></properties>

3. 引用db.properties中的数据库配置信息

<environment id="mysql">
    <transactionManager type="JDBC"></transactionManager>
    <dataSource type="POOLED">
        <property name="driver" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </dataSource>
</environment>

2. typeAliases 类型别名

<typeAliases>
    <!-- 单个别名定义 -->
    <typeAlias alias="user" type="com.yll.domain.User"/>
    <!-- 批量别名定义,扫描整个包下的类,别名为类名(首字母大写或小写都可以) -->
    <package name="com.yll.domain"/>
</typeAliases>
每一个在包 com.yll.domain 中的 JavaBean,在没有注解的情况下,会使用 Bean 的类名来作为它的别名.不区分大小写

比如 domain.blog.Author 的别名为 author;

若有注解,则别名为其注解值.看下面的例子:

@Alias("user")
public class User {
  ......
}

3. mappers映射器

我们在使用Mybatis开发时,会使用mapper映射配置文件定义SQL语句.之后我们需要告诉 MyBatis 到哪里去找到这些语句.

Java 在自动查找这方面没有提供一个很好的方法,所以最佳的方式是告诉 MyBatis 到哪里去找映射文件.

我们可以使用相对于类路径的资源引用, 或完全限定资源定位符(包括 file:/// 的 URL),或类名和包名等.

1. 配置mapper.xml映射配置文件路径

<!-- 使用相对于类路径的资源引用,适用于有映射文件的方式 -->
<mappers>
    <mapper resource="com/yll/dao/UserDao.xml"></mapper>
</mappers>

2. 配置mapper接口路径

<mappers>
    <!--class属性值为mapper接口全类名,既适用于注解的方式配置sql语句也适用于有映射文件的方式。-->
    <mapper class="com.yll.dao.UserDao"></mapper>
</mappers>

3. 扫描包下的所有mapper接口

<mappers>
    <!--用于指定所有dao所在的包,指定了就不需要再写mapper标签以及它的resource和class属性了-->
    <package name="com.yll.dao"></package>
</mappers>

(六) SQL标签

Mybatis的映射文件中,前面我们的SQL都是比较简单的,有些时候业务逻辑复杂时,我们的SQL是动态变化的,此时在前面的学习中我们的SQL就不能满足要求了。

MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。

例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。

虽然在以前使用动态 SQL 并非一件易事,但正是 MyBatis 提供了可以被用在任意 SQL 映射语句中的强大的动态 SQL 语言得以改进这种情形。

动态 SQL 元素和 JSTL 或基于类似 XML 的文本处理器相似。

MyBatis 3 大大精简了元素种类,现在只需学习原来一半的元素便可。

if
choose (when, otherwise)
trim (where, set)
foreach

1. 动态SQL之 if 标签

If标签用来判断输入参数是否符合规范,最常见的用法就是做多条件查询

编写接口方法

在UserDao接口中编写方法

/**
 * 根据用户信息,查询用户列表
 * @param user
 * @return
 */
List<User> findByUser(User user);
编写映射配置

在UserDao.xml中编写添加SQL语句

<!-- 动态sql,if标签-->
<select id="findByUser" resultType="user" parameterType="user">
    select * from user where 1=1

    <!-- 如果username不为null ,筛选用户名 -->
    <if test="username!=null and username != '' ">
        and username like '%${username}%'
    </if>

    <!-- 如果address不为null ,筛选地址 -->
    <if test="address != null and address != '' ">
        and address like '%${address}%'
    </if>
</select>
编写测试方法
@Test
public void findByUser() {
    //创建用户对象
    User user = new User();
    user.setUsername("小");
    user.setAddress("长");

    List<User> users = userDao.findByUser(user);
    System.out.println(users);
}

2. 动态SQL之 where 标签

在上面的案例中我们为了能够将两个条件拼接使用了一个小技巧

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9AGbaFZZ-1649528615226)(C:\Users\Administrator\Desktop\二期\框架阶段\day17_Mybatis\笔记\01-Mybatis框架一.assets\markdown-img-paste-20180831155727989.png)]

我们在where后面使用了一个1=1的恒等式,保证我们拼接的sql语句不会出现语法问题

mybatis提供了一个where标签,也可以帮助我们来解决sql中条件拼接的问题

编写接口方法

UserDao接口中编写方法

/**
 * 根据用户信息,查询用户列表
 * @param user
 * @return
 */
List<User> findLikeUser(User user);
编写映射配置

UserDao.xml中编写添加SQL语句

<!-- 动态sql,if标签  where标签-->
<select id="findLikeUser" resultType="user" parameterType="user">
    select * from user
    <where>
        <!-- 如果username不为null ,筛选用户名 -->
        <if test="username!=null and username != '' ">
            and username like '%${username}%'
        </if>

        <!-- 如果address不为null ,筛选地址 -->
        <if test="address != null and address != '' ">
            and address like '%${address}%'
        </if>
    </where>
</select>
编写测试方法
@Test
public void findLikeUser() {
    //创建用户对象
    User user = new User();
    user.setUsername("小");
    user.setAddress("长");

    List<User> users = userDao.findLikeUser(user);
    System.out.println(users);
}

3. 动态SQL之 foreach 标签

动态 SQL 的另外一个常用的操作需求是对一个集合进行遍历,通常是在构建 IN 条件语句的时候。比如:

编写接口方法

在UserDao接口中编写方法

/**
 * 根据传入的id查询用户信息
 * @param ids
 * @return
 */
List<User> findUsersByIds(Integer[] ids);
编写映射配置

在UserDao.xml中编写添加SQL语句

<!--根据多个id查询用户信息-->
<select id="findUsersByIds" resultType="user" >
    SELECT *from USER
    <where>
        <foreach collection="array"  open="and id in (" close=")" separator="," item="uid">
            #{uid}
        </foreach>
    </where>
</select>
注意:我们可以将任何`可迭代对象(如 List、Set)`、`Map对象`或者`数组对象`传递给 foreach 作为集合参数。

当使用可迭代对象或者数组时,`index`是当前迭代的次数,`item` 的值是本次迭代获取的元素。

<foreach>标签用于遍历集合,它的属性:
    collection:代表要遍历的集合元素,注意编写时不要写#{}
    open:代表语句的开始部分
    close:代表结束部分
    item:代表遍历集合的每个元素,生成的变量名
    sperator:代表分隔符

collection属性是在使用foreach的时候最关键的也是最容易出错的,该属性是必须指定的,但是在不同情况下,该属性的值是不一样的,主要有一下3种情况:

如果传入的参数是pojo类,collection属性值可以写pojo类的集合属性名称

如果传入的是单参数且参数类型是一个List的时候,collection属性值为list

如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array

如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map,所以这个时候collection属性值map的集合属性的key.

编写测试方法
 @Test
    //根据传入的dogId对象中的list集合中所包装的id值  查找对应的元素
    //sql语句的foreach语句的用法
    public void findDogByIds(){
        ArrayList<Integer> list = new ArrayList<Integer>();
        Collections.addAll(list,1,2);

        DogId dogId = new DogId(list);

        List<Dog> dogList = dogDao.findDogByIds(dogId);
        for (Dog d : dogList) {
            System.out.println(d);
        }
    }

4. Mybatis中的SQL片段

Sql中可将重复的sql提取出来,使用时用include引用即可,最终达到sql重用的目的。

我们之前写的功能中,每一个功能都会有 SELECT *from USER 这么一条SQL,对于这些重复性比较高的SQL语句我们可以提取出来,定义一个SQL片段,最终达到sql重用的目的.

定义SQL片段
<!--定义sql片段-->
<sql id="select_user">
    select * from user
</sql>
引用SQL片段

标签的refid属性的值就是 标签定义id的取值。

如果引用其它mapper.xml的sql片段,则在引用时需要加上namespace<include refid="namespace.sql片段”/>

(七) Mybatis多表查询

1. 多表之间的关系

一对多关系 ,   例如:
		一个部门下可以有多个员工,一个员工只能属于某一个部门。
		一个分类下有多个商品,一个商品只属于一个分类
		一个用户名下有多张银行卡,一个银行卡只属于一个用户

多对多关系 ,  例如:
		一个学生可以选择多门课程,一门课程可以被多个学生选择。
		一个订单可以有多个商品,一个商品也可以属于多个订单
		一个用户可以使用多辆自行车,一个自行车也可以被多个用户使用

 一对一关系 , 例如:
		一个公司可以有一个注册地址,一个注册地址只能对一个公司。
		一个人只能有一张身份证,一个身份证也只属于一个人

2. 环境准备

数据库准备(如果之前创建了就不用再创建)

使用用户和帐户表来说明表之间的级联操作

DROP TABLE IF EXISTS `user`;

CREATE TABLE `user` (
  `id` int(11) NOT NULL auto_increment,
  `username` varchar(32) NOT NULL COMMENT '用户名称',
  `birthday` datetime default NULL COMMENT '生日',
  `sex` char(1) default NULL COMMENT '性别',
  `address` varchar(256) default NULL COMMENT '地址',
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert  into `user`(`id`,`username`,`birthday`,`sex`,`address`) values (41,'老王','2018-02-27 17:47:08','男','北京'),(42,'小二王','2018-03-02 15:09:37','女','北京金燕龙'),(43,'小二王','2018-03-04 11:34:34','女','北京金燕龙'),(45,'孙悟空','2018-03-04 12:04:06','男','北京金燕龙'),(46,'老王','2018-03-07 17:37:26','男','北京'),(48,'小马宝莉','2018-03-08 11:44:00','女','北京修正');

DROP TABLE IF EXISTS `account`;

CREATE TABLE `account` (
  `ID` int(11) NOT NULL COMMENT '编号',
  `UID` int(11) default NULL COMMENT '用户编号',
  `MONEY` double default NULL COMMENT '金额',
  PRIMARY KEY  (`ID`),
  KEY `FK_Reference_8` (`UID`),
  CONSTRAINT `FK_Reference_8` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert  into `account`(`ID`,`UID`,`MONEY`) values (1,41,1000),(2,45,1000),(3,41,2000);


DROP TABLE IF EXISTS `role`;

CREATE TABLE `role` (
  `ID` int(11) NOT NULL COMMENT '编号',
  `ROLE_NAME` varchar(30) default NULL COMMENT '角色名称',
  `ROLE_DESC` varchar(60) default NULL COMMENT '角色描述',
  PRIMARY KEY  (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert  into `role`(`ID`,`ROLE_NAME`,`ROLE_DESC`) values (1,'院长','管理整个学院'),(2,'总裁','管理整个公司'),(3,'校长','管理整个学校');

DROP TABLE IF EXISTS `user_role`;

CREATE TABLE `user_role` (
  `UID` int(11) NOT NULL COMMENT '用户编号',
  `RID` int(11) NOT NULL COMMENT '角色编号',
  PRIMARY KEY  (`UID`,`RID`),
  KEY `FK_Reference_10` (`RID`),
  CONSTRAINT `FK_Reference_10` FOREIGN KEY (`RID`) REFERENCES `role` (`ID`),
  CONSTRAINT `FK_Reference_9` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert  into `user_role`(`UID`,`RID`) values (41,1),(45,1),(41,2);

开发环境准备
创建pojo实体类
Account.java

一个账户信息只能供某个用户使用

User.java

一个用户可以有多个账户

3. 多表查询之一对一

需求:查询所有账户信息,关联查询该账户所属的用户信息。

注意:
    因为一个账户信息只能供某个用户使用,所以从查询账户信息出发关联查询用户信息为一对一查询。

    如果从用户信息出发查询用户下的账户信息则为一对多查询,因为一个用户可以有多个账户。
方式一:扩展Account类
分析SQL语句

实现查询账户信息时,也要查询账户所对应的用户信息。

SELECT account.*, user.username, user.address FROM account, user WHERE account.uid = user.id

为了能够封装上面SQL语句的查询结果,我们可以通过继承扩展Account类

定义 AccountUser类中要包含账户信息同时还要包含用户信息,所以我们要在定义AccountUser类时可以继承User类。

定义AccountUser类

定义账户的持久层Dao接口
public interface AccountDao {

    /**
     * 查询所有账户信息,包含用户信息
     * @return
     */
    public List<AccountUser> findAll();
}
编写SQL映射文件AccountDao.xml
<select id="findAll" resultType="AccountUser">
    select  a.*,u.username,u.address from  account a inner join user u on a.UID = u.id
</select>
编写测试类
@Test
public void findAll() {
    List<AccountUser> aus = accountDao.findAll();
    System.out.println(aus);
}
方式二: 使用结果集映射

定义专门的resultMap用于映射一对一查询结果。

通过面向对象的(has a)关系可以得知,我们可以在Account类中加入一个User类的对象来代表这个账户是哪个用户的。

定义Account类,添加代表账户所属用户的User属性

AccountDao接口中编写方法
/**
 * 查询所有账户信息,包含用户对象
 * @return
 */
public List<Account> findAllAccountWithUser();
编写SQL映射文件AccountDao.xml
<!--定义结果集映射-->
<resultMap id="AccountUserMap" type="Account" >
    <id column="id" property="aid"></id>
    <result column="uid" property="uid"></result>
    <result column="money" property="money"></result>

    <!--配置一对一映射,一个账户只属于一个用户,javaType用来指定pojo中属性的类型 -->
    <association property="user" javaType="user">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>
    </association>
</resultMap>

<!--定义SQL语句-->
<select id="findAllAccountWithUser" resultMap="AccountUserMap">
    select u.*,a.id aid,a.uid,a.money from account a,user u where a.uid=u.id;
</select>
编写测试类
@Test
public void findAllAccountWithUser() {
    List<Account> accounts = accountDao.findAllAccountWithUser();
    System.out.println(accounts);
}

4. 多表查询之一对多

在帐户表中我们可以看到编号为41的用户有两个帐号,那么此时用户和帐号之间的关系为一对多,即一个用户拥有多个帐号。

现在我们来看一下如何通过用户来获得与他相关的所有帐号信息

建立实体之间的关系

注意getter和setter方法

编写接口方法

UserDao接口中编写方法

/**
 * 查询所有用户信息,以及对应的账户信息
 * @return
 */
public List<User> findAllWithAccount();
编写映射配置

编写SQL映射文件UserDao.xml , 使用 resultMap标签建立结果集映射

<!--定义结果集映射-->
<resultMap id="UserAccountMap" type="User">
    <id column="id" property="id"></id>
    <result column="username" property="username"></result>
    <result column="birthday" property="birthday"></result>
    <result column="sex" property="sex"></result>
    <result column="address" property="address"></result>

    <!--
        配置一对多映射,一个用户可以有多个账户,ofType属性用来指定集合中的pojo类型
    -->
    <collection property="accounts" ofType="Account" >
        <id column="aid" property="id"></id>
        <result column="id" property="uid"></result>
        <result column="money" property="money"></result>
    </collection>
</resultMap>

<!--定义查询的sql语句-->
<select id="findAllWithAccount" resultMap="UserAccountMap" >
    select u.*,a.id aid,a.MONEY  from  user u inner join account a on u.id = a.uid
</select>
编写测试类
@Test
public void findAllWithAccount() {
    List<User> users = userDao.findAllWithAccount();
    System.out.println(users);
}

5. 多表查询之多对多

多对多关系其实是双向的一对多关系。此处我们使用用户表和角色表.一个用户可以有多个角色,一个角色也可以属于多个用户

需求分析

实现在需要查询所有角色并且加载它所分配的用户信息。

分析: 查询角色我们需要用到Role表,但角色分配的用户的信息我们并不能直接找到用户信息,而是要通过中间表(USER_ROLE表)才能关联到用户信息。

下面是实现的SQL语句():

SELECT r.id rid,ROLE_NAME,r.ROLE_DESC,u.* from role r
left JOIN user_role ur ON r.ID = ur.RID
LEFT JOIN user u ON ur.UID = u.id;
建立实体之间的关系
/**
 * 角色实体
 */
public class Role {

    private Integer id ;

    private String role_name ;

    private String role_desc ;

    //多对多的关系映射:一个角色可以赋予多个用户
    private List<User> users;

    // getter/setter/toString方法此处省略。。。
}
编写接口方法

定义角色的持久层Dao接口

public interface RoleDao {

    /**
     * 查询所有的角色信息,包含拥有此角色的用户信息
     * @return
     */
    public List<Role> findAll();
}
编写映射配置

编写SQL映射文件RoleDao.xml , 建立结果集映射

<!--配置结果集映射-->
<resultMap id="roleMap" type="Role">
    <id property="id" column="rid"></id>
    <result property="role_name" column="role_name"></result>
    <result property="role_desc" column="role_desc"></result>

    <!--
        配置一对多映射,一个角色可以赋予多个用户
    -->
    <collection property="users" ofType="user">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>
    </collection>
</resultMap>

<select id="findAll" resultMap="roleMap">
      SELECT r.id rid,ROLE_NAME,r.ROLE_DESC,u.* from role r
      left JOIN user_role ur ON r.ID = ur.RID
      LEFT JOIN user u ON ur.UID = u.id;
</select>
编写测试类
@Test
public void findAll() {
    List<Role> roles = roleDao.findAll();
    System.out.println(roles);
}

6. 多表查询总结

resultMap中特殊节点说明:
  1. association:主要是用来解决一对一关系的。用来对主实体类中对象属性做映射
  2. collection: 用来解决一对多级联。用来对主实体类中的集合对象属性做映射

(八) Mybatis延迟加载

1. 什么是延迟加载

延迟加载: 就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据.延迟加载也称懒加载.

立即加载:不管用不用,只要一调用马上发起查询

2. 需求场景介绍

查询账户(Account)信息并且关联查询用户(User)信息.

先查询账户(Account)信息,当我们需要查询用户(User)信息时再查询用户(User)信息.

把对用户(User)信息的按需去查询就是延迟加载.

mybatis第二天实现多表操作时,我们使用了resultMap来实现一对一,一对多,多对多关系的操作.主要是通过association、collection实现一对一及一对多映射.

association、collection具备延迟加载功能.

3. Mybatis环境搭建

创建maven项目

创建pojo实体类

User实体类

public class User {

    private int id ;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    //账户属性 , 一个用户有多个账户
    private List<Account> accounts ;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

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

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public List<Account> getAccounts() {
        return accounts;
    }

    public void setAccounts(List<Account> accounts) {
        this.accounts = accounts;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                ", accounts=" + accounts +
                '}';
    }
}

账户实体类

package com.yll.domain;

/**
 * 用户账户信息实体
 */
public class Account {

    private  int id ;

    private int uid ;

    private double money ;

    private User user ;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getUid() {
        return uid;
    }

    public void setUid(int uid) {
        this.uid = uid;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", uid=" + uid +
                ", money=" + money +
                '}';
    }
}

为了能看到懒加载效果,注意toString不要输出user属性

4. 一对一延迟加载

编写接口方法

UserDao

public interface UserDao {

    /**
     * 查询根据ID查询用户信息
     * @param uid
     * @return
     */
    public User findById(int uid);
}

AccountDao

public interface AccountDao {

    /**
     * 查询所有账户信息
     * @return
     */
    public List<Account> findAll();
}
编写映射配置

编写UserDao.xml

<!--定义查询的sql语句-->
<select id="findById" resultMap="User"  parameterType="int" >
    select *  from  user  where id = #{id }
</select>

编写AccountDao.xml

编写账户的SQL映射文件AccountDao.xml , 使用 association 标签配置一对一延迟加载

property : 需要加载数据的的实体属性名称

javaType : 实体属性的类型

column : 根据查询结果的那一列去加载数据,必须指定

select : 调用哪一个查询操作去加载数据,必须指定

fetchType:加载数据类型,默认值lazy表示延迟加载,还有一个eager值表示立即加载,可选

<!-- 建立对应关系 -->
<resultMap type="account" id="AccountUserMap">
    <id column="aid" property="id"/>
    <result column="uid" property="uid"/>
    <result column="money" property="money"/>
    <!--配置account和user的一对一关系并配置延时加载-->
    <association property="user" javaType="user" select="com.yll.dao.UserDao.findById"
                 column="uid">
    </association>
</resultMap>

<!--定义SQL语句-->
<select id="findAll" resultMap="AccountUserMap">
    select  * from account
</select>
编写测试类
@Test
public void findAll() {
    List<Account> accounts = accountDao.findAll();
    for (Account account : accounts) {
        System.out.println("账户信息:"+account);
        System.out.println("用户信息:"+account.getUser());
        System.out.println("-------------------------");
    }
}

根据日志可以看到,此种方式配置Mybatis走的是立即加载,即一次性发送多条SQL语句加载所有数据

开启延迟加载

SqlMapConfig.xml配置文件中开启全局的懒加载配置

<settings>
    <!--
    延迟加载的全局开关. 当开启时,所有关联对象都会延迟加载.
    true    开启懒加载
    false   关闭懒加载
    -->
    <setting name="lazyLoadingEnabled" value="true"/>

    <!--
    积极延迟加载的全局开关 .
    true 代表只要访问延迟加载对象的任意一个属性,都会导致这个延迟加载对象的完全加载
    false 每个属性会按需加载(参考lazyLoadTriggerMethods).
    -->
    <setting name="aggressiveLazyLoading" value="false"/>

    <!--
    指定哪个对象的方法触发一次延迟加载
    在mybatis中,默认情况下只要调用了equals,clone,hashCode,toString这几个方法,都会对对象进行完全加载
    因为这里我需要调用toString方法,查看返回的数据,又不想触发完全加载,所以只要配置一个不是toString的值就能覆盖默认值,这样调用toString方法就不会触发延迟加载
    -->
    <setting name="lazyLoadTriggerMethods" value="hashCode"/>
</settings>
重新执行测试

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BzpfhU6x-1649528615228)(C:\Users\Administrator\Desktop\二期\框架阶段\day19_Mybatis\笔记\04-Mybatis框架.assets\image-20210710220356879.png)]

5. 一对多延迟加载

我们不仅可以在 一对一的节点上配置延迟加载,也可以在一对多的结点中配置延迟加载策略.

结点中也有select属性,column属性.

需求: 完成加载用户对象时,查询该用户所拥有的账户信息.

编写接口方法

编写AccountDao.java

/**
 * 根据用户id查询用户的所有账户信息
 * @param uid
 * @return
 */
public List<Account> findByUid(int uid);

编写UserDao.java

/**
 * 查询所有用户信息
 * @return
 */
public List<User> findAll();
编写映射配置

编写AccountDao.xml

<select id="findByUid"  parameterType="int" resultType="Account">
    select  * from account where uid = #{uid }
</select>

编写UserDao.xml

编写用户的映射配置文件UserDao.xml 使用 collection 标签配置一对多延迟加载

property : 需要加载数据的的实体属性名称

javaType : 实体属性的类型

ofType : 集合中的数据类型

column : 根据查询结果的那一列去加载数据

select : 调用哪一个查询操作去加载数据

<!--定义结果集映射-->
<resultMap id="UserAccountsMap" type="User">
    <id column="id" property="id"></id>
    <result column="username" property="username"></result>
    <result column="birthday" property="birthday"></result>
    <result column="sex" property="sex"></result>
    <result column="address" property="address"></result>

    <!--
        配置一对多映射,一个用户可以有多个账户
    -->
    <collection property="accounts" ofType="Account" select="com.yll.dao.AccountDao.findById" column="id" >
    </collection>
</resultMap>

<select id="findAll" resultMap="UserAccountsMap" >
    select *  from  user
</select>
编写测试类
@Test
public void findAll() {
    List<User> users = userDao.findAll();
    for (User user : users) {
        System.out.println("用户信息:"+user);
        System.out.println("账户信息:"+user.getAccounts());
        System.out.println("-----------------------------");
    }
}

(九) Mybatis缓存机制

缓存就是存在于内存中的临时数据.使用缓存能减少数据库的查询次数, 从而提高性能.但不是所有场合都适用于缓存.

适用场合:经常查询且不经常改变的数据,数据的正确与否对结果影响不大的数据

不适用场合:经常改变的数据,数据的正确与否对结果的影响很大.

Mybatis的缓存分为:一级缓存和二级缓存

一级缓存:基于Perpetual CacheHashMap本地缓存,其存储作用域为SqlSession,当SqlSession flushclose 之后,该SqlSession当中的所有缓存就将清空.

二级缓存:二级缓存与一级缓存其机制相同,默认也是采用PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如Ehcache.

1. Mybatis一级缓存

一级缓存是SqlSession级别的缓存,只要SqlSession没有flush或close,它就存在.

当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供一块区域中.

该区域的结构是一个Map.当我们再次查询同样的数据,mybatis会先去sqlsession中查询是否有,有的话直接拿出来用,当SqlSession对象消失时,mybatis的一级缓存也就消失了.

测试一级缓存(理解)
@Test
public void findById() {
    User user = userDao.findById(43);
    System.out.println("第一次查询:"+user);
    User user2 = userDao.findById(43);
    System.out.println("第一次查询:"+user2);
}

调用了两次dao查询方法,但只执行了一条sql语句,说明第二次查询获得的对象是从缓存中取的,并未执行sql语句

测试缓存是存在SqlSession中的(理解)
    @Test
    public void findById() {
        User user = userDao.findById(43);
        System.out.println("第一次查询:"+user);

        session.clearCache();//清空session

        User user2 = userDao.findById(43);
        System.out.println("第一次查询:"+user2);
    }

第一次查询执行完毕之后,清空session,执行第二次查询,二次查询执行了二条sql语句,说明第二次查询没有从缓存中获取到数据,重新发了SQL语句查询

测试一级缓存的清空(理解)

一级缓存是 SqlSession 范围的缓存,当调用sqlSession完成CUD(插入、更新、删除)操作, 会清空SqlSession中的一级缓存

这样做的目的为了让缓存中存储的是最新的信息,避免脏读.

当然当SqlSession关闭,或者我们手动调用session.clearCache()也可以清空一级缓存

编写用户的Dao接口UserDao.java及映射文件UserDao.xml
编写用户的Dao接口UserDao.java
/**
 * 更新用户信息
 * @param user1
 */
void update(User user1);
编写用户的映射文件UserDao.xml
<update id="update">
    update  user  set username = #{username },birthday = #{birthday },sex = #{sex },address = #{address } where id = #{id }
</update>
编写测试代码
@Test
public void testClearlCache() {
    //1.根据id查询用户
    User user1 = userDao.findById(41);
    System.out.println(user1);

    //2.更新用户信息
    user1.setUsername("update user clear cache");
    user1.setAddress("北京市海淀区");
    userDao.update(user1);

    //3.再次查询id为41的用户
    User user2 = userDao.findById(41);
    System.out.println(user2);
    System.out.println(user1 == user2);
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-97LH8fBe-1649528615230)(https://gitee.com/shuaiqiguoguo/cloudimage/raw/master/img/202204100217470.png)]

2. Mybatis二级缓存

二级缓存是mapper映射级别的缓存,多个SqlSession去操作同一个Mapper映射的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的.

首先开启mybatis的二级缓存.

sqlSession1去查询用户信息,查询到用户信息会将查询数据存储到二级缓存中.

sqlSession2去查询与sqlSession1相同的用户信息,首先会去缓存中找是否存在数据,如果存在直接从缓存中取出数据.

如果SqlSession3去执行相同mapper映射下sql,执行commit提交,将会清空该 mapper映射下的二级缓存区域的数据.

开启二级缓存(理解)
第一步:在SqlMapConfig.xml文件开启二级缓存

第二步:配置相关的Mapper映射文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jLFWIFkU-1649528615231)(C:\Users\Administrator\Desktop\二期\框架阶段\day19_Mybatis\笔记\04-Mybatis框架.assets\image-20210710220514395.png)]

测试二级缓存(理解)
@Test
public void testSecondLevelCache() {
    SqlSession sqlSession1 = sf.openSession();
    UserDao dao1 = sqlSession1.getMapper(UserDao.class);
    User user1 = dao1.findById(41);
    System.out.println(user1);

    //一级缓存消失,一定要close,否则一级缓存还在,就不会有二级缓存
    sqlSession1.close();

    SqlSession sqlSession2 = sf.openSession();
    UserDao dao2 = sqlSession2.getMapper(UserDao.class);
    User user2 = dao2.findById(41);
    System.out.println(user2);

    sqlSession2.close();
    System.out.println(user1 == user2);//false  二级缓存缓存的是数据而不是对象,从缓存中获取到对象重新组装成对象返回
}

注意(理解)

二级缓存缓存的是数据而不是对象,从缓存中获取到对象重新组装成对象返回

所以当我们在使用二级缓存时,所缓存的类一定要实现java.io.Serializable接口,这种就可以使用序列化方式来保存对象.

(十) Mybatis注解开发

1. 常用注解说明

mybatis注解就是使用注解代替mapper.xml映射文件,也就是说以后编写dao接口是没有对应的mapper.xml映射文件的.

虽然mybatis有注解功能但官方推荐使用mapper.xml配置方式来开发dao,注解方式大家会用即可

mybatis注解如下:

注解功能
@Insert实现新增
@Update实现更新
@Delete实现删除
@Select实现查询
@Result实现结果集封装
@Results可以与@Result 一起使用,封装多个结果集
@One实现一对一结果集封装
@Many实现一对多结果集封装
@SelectProvider实现动态SQL映射

mybatis针对crud操作一共四个注解:@select、@update、@insert、@update下面我们使用注解来进行CRUD操作

2. 环境准备

创建项目

创建实体类

注意:此处我们故意和数据库表的列名不一致。为了方便测试

引入配置文件

修改核心配置文件

修改核心配置文件 SqlMapConfig.xml 扫描包下的所有接口上的注解

3. 完成CRUD操作

编写接口方法
public interface UserDao {

    /**
     * 查询所有用户信息
     *
     * @return
     */
    @Select("select * from user")
    @Results(id = "userMap",
            value = {
                    @Result(id = true, column = "id", property = "userId"),
                    @Result(column = "username", property = "userName"),
                    @Result(column = "sex", property = "userSex"),
                    @Result(column = "address", property = "userAddress"),
                    @Result(column = "birthday", property = "userBirthday")})
    public List<User> findAll();

    /**
     * 查询根据ID查询用户信息
     *
     * @param uid
     * @return
     */
    @Select("select * from user where id = #{uid }")
    @ResultMap("userMap")
    public User findById(int uid);

    /**
     * 插入用户数据
     *
     * @param user
     * @return
     */
    @Insert("insert into user(username,sex,birthday,address)values(#{userName},#{userSex},#{userBirthday},#{userAddress})")
    @SelectKey(keyColumn = "id", keyProperty = "userId", resultType = Integer.class, before = false, statement = {"select last_insert_id()"})
    int add(User user);

    /**
     * 更新用户信息
     *
     * @param user
     */
    @Update("update user set username=#{userName},address=#{userAddress},sex=#{userSex},birthday=#{userBirthday} where id =#{userId} ")
    void update(User user);

    /**
     * 根据用户id删除用户
     *
     * @param id
     * @return
     */
    @Delete("delete from user where id = #{uid} ")
    int deleteById(Integer id);

    /**
     * 查询总数量
     *
     * @return
     */
    @Select("select count(*) from user ")
    int findTotalCount();


    /**
     * 模糊查询
     * @param name
     * @return
     */
    @Select("select * from user where username like #{username} ")
    List<User> findLikeName(String name);
}
编写测试方法
public class UserDaoTest {

    private SqlSession session;
    private InputStream is;
    private UserDao userDao;

    @Before
    public void init() throws IOException {
        //1. 创建sqlSessionBuilder构造者对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

        //2. 创建SqlSessionFactory对象
        //2.1 加载配置文件
        is = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.2 构建SqlSession工厂
        SqlSessionFactory sf = builder.build(is);

        //3. 获取SqlSession对象
        session = sf.openSession(true);

        //4. 获取mapper类的代理对象
        userDao = session.getMapper(UserDao.class);
    }

    @After
    public void destroy() throws IOException {
        //提交事物
        //session.commit();
        //6. 释放资源
        session.close();
        is.close();
    }


    @Test
    public void findAll() {
        List<User> users = userDao.findAll();
        System.out.println(users);
    }

    @Test
    public void findById() {
        User user = userDao.findById(44);
        System.out.println(user);
    }

    @Test
    public void add() {
        //创建用户对象
        User user = new User();
        user.setUserName("段誉");
        user.setUserAddress("金融港");
        user.setUserSex("男");
        user.setUserBirthday(new Date());

        //调用执行方法
        userDao.add(user);
        //获取自动生成的主键值
        int id = user.getUserId();
        System.out.println(id);
    }

    @Test
    public void update() {
        //创建用户对象
        User user = userDao.findById(45);
        user.setUserAddress("长城园");
        userDao.update(user);
    }

    @Test
    public void deleteById() {
        userDao.deleteById(57);
    }

    @Test
    public void findTotalCount() {
        int count = userDao.findTotalCount();
        System.out.println(count);
    }

    @Test
    public void findLikeName() {
        List<User> users = userDao.findLikeName("%小%");
        System.out.println(users);
    }
}
注解总结

@Insert @Update @Delete @Select 注解可以帮我们完成基本的CRUD操作

如果查询的数据库字段与实体属性不一致,我们可以使用@Results注解建立映射关系

@Select("select * from user")
@Results(id = "userMap",
        value = {
                @Result(id = true, column = "id", property = "userId"),
                @Result(column = "username", property = "userName"),
                @Result(column = "sex", property = "userSex"),
                @Result(column = "address", property = "userAddress"),
                @Result(column = "birthday", property = "userBirthday")})
public List<User> findAll();

4. 注解复杂关系映射

实现复杂关系映射之前我们可以在映射文件中通过配置来实现,在使用注解开发时我们需要借助**@Results注解,@Result注解,@One注解,@Many**注解.

@Results代替的是标签<resultMap>该注解中可以使用单个@Result注解,也可以使用@Result集合

@Results({@Result(),@Result()})或@Results(@Result())

@Result注解代替了<id>标签和<result>标签 :

@Result 中 属性介绍:
      id 是否是主键字段
      column 数据库的列名
      property需要装配的属性名
      one需要使用的@One注解@Result(one=@One),many 需要使用的@Many注解@Result(many=@many)

@One注解(一对一) 代替了<assocation>标签,是多表查询的关键,在注解中用来指定子查询返回单一对象.

@One注解属性介绍:
      select 指定用来多表查询的sqlmapper
      fetchType会覆盖全局的配置参数lazyLoadingEnabled.. 
      使用格式: @Result(column=" ",property="",one=@One(select=""))

@Many注解(多对一) 代替了<Collection>标签,是是多表查询的关键,在注解中用来指定子查询返回对象集合.

聚集元素用来处理"一对多"的关系.需要指定映射的Java实体类的属性,属性的javaType(一般为ArrayList)但是注解中可以不定义;
使用格式`@Result(property="",column="",many=@Many(select=""))`
注解一对一

需求:加载账户信息时并且加载该账户的用户信息,根据情况可实现延迟加载。(注解方式实现)

建立实体之间的关系

编写接口方法

编写AccountDao方法

public interface AccountDao {

    /**
     * 查询所有账户信息
     *
     * @return
     */
    @Select("select * from account")
    @Results(id = "accountMap",
            value = {
                    @Result(id = true, column = "id", property = "id"),
                    @Result(column = "uid", property = "uid"),
                    @Result(column = "money", property = "money"),
                    @Result(column = "uid", property = "user",
                            one = @One(select = "com.yll.dao.UserDao.findById",fetchType = FetchType.LAZY))})
    public List<Account> findAll();
}

编写UserDao方法

/**
 * 查询根据ID查询用户信息
 *
 * @param uid
 * @return
 */
@Select("select * from user where id = #{uid }")
@Results(id = "userMap",
        value = {
                @Result(id = true, column = "id", property = "userId"),
                @Result(column = "username", property = "userName"),
                @Result(column = "sex", property = "userSex"),
                @Result(column = "address", property = "userAddress"),
                @Result(column = "birthday", property = "userBirthday")})
public User findById(int uid);
编写测试代码
@Test
public void findAll() {
    List<Account> accounts = accountDao.findAll();
    for (Account account : accounts) {
        System.out.println("账户信息:"+account);
        System.out.println("用户信息:"+account.getUser());
        System.out.println("-------------------------");
    }
}
注解一对多

需求:查询用户信息时,也要查询他的账户列表。使用注解方式实现。一个用户可以拥有多个账户

建立实体之间的关系

编写接口方法

编写UserDao方法

/**
 * 查询所有用户信息
 *
 * @return
 */
@Select("select * from user")
@Results(id = "userAccountsMap",
        value = {
                @Result(id = true, column = "id", property = "userId"),
                @Result(column = "username", property = "userName"),
                @Result(column = "sex", property = "userSex"),
                @Result(column = "address", property = "userAddress"),
                @Result(column = "birthday", property = "userBirthday"),
                @Result(column = "id",property = "accounts",
                        many = @Many(select="com.yll.dao.AccountDao.findByUid", fetchType=FetchType.LAZY))})
public List<User> findAll();

编写AccountDao方法

/**
 * 根据用户id查询用户的所有账户信息
 * @param uid
 * @return
 */
@Select("select * from account where uid = #{uid }")
public List<Account> findByUid(int uid);
编写测试代码
@Test
public void findAll() {
    List<User> users = userDao.findAll();
    for (User user : users) {
        System.out.println("用户信息:"+user);
        //System.out.println("账户信息:"+user.getAccounts());
        System.out.println("---------------------------");
    }
}

5. 基于注解的Mybatis二级缓存

开启二级缓存

在SqlMapConfig中开启二级缓存支持

配置二级缓存
//mybatis基于注解方式实现配置二级缓存
@CacheNamespace(blocking=true)
public interface AccountDao {
  ......
}

(十一)综合案例

三层架构:软件设计架构

1.界面层(表示层):用户看的得界面,用户可以通过界面上的组件和服务器进行交互

2.业务逻辑层:处理业务逻辑的。

3.数据访问层:操作数据存储文件

1637827126746

案例:用户信息列表展示

1.简单功能

  1. 列表查询
  2. 登录
  3. 添加
  4. 删除
  5. 修改

2.复杂功能

1.删除选中

  1. 分页查询
  2. 复杂条件查询

1.开发过程

1.需求:用户信息的增删改查操作

2.设计:

  1. 技术选型:Servlet+jsp+mysql+JDBCTempleat+Duird+BeanUtils+Tomcat
  2. 数据库设计:

3.开发:

  1. 环境搭建

    1. 创建数据库环境
    2. 创建项目,导入需要的jar包
  2. 编码

4.测试

5.部署运维


2.环境搭建

创建数据库环境

CREATE DATABASE mydb;   #创建数据库
USE mydb;               #使用数据库

#创建表
CREATE TABLE USER(
	id INT PRIMARY KEY auto_increment,
	name VARCHAR(20) NOT NULL,
	gender VARCHAR(5),
	age INT,
	address VARCHAR(32),
	qq VARCHAR(20),
	email VARCHAR(50)
);

insert  into `user`(`id`,`name`,`gender`,`age`,`address`,`qq`,`email`) values (1,'冯宝宝','男',23,'湖北','1695054828','fbb@qq.com');

过程目录:

1637830211913


1.简单功能

代码实现

1.查询所有
1.创建实体类
public class User {
    private Integer id;
    private String name;
    private String gender;
    private Integer age;
    private String address;
    private String qq;
    private String email;

    public User() {
    }

    public User(Integer id, String name, String gender, Integer age, String address, String qq, String email) {
        this.id = id;
        this.name = name;
        this.gender = gender;
        this.age = age;
        this.address = address;
        this.qq = qq;
        this.email = email;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getQq() {
        return qq;
    }

    public void setQq(String qq) {
        this.qq = qq;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                ", age=" + age +
                ", address='" + address + '\'' +
                ", qq='" + qq + '\'' +
                ", email='" + email + '\'' +
                '}';
    }
}
2.导入工具类JDBCUtils
public class JDBCUtils {
    private static DataSource dataSource;
    static {
        Properties pro = new Properties();
        InputStream in = JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties");
        try {
            pro.load(in);
            dataSource = DruidDataSourceFactory.createDataSource(pro);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }

    public static DataSource getDataSource(){
        return dataSource;
    }
}

3.编写web层
/**
 * 查询所有用户信息
 */
@WebServlet("/userListServlet")
public class UserListServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.调用UserService完成查询
        UserService service = new UserServiceImpl();
        List<User> users = service.findAll();
        //2.将list存入request域
        request.setAttribute("users",users);
        //3.转发到list.jsp
        request.getRequestDispatcher("/list.jsp").forward(request,response);

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

4.编写service层
//用户管理的业务接口
public interface UserService {
    /**
     * 查询所有用户信息
     * @return
     */
    public List<User> findAll();
}

impl实现类

//实现类
public class UserServiceImpl implements UserService {
    private UserDao dao = new UserDaoImpl();

    /**
     * 查询所有用户信息
     *
     * @return
     */
    @Override
    public List<User> findAll() {
        //调用Dao完成查询
        return dao.findAll();
    }
}
5.编写dao层
//用户数据操作的DAO
public interface UserDao {
    public List<User> findAll();
}
public class UserDaoImpl implements UserDao {
 	//创建JDBCTemplate对象
    private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());

    @Override
    public List<User> findAll() {
        //使用JDBC操作数据库
        String sql = "select * from user";
        List<User> users = template.query(sql, new BeanPropertyRowMapper<User>(User.class));
        //将结果返回
        return users;
    }
}
6.编写页面list.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<!-- 网页使用的语言 -->
<html lang="zh-CN">
<head>
    <!-- 指定字符集 -->
    <meta charset="utf-8">
    <!-- 使用Edge最新的浏览器的渲染方式 -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!-- viewport视口:网页可以根据设置的宽度自动进行适配,在浏览器的内部虚拟一个容器,容器的宽度与设备的宽度相同。
    width: 默认宽度与设备的宽度相同
    initial-scale: 初始的缩放比,为1:1 -->
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>用户信息管理系统</title>

    <!-- 1. 导入CSS的全局样式 -->
    <link href="css/bootstrap.min.css" rel="stylesheet">
    <!-- 2. jQuery导入,建议使用1.9以上的版本 -->
    <script src="js/jquery-2.1.0.min.js"></script>
    <!-- 3. 导入bootstrap的js文件 -->
    <script src="js/bootstrap.min.js"></script>
    <style type="text/css">
        td, th {
            text-align: center;
        }
    </style>
</head>
<body>
<div class="container">
    <h3 style="text-align: center">用户信息列表</h3>
    <table border="1" class="table table-bordered table-hover">
        <tr class="success">
            <th>编号</th>
            <th>姓名</th>
            <th>性别</th>
            <th>年龄</th>
            <th>籍贯</th>
            <th>技能</th>
            <th>邮箱</th>
            <th>操作</th>
        </tr>
        <c:forEach items="${users}" var="user" varStatus="s">
            <tr>
                <td>${s.count}</td>
                <td>${user.name}</td>
                <td>${user.gender}</td>
                <td>${user.age}</td>
                <td>${user.address}</td>
                <td>${user.qq}</td>
                <td>${user.email}</td>
                <td><a class="btn btn-default btn-sm" href="update.html">修改</a>&nbsp;<a class="btn btn-default btn-sm" href="">删除</a></td>
            </tr>
        </c:forEach>

        <tr>
            <td colspan="8" align="center"><a class="btn btn-primary" href="add.html">添加联系人</a></td>
        </tr>
    </table>
</div>
</body>
</html>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GpuFM5k6-1649528615233)(https://gitee.com/shuaiqiguoguo/cloudimage/raw/master/img/202204100217483.png)]


2.改造页面

list.jsp

第一步

1637896808718

第二步

https://v3.bootcss.com/css/#forms 官网复制表单

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v2zArYhh-1649528615234)(https://gitee.com/shuaiqiguoguo/cloudimage/raw/master/img/202204100217485.png)]

1637896912822

第三步

官网复制分页

1637896944850

1637896979860

示例代码:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<!-- 网页使用的语言 -->
<html lang="zh-CN">
<head>
    <!-- 指定字符集 -->
    <meta charset="utf-8">
    <!-- 使用Edge最新的浏览器的渲染方式 -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!-- viewport视口:网页可以根据设置的宽度自动进行适配,在浏览器的内部虚拟一个容器,容器的宽度与设备的宽度相同。
    width: 默认宽度与设备的宽度相同
    initial-scale: 初始的缩放比,为1:1 -->
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>用户信息管理系统</title>

    <!-- 1. 导入CSS的全局样式 -->
    <link href="css/bootstrap.min.css" rel="stylesheet">
    <!-- 2. jQuery导入,建议使用1.9以上的版本 -->
    <script src="js/jquery-2.1.0.min.js"></script>
    <!-- 3. 导入bootstrap的js文件 -->
    <script src="js/bootstrap.min.js"></script>
    <style type="text/css">
        td, th {
            text-align: center;
        }
    </style>
</head>
<body>
<div class="container">
    <h3 style="text-align: center">用户信息列表</h3>
    <div style="float: left">
        <form class="form-inline">
            <div class="form-group">
                <label for="exampleInputName2">姓名</label>
                <input type="text" class="form-control" id="exampleInputName2">
            </div>
            <div class="form-group">
                <label for="exampleInputName3">籍贯</label>
                <input type="text" class="form-control" id="exampleInputName3">
            </div>
            <div class="form-group">
                <label for="exampleInputEmail2">邮箱</label>
                <input type="email" class="form-control" id="exampleInputEmail2">
            </div>
            <button type="submit" class="btn btn-default">查询</button>
        </form>
    </div>

    <div style="float: right;margin: 5px">
        <a class="btn btn-primary" href="add.html">添加联系人</a>
        <a class="btn btn-primary" href="add.html">刷新选中</a>
    </div>

    <table border="1" class="table table-bordered table-hover">
        <tr class="success">
            <th><input type="checkbox"></th>
            <th>编号</th>
            <th>姓名</th>
            <th>性别</th>
            <th>年龄</th>
            <th>籍贯</th>
            <th>技能</th>
            <th>邮箱</th>
            <th>操作</th>
        </tr>
        <c:forEach items="${users}" var="user" varStatus="s">
            <tr>
                <td><input type="checkbox"></td>
                <td>${s.count}</td>
                <td>${user.name}</td>
                <td>${user.gender}</td>
                <td>${user.age}</td>
                <td>${user.address}</td>
                <td>${user.qq}</td>
                <td>${user.email}</td>
                <td><a class="btn btn-default btn-sm" href="update.html">修改</a>&nbsp;<a class="btn btn-default btn-sm" href="">删除</a></td>
            </tr>
        </c:forEach>
    </table>
    <%--分页--%>
    <div>
    <nav aria-label="Page navigation">
        <ul class="pagination">
            <li>
                <a href="#" aria-label="Previous">
                    <span aria-hidden="true">&laquo;</span>
                </a>
            </li>
            <li><a href="#">1</a></li>
            <li><a href="#">2</a></li>
            <li><a href="#">3</a></li>
            <li><a href="#">4</a></li>
            <li><a href="#">5</a></li>
            <li>
                <a href="#" aria-label="Next">
                    <span aria-hidden="true">&raquo;</span>
                </a>
            </li>
            <span style="font-size: 24px;margin-left: 5px">
                共16条记录,共4页
            </span>
        </ul>
    </nav>
    </div>
</div>
</body>
</html>



3.用户登录
1.改写实体类
public class User {
    private Integer id;
    private String name;
    private String gender;
    private Integer age;
    private String address;
    private String qq;
    private String email;
    private String username;
    private String password;


    public User() {
    }

    public User(Integer id, String name, String gender, Integer age, String address, String qq, String email, String username, String password) {
        this.id = id;
        this.name = name;
        this.gender = gender;
        this.age = age;
        this.address = address;
        this.qq = qq;
        this.email = email;
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

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

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getQq() {
        return qq;
    }

    public void setQq(String qq) {
        this.qq = qq;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                ", age=" + age +
                ", address='" + address + '\'' +
                ", qq='" + qq + '\'' +
                ", email='" + email + '\'' +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}
2.编写web层
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.设置编码
        request.setCharacterEncoding("utf-8");
        //2.获取数据
        //2.1获取用户填写验证码
        String verifycode = request.getParameter("verifycode");
        //获取所有参数
        Map<String, String[]> map = request.getParameterMap();

        //3.验证码校验
        HttpSession session = request.getSession();
        String checkcode_server = (String) session.getAttribute("CHECKCODE_SERVER");
        session.removeAttribute("CHECKCODE_SERVER");  //确保验证码一次性
        //取反
        if (!checkcode_server.equalsIgnoreCase(verifycode)){
            //验证码不正确
            //提示信息
            request.setAttribute("login_msg","验证码错误");
            //跳转登录页面
            request.getRequestDispatcher("/login.jsp").forward(request,response);
            return;
        }

        //4.封装User对象
        User user = new User();
        try {
            BeanUtils.populate(user,map);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        //5.调用Service查询
        UserService service = new UserServiceImpl();
        User loginUser = service.login(user);
        //6.判断是否登录成功
        if (loginUser != null){
            //登录成功
            //将用户存到session
            session.setAttribute("user",loginUser);
            //跳转页面
            response.sendRedirect(request.getContextPath()+"/index.jsp");
        }else {
            //登录失败
            //提示信息
            request.setAttribute("login_msg","用户名或密码错误!");
            //跳转登录页面
            request.getRequestDispatcher("/login.jsp").forward(request,response);
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

3.编写service层
//用户管理的业务接口
public interface UserService {
    /**
     * 查询所有用户信息
     * @return
     */
    public List<User> findAll();

    /**
     * 登录
     * @param user
     * @return
     */
    User login(User user);
}
//实现类
public class UserServiceImpl implements UserService {
    private UserDao dao = new UserDaoImpl();

    /**
     * 查询所有用户信息
     *
     * @return
     */
    @Override
    public List<User> findAll() {
        //调用Dao完成查询
        return dao.findAll();
    }

    /**
     * 登录
     * @param user
     * @return
     */
    @Override
    public User login(User user) {
        return dao.findUserByUsernameAndPassword(user.getUsername(),user.getPassword());
    }
}
4.编写dao层
//用户数据操作的DAO
public interface UserDao {
    public List<User> findAll();

    public User findUserByUsernameAndPassword(String username,String password);
}
public class UserDaoImpl implements UserDao {

    private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
	//查询列表
    @Override
    public List<User> findAll() {
        //使用JDBC操作数据库
        String sql = "select * from user";
        List<User> users = template.query(sql, new BeanPropertyRowMapper<User>(User.class));
        return users;
    }

    //登录
    @Override
    public User findUserByUsernameAndPassword(String username, String password) {
        try {
            String sql = "select * from user where username = ? and password = ?";
            User user = template.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class), username, password);
            return user;
        } catch (DataAccessException e) {
            e.printStackTrace();
        }
        return null;
    }
}
5.编写页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>
    <title>管理员登录</title>

    <!-- 1. 导入CSS的全局样式 -->
    <link href="css/bootstrap.min.css" rel="stylesheet">
    <!-- 2. jQuery导入,建议使用1.9以上的版本 -->
    <script src="js/jquery-2.1.0.min.js"></script>
    <!-- 3. 导入bootstrap的js文件 -->
    <script src="js/bootstrap.min.js"></script>

    <script type="text/javascript">
        function refreshCode(){
            //1.获取验证码图片对象
            var vcode = document.getElementById("vcode");
            //2.设置src属性,加时间戳
            vcode.src = "${pageContext.request.contextPath}/checkCodeServlet?+time="+new Date().getTime();
        }
    </script>

</head>
<body>
<div class="container" style="width: 400px;">
    <h3 style="text-align: center;">管理员登录</h3>
    <form action="${pageContext.request.contextPath}/loginServlet" method="post">
        <div class="form-group">
            <label for="user">用户名:</label>
            <input type="text" name="username" class="form-control" id="user" placeholder="请输入用户名"/>
        </div>

        <div class="form-group">
            <label for="password">密码:</label>
            <input type="password" name="password" class="form-control" id="password" placeholder="请输入密码"/>
        </div>

        <div class="form-inline">
            <label for="vcode">验证码:</label>
            <input type="text" name="verifycode" class="form-control" id="verifycode" placeholder="请输入验证码" style="width: 120px;"/>
            <a href="javascript:refreshCode();"><img src="${pageContext.request.contextPath}/checkCodeServlet" title="看不清点击刷新" id="vcode"/></a>
        </div>
        <hr/>
        <div class="form-group" style="text-align: center;">
            <input class="btn btn btn-primary" type="submit" value="登录">
        </div>
    </form>

    <!-- 出错显示的信息框 -->
    <div class="alert alert-warning alert-dismissible" role="alert">
        <button type="button" class="close" data-dismiss="alert" >
            <span>&times;</span></button>
        <strong>${login_msg}</strong>
    </div>
</div>
</body>
</html>

6 index.jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8"/>
  <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
  <meta name="viewport" content="width=device-width, initial-scale=1"/>
  <title>首页</title>

  <!-- 1. 导入CSS的全局样式 -->
  <link href="css/bootstrap.min.css" rel="stylesheet">
  <!-- 2. jQuery导入,建议使用1.9以上的版本 -->
  <script src="js/jquery-2.1.0.min.js"></script>
  <!-- 3. 导入bootstrap的js文件 -->
  <script src="js/bootstrap.min.js"></script>
  <script type="text/javascript">
  </script>
</head>
<body>

<div align="center">
  <a href="${pageContext.request.contextPath}/userListServlet" style="text-decoration:none;font-size:33px">${user.name},欢迎你查询所有用户信息
  </a>
</div>
</body>
</html>

4.添加操作
1.流程图

1637917693277

2 add.jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!-- HTML5文档-->
<!DOCTYPE html>
<!-- 网页使用的语言 -->
<html lang="zh-CN">
<head>
    <!-- 指定字符集 -->
    <meta charset="utf-8">
    <!-- 使用Edge最新的浏览器的渲染方式 -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!-- viewport视口:网页可以根据设置的宽度自动进行适配,在浏览器的内部虚拟一个容器,容器的宽度与设备的宽度相同。
    width: 默认宽度与设备的宽度相同
    initial-scale: 初始的缩放比,为1:1 -->
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>添加用户</title>

    <!-- 1. 导入CSS的全局样式 -->
    <link href="css/bootstrap.min.css" rel="stylesheet">
    <!-- 2. jQuery导入,建议使用1.9以上的版本 -->
    <script src="js/jquery-2.1.0.min.js"></script>
    <!-- 3. 导入bootstrap的js文件 -->
    <script src="js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
    <center><h3>添加联系人页面</h3></center>
    <form action="${pageContext.request.contextPath}/addUserServlet" method="post">
        <div class="form-group">
            <label for="name">姓名:</label>
            <input type="text" class="form-control" id="name" name="name" placeholder="请输入姓名">
        </div>

        <div class="form-group">
            <label>性别:</label>
            <input type="radio" name="gender" value="男" checked="checked"/>男
            <input type="radio" name="gender" value="女"/>女
        </div>

        <div class="form-group">
            <label for="age">年龄:</label>
            <input type="text" class="form-control" id="age" name="age" placeholder="请输入年龄">
        </div>

        <div class="form-group">
            <label for="address">籍贯:</label>
            <select name="address" class="form-control" id="jiguan">
                <option value="广东">广东</option>
                <option value="广西">广西</option>
                <option value="湖南">湖南</option>
            </select>
        </div>

        <div class="form-group">
            <label for="qq">qq:</label>
            <input type="text" class="form-control" name="qq" placeholder="请输入技能"/>
        </div>

        <div class="form-group">
            <label for="email">Email:</label>
            <input type="text" class="form-control" name="email" placeholder="请输入邮箱地址"/>
        </div>

        <div class="form-group" style="text-align: center">
            <input class="btn btn-primary" type="submit" value="提交" />
            <input class="btn btn-default" type="reset" value="重置" />
            <input class="btn btn-default" type="button" value="返回" />
        </div>
    </form>
</div>
</body>
</html>
3 web层
@WebServlet("/addUserServlet")
public class AddUserServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.设置编码
        request.setCharacterEncoding("utf-8");
        //2.获取参数
        Map<String, String[]> map = request.getParameterMap();
        //3.封装对象
        User user = new User();
        try {
            BeanUtils.populate(user,map);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        //4.调用Service保存
        UserService service = new UserServiceImpl();
        service.addUser(user);

        //5.跳转到userListServlet
        response.sendRedirect(request.getContextPath()+"/userListServlet");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}
4 service层
//用户管理的业务接口
public interface UserService {
    /**
     * 保存User
     * @param user
     */
    void addUser(User user);
}
//实现类
public class UserServiceImpl implements UserService {
    private UserDao dao = new UserDaoImpl();
    /**
     * 保存User
     *
     * @param user
     */
    @Override
    public void addUser(User user) {
        dao.add(user);
    }
}
5 dao层
//用户数据操作的DAO
public interface UserDao {
    void add(User user);
}
public class UserDaoImpl implements UserDao {
    private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
    //添加
    @Override
    public void add(User user) {
       //1.定义sql
       String sql = "insert into user values(null,?,?,?,?,?,?,null,null)";
       //2.实现sql
        template.update(sql, user.getName(), user.getGender(), user.getAge(), user.getAddress(), user.getQq(), user.getEmail());
    }
}

5.删除操作
1.流程图

1637925059238

2 list.jsp页面
    <script>
        function deleteUser(id) {
            //用户安全提示
            if (confirm("你确定要删除吗?")){
                location.href = "${pageContext.request.contextPath}/delUserServlet?id="+id;
            }

        }
    </script>
    
     <a class="btn btn-default btn-sm" href="javascript:deleteUser(${user.id});">删除</a>
3 web层
@WebServlet("/delUserServlet")
public class DelUserServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.获取id
        String id = request.getParameter("id");
        //2.调用service删除
        UserService service = new UserServiceImpl();
        service.deleteUser(id);

        //3.跳转到查询所有的Servlet
        response.sendRedirect(request.getContextPath()+"/userListServlet");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

4 service层
/**
     * 根据ID删除user
     * @param id
     */
    void deleteUser(String id);
 /**
     * 根据ID删除user
     *
     * @param id
     */
    @Override
    public void deleteUser(String id) {
        dao.delete(Integer.parseInt(id));
    }
5 dao层
void delete(int id);
  //删除
    @Override
    public void delete(int id) {
        //1.定义sql
        String sql = "delete from user where id = ?";
        //2.执行sql
        template.update(sql,id);
    }

6 修改操作
1.流程图

1637998234211

2. list.jsp页面
<a class="btn btn-default btn-sm" href="${pageContext.request.contextPath}/findUserServlet?id=${user.id}">修改</a>&nbsp;
3. web层
@WebServlet("/findUserServlet")
public class FindUserServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.获取id
        String id = request.getParameter("id");
        //2.调用Service查询
        UserService service = new UserServiceImpl();
        User user = service.findUserById(id);

        //3.将user存入request
        request.setAttribute("user",user);
        //4.转发到update.jsp
        request.getRequestDispatcher("/update.jsp").forward(request,response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, reponse);
    }
}
4. service层
    /**
     * 根据id查询
     * @param id
     * @return
     */
    User findUserById(String id);
  /**
     * 根据id查询
     *
     * @param id
     * @return
     */
    @Override
    public User findUserById(String id) {
        return dao.findById(Integer.parseInt(id));
    }
5. dao层
    User findById(int id);
//根据id查询
    @Override
    public User findById(int id) {
        String sql = "select * from user where id = ?";
        return template.queryForObject(sql,new BeanPropertyRowMapper<User>(User.class),id);
    }
6. 页面回显数据和跟新数据

update.jsp页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<!-- 网页使用的语言 -->
<html lang="zh-CN">
    <head>
        <!-- 指定字符集 -->
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>修改用户</title>

        <link href="css/bootstrap.min.css" rel="stylesheet">
        <script src="js/jquery-2.1.0.min.js"></script>
        <script src="js/bootstrap.min.js"></script>
        
    </head>
    <body>
        <div class="container" style="width: 400px;">
        <h3 style="text-align: center;">修改联系人</h3>
        <form action="${pageContext.request.contextPath}/updateUserServlet" method="post">
            <%--隐藏域   提交id--%>
            <input type="hidden" name="id" value="${user.id}">

          <div class="form-group">
            <label for="name">姓名:</label>
            <input type="text" class="form-control" id="name" name="name" value="${user.name}" readonly="readonly" placeholder="请输入姓名" />
          </div>

          <div class="form-group">
            <label>性别:</label>
              <c:if test="${user.gender == '男'}">
                  <input type="radio" name="gender" value="男" checked />男
                  <input type="radio" name="gender" value="女"  />女
              </c:if>

              <c:if test="${user.gender == '女'}">
                  <input type="radio" name="gender" value="男"  />男
                  <input type="radio" name="gender" value="女" checked />女
              </c:if>

          </div>

          <div class="form-group">
            <label for="age">年龄:</label>
            <input type="text" class="form-control" id="age"  name="age" value="${user.age}" placeholder="请输入年龄" />
          </div>

          <div class="form-group">
            <label for="address">籍贯:</label>
             <select name="address" class="form-control" >
                 <c:if test="${user.address == '广东'}">
                     <option value="广东" selected>广东</option>
                     <option value="湖北">湖北</option>
                     <option value="湖南">湖南</option>
                 </c:if>

                 <c:if test="${user.address == '湖北'}">
                     <option value="广东" >广东</option>
                     <option value="湖北" selected>湖北</option>
                     <option value="湖南">湖南</option>
                 </c:if>

                 <c:if test="${user.address == '湖南'}">
                     <option value="广东" >广东</option>
                     <option value="湖北" >湖北</option>
                     <option value="湖南" selected>湖南</option>
                 </c:if>

            </select>
          </div>

          <div class="form-group">
            <label for="qq">技能:</label>
            <input type="text" class="form-control" name="qq" value="${user.qq}" placeholder="请输入技能"/>
          </div>

          <div class="form-group">
            <label for="email">Email:</label>
            <input type="text" class="form-control" name="email" value="${user.email}" placeholder="请输入邮箱地址"/>
          </div>

             <div class="form-group" style="text-align: center">
                <input class="btn btn-primary" type="submit" value="提交" />
                <input class="btn btn-default" type="reset" value="重置" />
                <input class="btn btn-default" type="button" value="返回"/>
             </div>
        </form>
        </div>
    </body>
</html>
7. web层
@WebServlet("/updateUserServlet")
public class UpdateUserServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.设置编码
        request.setCharacterEncoding("utf-8");
        //2.封装map
        Map<String, String[]> map = request.getParameterMap();
        //3.封装对象
        User user = new User();
        try {
            BeanUtils.populate(user,map);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        //4.调用Service
        UserService service = new UserServiceImpl();
        service.updateUser(user);

        //5.跳转到查询所有Servlet
        response.sendRedirect(request.getContextPath()+"/userListServlet");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}
8. service层
   /**
     * 修改用户信息
     * @param user
     */
    void updateUser(User user);
 /**
     * 修改用户信息
     *
     * @param user
     */
    @Override
    public void updateUser(User user) {
        dao.update(user);
    }
9. dao层
void update(User user);
//修改
    @Override
    public void update(User user) {
        String sql = "update user set name = ?,gender = ?,age = ?,address = ?,qq = 						?,email = ? where id = ?";
        template.update(sql,user.getName(), user.getGender(), user.getAge(), 				user.getAddress(), user.getQq(), user.getEmail(),user.getId());
    }

2. 复杂功能

代码实现

1.删除选中
1. 流程图

1637999654941

2.改造list页面

获取删除选中的id编号

1638000525587

1638000564106

list.jsp页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<!-- 网页使用的语言 -->
<html lang="zh-CN">
<head>
    <!-- 指定字符集 -->
    <meta charset="utf-8">
    <!-- 使用Edge最新的浏览器的渲染方式 -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!-- viewport视口:网页可以根据设置的宽度自动进行适配,在浏览器的内部虚拟一个容器,容器的宽度与设备的宽度相同。
    width: 默认宽度与设备的宽度相同
    initial-scale: 初始的缩放比,为1:1 -->
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>用户信息管理系统</title>

    <!-- 1. 导入CSS的全局样式 -->
    <link href="css/bootstrap.min.css" rel="stylesheet">
    <!-- 2. jQuery导入,建议使用1.9以上的版本 -->
    <script src="js/jquery-2.1.0.min.js"></script>
    <!-- 3. 导入bootstrap的js文件 -->
    <script src="js/bootstrap.min.js"></script>
    <style type="text/css">
        td, th {
            text-align: center;
        }
    </style>

    <script>
        function deleteUser(id) {
            //用户安全提示
            if (confirm("你确定要删除吗?")){
                location.href = "${pageContext.request.contextPath}/delUserServlet?id="+id;
            }

        }

        window.onload = function () {
            //给删除选中按钮添加单击事件
            document.getElementById("delSelected").onclick = function () {
                //提交表单
                document.getElementById("form").submit();
            }
        }
    </script>


</head>
<body>
<div class="container">
    <h3 style="text-align: center">用户信息列表</h3>
    <div style="float: left">
        <form class="form-inline">
            <div class="form-group">
                <label for="exampleInputName2">姓名</label>
                <input type="text" class="form-control" id="exampleInputName2">
            </div>
            <div class="form-group">
                <label for="exampleInputName3">籍贯</label>
                <input type="text" class="form-control" id="exampleInputName3">
            </div>
            <div class="form-group">
                <label for="exampleInputEmail2">邮箱</label>
                <input type="email" class="form-control" id="exampleInputEmail2">
            </div>
            <button type="submit" class="btn btn-default">查询</button>
        </form>
    </div>

    <div style="float: right;margin: 5px">
        <a class="btn btn-primary" href="${pageContext.request.contextPath}/add.jsp">添加联系人</a>
        <a class="btn btn-primary" href="javascript:void(0);" id="delSelected">删除选中</a>
    </div>


    <form  id="form" action="${pageContext.request.contextPath}/delSelectedServlet" method="post">
    <table border="1" class="table table-bordered table-hover">
        <tr class="success">
            <th><input type="checkbox"></th>
            <th>编号</th>
            <th>姓名</th>
            <th>性别</th>
            <th>年龄</th>
            <th>籍贯</th>
            <th>技能</th>
            <th>邮箱</th>
            <th>操作</th>
        </tr>
        <c:forEach items="${users}" var="user" varStatus="s">
            <tr>
                <td><input type="checkbox" name="uid" value="${user.id}"></td>
                <td>${s.count}</td>
                <td>${user.name}</td>
                <td>${user.gender}</td>
                <td>${user.age}</td>
                <td>${user.address}</td>
                <td>${user.qq}</td>
                <td>${user.email}</td>
                <td>
                    <a class="btn btn-default btn-sm" href="${pageContext.request.contextPath}/findUserServlet?id=${user.id}">修改</a>&nbsp;
                    <a class="btn btn-default btn-sm" href="javascript:deleteUser(${user.id});">删除</a>
                </td>
            </tr>
        </c:forEach>
    </table>
    </form>

    <%--分页--%>
    <div>
    <nav aria-label="Page navigation">
        <ul class="pagination">
            <li>
                <a href="#" aria-label="Previous">
                    <span aria-hidden="true">&laquo;</span>
                </a>
            </li>
            <li><a href="#">1</a></li>
            <li><a href="#">2</a></li>
            <li><a href="#">3</a></li>
            <li><a href="#">4</a></li>
            <li><a href="#">5</a></li>
            <li>
                <a href="#" aria-label="Next">
                    <span aria-hidden="true">&raquo;</span>
                </a>
            </li>
            <span style="font-size: 24px;margin-left: 5px">
                共16条记录,共4页
            </span>
        </ul>
    </nav>
    </div>
</div>
</body>
</html>


3.web层
@WebServlet("/delSelectedServlet")
public class DelSelectedServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.获取所有id
        String[] ids = request.getParameterValues("uid");
        //2.调用service删除
        UserService service = new UserServiceImpl();
        service.delSelectedUser(ids);
        //3.跳转查询所有Servlet
        response.sendRedirect(request.getContextPath()+"/userListServlet");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}
4. service层
 /**
     * 批量删除用户
     * @param ids
     */
    void delSelectedUser(String[] ids);
  /**
     * 批量删除用户
     *
     * @param ids
     */
    @Override
    public void delSelectedUser(String[] ids) {
        if (ids != null && ids.length >0){
            //1.遍历数组
            for (String id : ids) {
                //2.调用dao删除
                dao.delete(Integer.parseInt(id));
            }
        }
    }

dao层有delete方法 直接调用就行了。

5.list.jsp页面全选功能

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-av1vNrsX-1649528615236)(https://gitee.com/shuaiqiguoguo/cloudimage/raw/master/img/202204100217495.png)]

js

1638003614422

删除提示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ddXd5hAK-1649528615237)(https://gitee.com/shuaiqiguoguo/cloudimage/raw/master/img/202204100217497.png)]

没有选中删除 会报错误页面 解决方案

1638004240799

list.jsp页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<!-- 网页使用的语言 -->
<html lang="zh-CN">
<head>
    <!-- 指定字符集 -->
    <meta charset="utf-8">
    <!-- 使用Edge最新的浏览器的渲染方式 -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!-- viewport视口:网页可以根据设置的宽度自动进行适配,在浏览器的内部虚拟一个容器,容器的宽度与设备的宽度相同。
    width: 默认宽度与设备的宽度相同
    initial-scale: 初始的缩放比,为1:1 -->
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>用户信息管理系统</title>

    <!-- 1. 导入CSS的全局样式 -->
    <link href="css/bootstrap.min.css" rel="stylesheet">
    <!-- 2. jQuery导入,建议使用1.9以上的版本 -->
    <script src="js/jquery-2.1.0.min.js"></script>
    <!-- 3. 导入bootstrap的js文件 -->
    <script src="js/bootstrap.min.js"></script>
    <style type="text/css">
        td, th {
            text-align: center;
        }
    </style>

    <script>
        function deleteUser(id) {
            //用户安全提示
            if (confirm("你确定要删除吗?")){
                //访问路劲
                location.href = "${pageContext.request.contextPath}/delUserServlet?id="+id;
            }

        }

        window.onload = function () {
            //给删除选中按钮添加单击事件
            document.getElementById("delSelected").onclick = function () {
              if (confirm("你确定要删除选中条目吗?")){

                  var flag = false;
                  //判断是否有选中条目
                  var cbs = document.getElementsByName("uid");
                  for (var i = 0; i < cbs.length; i++) {
                      if ( cbs[i].checked){
                          //有一个条目选中了
                          flag = true;
                          break;
                      }
                  }

                  if (flag){  //有条目选中了
                      //提交表单
                      document.getElementById("form").submit();
                  }

              }
            }


            //1.获取第一个firstCb
            document.getElementById("firstCb").onclick = function () {
                //2.获取下边列表中所有的cb
                var cbs = document.getElementsByName("uid");

                //3.遍历
                for (var i = 0; i < cbs.length; i++) {
                    //4.设置这些cbs[i]的checked状态 = firstCb.checked
                    cbs[i].checked = this.checked;
                }
            }
        }
    </script>


</head>
<body>
<div class="container">
    <h3 style="text-align: center">用户信息列表</h3>
    <div style="float: left">
        <form class="form-inline">
            <div class="form-group">
                <label for="exampleInputName2">姓名</label>
                <input type="text" class="form-control" id="exampleInputName2">
            </div>
            <div class="form-group">
                <label for="exampleInputName3">籍贯</label>
                <input type="text" class="form-control" id="exampleInputName3">
            </div>
            <div class="form-group">
                <label for="exampleInputEmail2">邮箱</label>
                <input type="email" class="form-control" id="exampleInputEmail2">
            </div>
            <button type="submit" class="btn btn-default">查询</button>
        </form>
    </div>

    <div style="float: right;margin: 5px">
        <a class="btn btn-primary" href="${pageContext.request.contextPath}/add.jsp">添加联系人</a>
        <a class="btn btn-primary" href="javascript:void(0);" id="delSelected">删除选中</a>
    </div>


    <form  id="form" action="${pageContext.request.contextPath}/delSelectedServlet" method="post">
    <table border="1" class="table table-bordered table-hover">
        <tr class="success">
            <th><input type="checkbox" id="firstCb"></th>
            <th>编号</th>
            <th>姓名</th>
            <th>性别</th>
            <th>年龄</th>
            <th>籍贯</th>
            <th>技能</th>
            <th>邮箱</th>
            <th>操作</th>
        </tr>
        <c:forEach items="${users}" var="user" varStatus="s">
            <tr>
                <td><input type="checkbox" name="uid" value="${user.id}"></td>
                <td>${s.count}</td>
                <td>${user.name}</td>
                <td>${user.gender}</td>
                <td>${user.age}</td>
                <td>${user.address}</td>
                <td>${user.qq}</td>
                <td>${user.email}</td>
                <td>
                    <a class="btn btn-default btn-sm" href="${pageContext.request.contextPath}/findUserServlet?id=${user.id}">修改</a>&nbsp;
                    <a class="btn btn-default btn-sm" href="javascript:deleteUser(${user.id});">删除</a>
                </td>
            </tr>
        </c:forEach>
    </table>
    </form>

    <%--分页--%>
    <div>
    <nav aria-label="Page navigation">
        <ul class="pagination">
            <li>
                <a href="#" aria-label="Previous">
                    <span aria-hidden="true">&laquo;</span>
                </a>
            </li>
            <li><a href="#">1</a></li>
            <li><a href="#">2</a></li>
            <li><a href="#">3</a></li>
            <li><a href="#">4</a></li>
            <li><a href="#">5</a></li>
            <li>
                <a href="#" aria-label="Next">
                    <span aria-hidden="true">&raquo;</span>
                </a>
            </li>
            <span style="font-size: 24px;margin-left: 5px">
                共16条记录,共4页
            </span>
        </ul>
    </nav>
    </div>
</div>
</body>
</html>

2.分页查询
1.分页查询分析

1638085850310

2.分页查询功能流程图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mx262CMj-1649528615237)(https://gitee.com/shuaiqiguoguo/cloudimage/raw/master/img/202204100217501.png)]

3.web层
@WebServlet("/findUserByPageServlet")
public class FindUserByPageServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.获取参数
        String currentPage = request.getParameter("currentPage");  //当前页码
        String rows = request.getParameter("rows");  //每页显示条数
        
        //2.调用service查询
        UserService service = new UserServiceImpl();
        PageBean<User> pb = service.findUserByPage(currentPage,rows);
        
        //3.将pageBean存入request
        request.setAttribute("pb",pb);
        //4.转发到list.jsp
        request.getRequestDispatcher("/list.jsp").forward(request,response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}
4.service层
/**
     * 分页查询
     * @param currentPage
     * @param rows
     * @return
     */
    PageBean<User> findUserByPage(String currentPage, String rows);
 /**
     * 分页查询
     *
     * @param _currentPage
     * @param _rows
     * @return
     */
    @Override
    public PageBean<User> findUserByPage(String _currentPage, String _rows) {
        int currentPage = Integer.parseInt(_currentPage);
        int rows = Integer.parseInt(_rows);

        //1.创建空的PageBean对象
        PageBean<User> pb = new PageBean<User>();
        //2.设置参数
        pb.setCurrentPage(currentPage);
        pb.setRows(rows);

        //3.调用dao查询总记录数
        int totalCount = dao.findTotalCount();
        pb.setTotalCount(totalCount);
        //4.调用dao查询List集合
        //计算开始的记录索引
        int start = (currentPage - 1) * rows;
        List<User> list = dao.findByPage(start,rows);
        pb.setList(list);

        //5.计算总页码
        int totalPage  = (totalCount % rows) == 0 ? totalCount / rows : (totalCount / rows) + 1;
        pb.setTotalPage(totalPage);
        return pb;
    }
5.dao层
 //查询总记录数
    int findTotalCount();

    //分页查询每页记录
    List<User> findByPage(int start, int rows);
   //查询总记录数
    @Override
    public int findTotalCount() {
        String sql = "select count(*) from user";
        return template.queryForObject(sql,Integer.class);
    }

    //分页查询每页记录
    @Override
    public List<User> findByPage(int start, int rows) {
        String sql = "select * from user limit ? , ?";
        return template.query(sql,new BeanPropertyRowMapper<User>(User.class),start,rows);
    }
6.list.jsp页面

1638088143486

1638088184270

index.jsp页面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pomssR8G-1649528615238)(https://gitee.com/shuaiqiguoguo/cloudimage/raw/master/img/202204100217504.png)]

FindUserByPageServlet

1638088267955

分页显示选择状态

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o79HHxi0-1649528615239)(https://gitee.com/shuaiqiguoguo/cloudimage/raw/master/img/202204100217506.png)]

禁用和激活状态

1638097542331

上一页

1638097711122

1638098006561

下一页

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d1luP6Rt-1649528615240)(https://gitee.com/shuaiqiguoguo/cloudimage/raw/master/img/202204100217510.png)]

1638098042058

list.jsp页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<!-- 网页使用的语言 -->
<html lang="zh-CN">
<head>
    <!-- 指定字符集 -->
    <meta charset="utf-8">
    <!-- 使用Edge最新的浏览器的渲染方式 -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!-- viewport视口:网页可以根据设置的宽度自动进行适配,在浏览器的内部虚拟一个容器,容器的宽度与设备的宽度相同。
    width: 默认宽度与设备的宽度相同
    initial-scale: 初始的缩放比,为1:1 -->
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>用户信息管理系统</title>

    <!-- 1. 导入CSS的全局样式 -->
    <link href="css/bootstrap.min.css" rel="stylesheet">
    <!-- 2. jQuery导入,建议使用1.9以上的版本 -->
    <script src="js/jquery-2.1.0.min.js"></script>
    <!-- 3. 导入bootstrap的js文件 -->
    <script src="js/bootstrap.min.js"></script>
    <style type="text/css">
        td, th {
            text-align: center;
        }
    </style>

    <script>
        function deleteUser(id) {
            //用户安全提示
            if (confirm("你确定要删除吗?")){
                //访问路劲
                location.href = "${pageContext.request.contextPath}/delUserServlet?id="+id;
            }

        }

        window.onload = function () {
            //给删除选中按钮添加单击事件
            document.getElementById("delSelected").onclick = function () {
              if (confirm("你确定要删除选中条目吗?")){

                  var flag = false;
                  //判断是否有选中条目
                  var cbs = document.getElementsByName("uid");
                  for (var i = 0; i < cbs.length; i++) {
                      if ( cbs[i].checked){
                          //有一个条目选中了
                          flag = true;
                          break;
                      }
                  }

                  if (flag){  //有条目选中了
                      //提交表单
                      document.getElementById("form").submit();
                  }

              }
            }


            //1.获取第一个firstCb
            document.getElementById("firstCb").onclick = function () {
                //2.获取下边列表中所有的cb
                var cbs = document.getElementsByName("uid");

                //3.遍历
                for (var i = 0; i < cbs.length; i++) {
                    //4.设置这些cbs[i]的checked状态 = firstCb.checked
                    cbs[i].checked = this.checked;
                }
            }
        }
    </script>


</head>
<body>
<div class="container">
    <h3 style="text-align: center">用户信息列表</h3>
    <div style="float: left">
        <form class="form-inline">
            <div class="form-group">
                <label for="exampleInputName2">姓名</label>
                <input type="text" class="form-control" id="exampleInputName2">
            </div>
            <div class="form-group">
                <label for="exampleInputName3">籍贯</label>
                <input type="text" class="form-control" id="exampleInputName3">
            </div>
            <div class="form-group">
                <label for="exampleInputEmail2">邮箱</label>
                <input type="email" class="form-control" id="exampleInputEmail2">
            </div>
            <button type="submit" class="btn btn-default">查询</button>
        </form>
    </div>

    <div style="float: right;margin: 5px">
        <a class="btn btn-primary" href="${pageContext.request.contextPath}/add.jsp">添加联系人</a>
        <a class="btn btn-primary" href="javascript:void(0);" id="delSelected">删除选中</a>
    </div>


    <form  id="form" action="${pageContext.request.contextPath}/delSelectedServlet" method="post">
    <table border="1" class="table table-bordered table-hover">
        <tr class="success">
            <th><input type="checkbox" id="firstCb"></th>
            <th>编号</th>
            <th>姓名</th>
            <th>性别</th>
            <th>年龄</th>
            <th>籍贯</th>
            <th>技能</th>
            <th>邮箱</th>
            <th>操作</th>
        </tr>
        <c:forEach items="${pb.list}" var="user" varStatus="s">
            <tr>
                <td><input type="checkbox" name="uid" value="${user.id}"></td>
                <td>${s.count}</td>
                <td>${user.name}</td>
                <td>${user.gender}</td>
                <td>${user.age}</td>
                <td>${user.address}</td>
                <td>${user.qq}</td>
                <td>${user.email}</td>
                <td>
                    <a class="btn btn-default btn-sm" href="${pageContext.request.contextPath}/findUserServlet?id=${user.id}">修改</a>&nbsp;
                    <a class="btn btn-default btn-sm" href="javascript:deleteUser(${user.id});">删除</a>
                </td>
            </tr>
        </c:forEach>
    </table>
    </form>

    <%--分页--%>
    <div>
    <nav aria-label="Page navigation">
        <ul class="pagination">
            <c:if test="${pb.currentPage == 1}">
                <li class="disabled">
            </c:if>

            <c:if test="${pb.currentPage != 1}">
                <li>
            </c:if>

                <a href="${pageContext.request.contextPath}/findUserByPageServlet?currentPage=${pb.currentPage - 1}&rows=5" aria-label="Previous">
                    <span aria-hidden="true">&laquo;</span>
                </a>
            </li>


          <c:forEach begin="1" end="${pb.totalPage}" var="i">

              <c:if test="${pb.currentPage == i}">
                  <li class="active"><a href="${pageContext.request.contextPath}/findUserByPageServlet?currentPage=${i}&rows=5">${i}</a></li>
              </c:if>
              <c:if test="${pb.currentPage != i}">
                  <li><a href="${pageContext.request.contextPath}/findUserByPageServlet?currentPage=${i}&rows=5">${i}</a></li>
              </c:if>

          </c:forEach>


          <c:if test="${pb.currentPage == pb.totalPage}">
             <li class="disabled">
                 </c:if>

          <c:if test="${pb.currentPage != pb.totalPage}">
              <li>
          </c:if>

                <a href="${pageContext.request.contextPath}/findUserByPageServlet?currentPage=${pb.currentPage + 1}&rows=5" aria-label="Next">
                    <span aria-hidden="true">&raquo;</span>
                </a>
            </li>


            <span style="font-size: 24px;margin-left: 5px">
                共<span style="color: red">${pb.totalCount}</span>条记录,共<span style="color: red">${pb.totalPage}</span>页
            </span>
        </ul>
    </nav>
    </div>
</div>
</body>
</html>

3.复杂条件查询
1.流程图

1638098768542

1638098809749

1638253697181

2.web层

1638098933285

3.service层

1638099138904

实现类

1638099107019

dao层
  //查询总记录数
    int findTotalCount(Map<String, String[]> condition);

    //分页查询每页记录
    List<User> findByPage(int start, int rows, Map<String, String[]> condition);
 //查询总记录数
    @Override
    public int findTotalCount(Map<String, String[]> condition) {
        //1.定义模板初始化sql
        String sql = "select count(*) from user where 1 = 1 ";
        StringBuilder sb = new StringBuilder(sql);
        //2.遍历map
        Set<String> keySet = condition.keySet();
        //定义参数的集合
        List<Object> params = new ArrayList<Object>();

        for (String key : keySet) {

            //排除分页软件参数
            if ("currentPage".equals(key) || "rows".equals(key)){
                continue;
            }

            //获取value
            String value = condition.get(key)[0];  //获取的是数组,我们不考虑数组的情况   直接[0]获取一个值
            //判断value是否有值
            if (value != null && !"".equals(value)){
                //有值
                sb.append(" and "+key+" like ? ");
                params.add("%"+value+"%");  // ? 条件的值
            }
        }
        System.out.println(sb.toString());
        System.out.println(params);
        return template.queryForObject(sb.toString(),Integer.class,params.toArray());
    }

  //分页查询每页记录
    @Override
    public List<User> findByPage(int start, int rows, Map<String, String[]> condition) {
        String sql = "select * from user where 1 = 1 ";

        StringBuilder sb = new StringBuilder(sql);
        //2.遍历map
        Set<String> keySet = condition.keySet();
        //定义参数的集合
        List<Object> params = new ArrayList<Object>();

        for (String key : keySet) {

            //排除分页软件参数
            if ("currentPage".equals(key) || "rows".equals(key)){
                continue;
            }

            //获取value
            String value = condition.get(key)[0];  //获取的是数组,我们不考虑数组的情况   直接[0]获取一个值
            //判断value是否有值
            if (value != null && !"".equals(value)){
                //有值
                sb.append(" and "+key+" like ? ");
                params.add("%"+value+"%");  // ? 条件的值
            }
        }

        //添加分页查询
        sb.append(" limit ?,? ");
        //添加分页查询参数值
        params.add(start);
        params.add(rows);

        System.out.println(sql);
        System.out.println(params);
        return template.query(sb.toString(),new BeanPropertyRowMapper<User>(User.class),params.toArray());
    }

1638270697403

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n9TkH3aI-1649528615241)(https://gitee.com/shuaiqiguoguo/cloudimage/raw/master/img/202204100217519.png)]

1638272345303

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值