Mybatis应用(3)——mybatis框架使用 & mybatis项目应用初步 & mybatis使用报错集锦

引出


1.mybatis可以实现SQL语句和Java代码的解耦;
2.mybatis使用,tomcat的war包,和spring的jar包,分页的拦截器;
3.mybatis项目应用初步,SqlSessionUtils工具类,泛型的方式;
4.多条件查询<where,choose,when,otherwise,foreach,if>,排序order;
5.事务回滚,要么都成功,要么都失败,@Transactional注解;
6.在tomcat和spring框架下,常见的报错集锦

mybatis框架使用

https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api

在这里插入图片描述

1.导包:mybatis包+分页的包pom.xml文件【war包】

mybatis的包,日志相关的包,分页相关的包,

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.tianju</groupId>
  <artifactId>javawebHome0529</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>


  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
      <scope>provided</scope>
    </dependency>

    <!--    jsp相关-->
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>javax.servlet.jsp-api</artifactId>
      <version>2.3.3</version>
    </dependency>

    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>

    <dependency>
      <groupId>taglibs</groupId>
      <artifactId>standard</artifactId>
      <version>1.1.2</version>
    </dependency>

    <!--    数据库相关-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.20</version>
    </dependency>

    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.2.16</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.22.RELEASE</version>
    </dependency>

    <!--    其他-->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.22</version>
    </dependency>

    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.5</version>
    </dependency>

    <!--    工具包-->
    <dependency>
      <groupId>cn.hutool</groupId>
      <artifactId>hutool-all</artifactId>
      <version>5.8.11</version>
    </dependency>

    <!--    fastjson包-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.76</version>
    </dependency>
    
    <!--   mybatis 框架-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.4.5</version>
    </dependency>

    <!--        分页相关的包-->
    <dependency>
      <groupId>com.github.pagehelper</groupId>
      <artifactId>pagehelper</artifactId>
      <version>3.7.5</version>
    </dependency>

    <!--    日志相关的包-->
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>

  </dependencies>

</project>

1.导包:mybatis包+分页的包pom.xml文件【jar包】

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>communityManager</artifactId>
    <version>1.0-SNAPSHOT</version>
    <!--    项目打包成jar包-->
    <packaging>jar</packaging>
    <!--    项目描述-->
    <description>物业管理v1.0版本</description>

    <!--    继承一个父-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <version>2.3.0.RELEASE</version>
        <artifactId>spring-boot-starter-parent</artifactId>
    </parent>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <!--    做web项目的包-->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--       前端模板引擎,功能类似于jsp-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <!--        mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!--        分页工具,匹配springBoot的jar包-->
        <!--        后面加spring-boot-starter,表示按照spring重新改写的版本-->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.2.12</version>
        </dependency>

        <!--        mybatis的包-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.1</version>
        </dependency>

        <!--        druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.16</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!--    工具包-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.11</version>
        </dependency>

<!--        commons-io-->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.2</version>
        </dependency>

<!--        阿里云短信验证码相关包-->
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-core</artifactId>
            <version>4.5.3</version>
        </dependency>

<!--        fastjson-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>

<!--        增强方法的包-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>

<!--        测试sql mapper 的包-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>

    </dependencies>

    <!--    构建-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.yml配置文件

server:
  port: 82

# 1.连接数据库——对应之前 xml文件的数据库
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/community?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
    username: root
    password: 123

  ## TODO:设置上传文件大小
  servlet:
    multipart:
      max-file-size: 10MB # 设置单个文件最大大小为10MB

# mybatis其他配置
mybatis:
  # 2.给实体类起别名,首字母小写
  type-aliases-package: com.tianju.entity
  configuration:
    # 3.开启驼峰命名
    map-underscore-to-camel-case: true
    # 4.让日志生效
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  # 5.扫描sql的位置
  mapper-locations: classpath:/mapper/*Mapper.xml

# 全局的一个路径,用@Values获取
repairImg: D:/620/repair/

# 日志的相关配置
logging:
  file:
    name: D:\\620\\log\\community.log
  level:
    org.springframework.web: debug
    com.tianju: debug
    org.springframework.jdbc.support.JdbcTransactionManager: debug

2.resources下配置mybatis-config.xml文件,以及log4j.properties文件

在这里插入图片描述

my-batis-cofig.xml

要点:

(1)开启驼峰命名;(2)日志生效;(3)给所有实体类起别名;(4)分页工具PageHelper要加拦截器;(5)配置连接数据库相关的driver,url,username,password;其中url需要在&后面加 amp;

(6)配置mapper,采用简化的方式,用<package,点的方式

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 数据库和实体类 驼峰的问题 :开启驼峰命名-->
    <settings>
<!--        处理驼峰命名-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
<!--        日志生效-->
        <setting name="logImpl" value="LOG4J"/>
    </settings>

<!--    别名 给所有的实体类都起一个别名com.tianju.entity,所有别名都是类名的首字母小写-->
    <typeAliases>
        <package name="com.tianju.entity"/>

    </typeAliases>

<!--    分页需要加个拦截器-->
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageHelper">
            <property name="dialect" value="mysql"/>
        </plugin>
    </plugins>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useSSL=false&amp;serverTimezone=Asia/Shanghai&amp;allowPublicKeyRetrieval=true"/>
                <property name="username" value="root"/>
                <property name="password" value="123"/>
            </dataSource>
        </environment>
    </environments>
<!--    告诉配置文件,sql的xml文件在哪,java找用.  普通文件用 /-->
    <mappers>
<!--        简化方式,不然得写很多mapper-->
<!--        <mapper resource="com/tianju/dao/UserMapper.xml"/>-->
        <package name="com.tianju.dao"/>
    </mappers>
</configuration>

log4j.properties文件:

# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE            debug   info   warn error fatal
#log4j.rootCategory=debug, CONSOLE, LOGFILE
log4j.rootCategory=debug, CONSOLE

# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE

# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n

# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=D:/axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n


3.在resources下建文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 指定xml文件和 项目中哪个接口对应-->
<mapper namespace="com.tianju.dao.ComUserMapper">

</mapper>

在这里插入图片描述

4.在UserMapper中写SQL

要点:

(1)update,delete,insert不要写成select,不然会出现删除数据后,数据库确实删除了,但是前端list还有;

(2)if语句中不要忘记加逗号:

(3)select语句中,要记得加返回值;

mybatis项目应用

1.封装SqlSessionUtils.java工具类

要点:

  • 读取mybatis配置文件,告诉那里读取;
  • 生成一个建造sqlSession工厂的建造者,SqlSessionFactoryBuilder;
  • 建造者SqlSessionFactoryBuilder根据mybatis-config.xml建造一个SqlSession工厂,SqlSessionFactory;
  • 在SqlSession工厂中,通过SqlSessionFactory生成一个sqlSession,且打开自动提交sqlSession = sqlSessionFactory.openSession(true);
  • 需要什么类型的mapper就提供什么类型的mapper,public static <T> T getMapper(Class<T> mapperClass){return sqlSession.getMapper(mapperClass);};

getMapper方法—泛型T

在这里插入图片描述

在这里插入图片描述

package com.tianju.util;


import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

public class SqlSessionUtils {
    private static SqlSession sqlSession;
    static {
        //1.读取mybatis配置文件
        InputStream is = null;
        try {
            is = Resources.getResourceAsStream("mybatis-config.xml");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        //2.生成一个工厂--这个工程是通过建造者建造出来的
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

        //3.建造者根据mybatis-config.xml建造一个SqlSession工厂
        SqlSessionFactory sqlSessionFactory = builder.build(is);

        //4.通过sqlSessionFactory生成一个sqlSession
        sqlSession = sqlSessionFactory.openSession(true);
        //如果设置为true,每次自动提交
    }
    public static <T> T getMapper(Class<T> mapperClass){
        return sqlSession.getMapper(mapperClass);
    }
}

2.多条件查询<where, if>,传入Boolean

在这里插入图片描述

@Mapper
public interface HouseMapper {
    List<House> queryList(
            @Param("buildingId") Integer buildingId, // 楼栋
            @Param("storey") Integer storey, // 楼层
            @Param("roomNum") String roomNum, // 房间号
            @Param("areaMin") Double areaMin, // 面积
            @Param("areaMax") Double areaMax, // 面积
            @Param("intoDateBegin") Date intoDateBegin, // 入住开始时间
            @Param("intoDateEnd") Date intoDateEnd, // 入住结束时间
            @Param("areaOrderASC") Boolean areaOrderASC,
            @Param("areaOrderDESC") Boolean areaOrderDESC// 排序
                                      );
<mapper namespace="com.tianju.dao.HouseMapper">
    <select id="queryList" resultType="house">
<!--        SELECT-->
<!--        c_owner_house.*,-->
        SELECT
        c_house.id,
        c_owner_house.ownerId,
        c_building.num,c_building.floors,c_building.unit,
        c_house.storey,c_house.roomNum,c_house.area,c_house.into_date,c_house.status,c_house.building_id,
        user_owner.realname AS ownerName

        FROM c_house
        LEFT JOIN c_building ON c_building.id = c_house.building_id
        LEFT JOIN c_owner_house ON c_owner_house.houseId = c_house.id
        LEFT JOIN user_owner ON user_owner.id = c_owner_house.ownerId

        <where>
            <if test="buildingId!=null">
                c_building.id=#{buildingId}
            </if>
            <if test="storey!=null">
                AND c_house.storey=#{storey}
            </if>
            <if test="areaMin!=null">
                AND c_house.area &gt;= #{areaMin}
            </if>
            <if test="areaMax!=null">
                AND c_house.area &lt;= #{areaMax}
            </if>
            <if test="intoDateBegin!=null">
                AND c_house.into_date &gt; #{intoDateBegin}
            </if>
            <if test="intoDateEnd!=null">
                AND c_house.into_date &lt;= #{intoDateEnd}
            </if>
            <if test="roomNum!=null and roomNum!=''">
                AND roomNum LIKE #{roomNum}
            </if>
        </where>
        <if test="areaOrderASC">
            ORDER BY c_house.area
        </if>
        <if test="areaOrderDESC">
            ORDER BY c_house.area DESC
        </if>
    </select>

service层使用

    @Autowired
    private HouseMapper houseMapper;
    @Override
    public PageInfo<House> queryList(Integer buildingId, Integer storey, String roomNum, Double areaMin, Double areaMax, Date intoDateBegin, Date intoDateEnd,
                                     Integer pageNum, Integer pageSize,
                                     Boolean areaOrderASC, Boolean areaOrderDESC) {
        PageHelper.startPage(pageNum,pageSize);
        List<House> list = houseMapper.queryList(buildingId,storey,roomNum,areaMin,areaMax,intoDateBegin,intoDateEnd,areaOrderASC,areaOrderDESC);
        return new PageInfo<>(list);
    }

3.多条件查询IN,foreach+可变长度参数<choose,when,otherwise>,传入list

    /**
     * 查询空闲的房子,根据buildingId查询,忽略单元
     * @param buildingIdList 根据buildingId查询房间,默认查询空间房间,如果status为false,
     *                   则也可以查询到非空闲的房间
     * @param status 默认为true,如果为false,则可以查询到非空闲房子
     * @return
     */
    List<House> queryHouseByBuildingId(
            @Param("buildingIdList") List<Integer> buildingIdList,
            @Param("status") Boolean... status);
    <select id="queryHouseByBuildingId" resultType="house">
<!--        SELECT * FROM c_house WHERE building_id IN (1,2,3) AND !status-->
        SELECT * FROM c_house

        WHERE
        building_id IN
        <foreach collection="buildingIdList" open="(" separator="," close=")" item="id">
            #{id}
        </foreach>
        <choose>
            <when test="status!=null and status.length>0 and !status[0]">
<!--                如果为false,则可以查询到非空闲房子-->
            </when>
            <otherwise>
                AND !status
            </otherwise>
        </choose>
    </select>

4.多条件查询进阶版<where,choose,when,otherwise,foreach,if>

在这里插入图片描述

    /**
     * 查询所有空闲的房子,显示到左边的框里面 TODO:后面再全查询的基础上进行
     */
    List<House> queryAllNoIntoHouse(
            @Param("buildingIdList") List<Integer> buildingIdList, // 楼栋
            @Param("storey") Integer storey, // 楼层
            @Param("areaMin") Double areaMin, // 面积
            @Param("areaMax") Double areaMax, // 面积
            @Param("areaOrderASC") Boolean areaOrderASC,
            @Param("areaOrderDESC") Boolean areaOrderDESC,// 排序
            @Param("status") Boolean... status // 如果为false,则可以查询到非空闲房子,其他情况 都是查询空闲房子

    );
    <select id="queryAllNoIntoHouse" resultType="house">
        SELECT
        c_house.id,
        c_owner_house.ownerId,
        c_building.num,c_building.floors,c_building.unit,
        c_house.storey,c_house.roomNum,c_house.area,c_house.into_date,c_house.status,c_house.building_id,
        user_owner.realname AS ownerName

        FROM c_house
        LEFT JOIN c_building ON c_building.id = c_house.building_id
        LEFT JOIN c_owner_house ON c_owner_house.houseId = c_house.id
        LEFT JOIN user_owner ON user_owner.id = c_owner_house.ownerId

        <where>
            <choose>
                <when test="buildingIdList!=null and buildingIdList.size()>0">
                    building_id IN
                    <foreach collection="buildingIdList" open="(" separator="," close=")" item="id">
                        #{id}
                    </foreach>
                </when>
                <otherwise>
<!--                    默认全查-->
                </otherwise>
            </choose>

            <if test="storey!=null">
                AND c_house.storey=#{storey}
            </if>
            <if test="areaMin!=null">
                AND c_house.area &gt;= #{areaMin}
            </if>
            <if test="areaMax!=null">
                AND c_house.area &lt;= #{areaMax}
            </if>
            <choose>
                <when test="status!=null and status.length>0 and !status[0]">
                    <!--                如果为false,则可以查询到非空闲房子-->
                </when>
                <otherwise>
                    AND !status
                </otherwise>
            </choose>
        </where>
        <if test="areaOrderASC">
            ORDER BY c_house.area
        </if>
        <if test="areaOrderDESC">
            ORDER BY c_house.area DESC
        </if>
    </select>

前后端交互controller层

// 2.处理搜索请求
    // 默认显示所有可以选择的房间,放到左侧的框里,如果选择,双击,这条数据跳到右边的框里
    @RequestMapping("/query/house")
    @ResponseBody
    public ResData queryHouse(
//            @RequestParam(value = "buildingList", defaultValue = "List[]") List<Building> buildingList, // 楼栋
//            Integer storey, // 楼层,没有用到
//            Double areaMin,
//            Double areaMax,
//            @RequestParam(value = "areaOrderASC",defaultValue = "false") Boolean areaOrderASC,
//            @RequestParam(value = "areaOrderDESC",defaultValue = "false") Boolean areaOrderDESC
            @RequestBody HouseFront houseFront
            ){
        Double areaMax = houseFront.getAreaMax();
        Double areaMin = houseFront.getAreaMin();
        Boolean areaOrderASC = houseFront.getAreaOrderASC();
        Boolean areaOrderDESC = houseFront.getAreaOrderDESC();
        Integer storey = houseFront.getStorey();
        List<Building> buildingList = houseFront.getBuildingList();
        List<Integer> buildingIds = buildingList.stream().map(Building::getId).collect(Collectors.toList());

        System.out.println("查询条件:"
                +areaMin+"/"
                +areaMax+"/"
                +buildingIds+"/"
                +storey+"/"
        );

        // 大小的问题
        if (!StringUtils.isBlank(areaMin) && !StringUtils.isBlank(areaMax)){
            if (areaMin.compareTo(areaMax)>0){
                return new ResData(1002, "最小面积不能大于最大面积", null);
            }
        }
        // 这个排序顺序不能两个都是true
        if (areaOrderASC && areaOrderDESC){
            return new ResData(1003, "排序条件冲突", null);
        }

        List<House> list = houseService.queryAllNoIntoHouse(buildingIds, storey, areaMin, areaMax, areaOrderASC, areaOrderDESC, true);
        list.forEach(System.out::println);
        return new ResData(200, "ok", list);
    }

前端代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>新增业主</title>
    <link rel="stylesheet" href="/bootstrap/css/bootstrap.min.css">
    <script src="/js/jquery-3.5.1.js"></script>
    <script src="/bootstrap/js/bootstrap.js"></script>
    <script src="/js/axios.min.js"></script>
    <script src="/js/vue.min-v2.5.16.js"></script>
</head>
<body>
<div id="app">
    <br>
    录入业主基础信息:<br>
    用户名:&nbsp;&nbsp; <input type="text" v-model="username">
    真实姓名:<input type="text" v-model="realname">
    电话号码:<input type="text" v-model="tel"><br>

    身份证号:<input type="text" v-model="identity">
    性别:
    <input type="radio" v-model="gender" value="男"><input type="radio" v-model="gender" value="女"><br>
    备注信息:<textarea v-model="notes" rows="3" cols="22"></textarea><br>
    <br>
    录入选房信息:<br>
    <div>
<!--        搜索所有可以选择的房子-->
        楼栋编号:
        <select v-model="buildingId">
            <option value="">--请选择楼栋--</option>
            <option v-for="building in buildingList" :value="building.id">{{building.num}}-{{building.unit}}</option>
        </select>
        <button @click="addBuildingId">添加楼栋</button>
        <button @click="resetSelectBuildsBtn">重选楼栋</button>
        <br>

        选中的楼栋为:
        <select multiple style="width: 100px" @dbclick="removeSelectedBuildingsBtn">
            <option v-for="building in selectedBuildingList" :value="building.id">{{building.num}}-{{building.unit}}</option>
        </select>
        楼层:
        <select v-model="selectFloor">
            <option value="">-选择楼层--</option>
            <option v-for="floors in floorList" :value="floors">{{floors}}</option>
        </select>
        面积:
        <input type="text" v-model="areaMin" style="width: 50px" placeholder="小">---
        <input type="text" v-model="areaMax" style="width: 50px" placeholder="大">

        <button @click="areaASC">面积升序</button>
        <button @click="areaDESC">面积降序</button>

        <button @click="searchHouseBtn">搜索房子</button>
        <button @click="searchHouseBtnClr">重置搜索</button><br>
    </div>


<!--    TODO:可以选的房子的左侧的框-->
<!--    canSelectHouse-->
    可选的房子为:
    <select multiple style="width: 200px" @dblclick="selectHouseDbc">
        <option v-for="house in canSelectHouse" :value="house.id">
            {{house.num}}--{{house.unit}}--{{house.roomNum}}--{{house.area}}
        </option>
    </select>

    选中的房子为:
    <select  multiple style="width: 200px" @dblclick="removeSelectedHouseDbc">
        <option v-for="house in selectedHouse" :value="house.id">
            {{house.num}}--{{house.unit}}--{{house.roomNum}}--{{house.area}}平米
        </option>
    </select>
    <br>
    <button @click="add">添加</button>
    <button @click="reset">重置</button><br>
    <br>
</div>

<script>
    let app = new Vue({
        el:"#app",
        data:{
            // 首先是用户的基础信息
            username:"",
            realname:"",
            tel:"",
            gender:"男",
            identity:"",
            buildingId:"",
            houseId:"",
            notes:"",

            // 然后是查询的数据
            // 然后是选中的楼栋的id,1栋1单元...
            floorList : Array.from({ length: 12 }, (v, i) => i + 1), // 最大有12层楼,因此这里可以选择1到12
            selectedBuildingList:[], // List<Building> buildingList, // 楼栋
            selectFloor:"", // 楼层
            areaMin:"", // 面积
            areaMax:"", // 面积
            // 和排序相关的
            areaOrderASC:false,
            areaOrderDESC:false,

            // 在左侧边框里面显示可以选择的所有房间信息
            canSelectHouse:[],

            // 在右侧框里面显示已经选择的所有房间信息
            selectedHouse:[],

            buildingList:[]
        },
        methods:{
            searchHouseBtnClr(){
                // 重置搜索
                this.selectedBuildingList =[]
                this.buildingId = ""
                this.selectFloor="" // 楼层
                this.areaMin="" // 面积
                this.areaMax="" // 面积
                    // 和排序相关的
                this.areaOrderASC=false
                this.areaOrderDESC=false
                this.queryHouse();

            },
            // 如果选中了一个楼栋,就添加一个楼栋来,如果有重复的就不添加
            addBuildingId(){
                let selected = this.buildingList.find(b=>b.id===this.buildingId)
                // 这个selected是一个 building对象
                console.log(selected)
                // 只有selected里面有值,并且没有重复的时候才能添加进来
                if (selected && !this.selectedBuildingList.find(b=>b.id===selected.id)){
                    this.selectedBuildingList.push(selected)
                }
            },
            // 如果双击选中的楼栋,则删除已经选中的楼栋
            removeSelectedBuildingsBtn(event){
                const optionValue = event.target.value; // 获取双击的对象的值
                this.selectedBuildingList = this.selectedBuildingList.filter(building=>building.id != optionValue)
            },

            // 在可选的房子的框里面双击,则在选中的房子的框里面加这个房子,并且在可选的房子的框里面删除被选的房子
            selectHouseDbc(event){
                const optionValue = event.target.value; // 获取双击的对象的值
                // TODO:注意两点,这个过滤后的还是一个集合,需要获取第一个元素,另外要用== 不要用===
                const house = this.canSelectHouse.filter(house=>house.id == optionValue)[0]
                console.log(house)
                this.selectedHouse.push(house)
                this.canSelectHouse = this.canSelectHouse.filter(house=>house.id != optionValue)
                console.log(this.canSelectHouse)
                console.log(this.selectedHouse)
            },

            removeSelectedHouseDbc(event){
                const optionValue = event.target.value; // 获取双击的对象house
                console.log(optionValue)
                const house = this.selectedHouse.filter(house=>house.id == optionValue)[0]
                this.selectedHouse = this.selectedHouse.filter(house=>house.id !=optionValue)
                this.canSelectHouse.push(house)
            },

            //如果选中了一个房子,就添加一个进来,如果有重复的就不添加
            addHouseId(){
                const selectedOption = this.allhouseList.find(house=>house.id===this.houseId);

                if (selectedOption && !this.houseList.find(house => house.id === selectedOption.id)) {
                    this.houseList.push(selectedOption);
                }
            },

            removeSelectedOption(event) {
                const optionValue = event.target.value;//获取双击的对象的值
                console.log(optionValue);
                this.houseList = this.houseList.filter(house => house.id != optionValue);
            },

            add(){
                let param = {};
                param.username = this.username;
                param.realname = this.realname;
                param.tel = this.tel;
                param.gender = this.gender;
                param.identity = this.identity;
                param.notes = this.notes;
                param.houseList = this.selectedHouse;

                axios.post("/owner/add",param)
                    .then(response=>{
                        if(response.data.code==200)
                        {
                            location.href="/user/ownerPage"
                        }
                        else
                        {
                            alert(response.data.msg);
                        }
                    })


            },
            // 重新输入用户信息
            reset(){
                this.username=""
                this.realname=""
                this.tel=""
                this.identity=""
                this.notes=""
            },
            // 重新选择楼栋
            resetSelectBuildsBtn(){
                this.selectedBuildingList =[]
                this.buildingId = ""
            },
            areaASC(){
                this.areaOrderASC=true;
                this.areaOrderDESC=false;
                this.queryHouse();
            },
            areaDESC(){
                this.areaOrderASC=false;
                this.areaOrderDESC=true;
                this.queryHouse();
            },
            // 查询所有可以选择的房间
            queryHouse(){
                let jsonObj = {}
                jsonObj.buildingList=this.selectedBuildingList;
                jsonObj.storey=this.selectFloor;
                jsonObj.areaMin=this.areaMin;
                jsonObj.areaMax=this.areaMax;
                jsonObj.areaOrderASC=this.areaOrderASC;
                jsonObj.areaOrderDESC=this.areaOrderDESC;
                axios.post("/owner/query/house",jsonObj)
                    .then(response=>{
                        if (response.data.code==200){
                            this.canSelectHouse = response.data.data;
                        }else {
                            alert(response.data.msg)
                        }
                    })
            },
            searchHouseBtn(){
                this.queryHouse();
            },

        },
        mounted(){
            // 首先要获得所有可以选择的房子
            this.queryHouse();

            //查询楼栋下拉列表
            axios.get("/build/list/all")
                .then(response=>{
                    this.buildingList = response.data.data;
                })
        }
    });
</script>
</body>
</html>

专门用来前后端交互的实体类

package com.tianju.entity;

import com.alibaba.druid.filter.AutoLoad;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

/**
 * 专门用来和前端交互的一个对象;
 * 用于选房页面的复杂查询
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class HouseFront {
    private List<Building> buildingList;
    private Integer storey;
    private Double areaMin;
    private Double areaMax;
    private Boolean areaOrderASC;
    private Boolean areaOrderDESC;
}

5.传入Boolean类型的可变长度参数

    /**
     * 超级管理员能看离职员工和在职员工的信息,
     * 其他员工只能看在职员工信息。
     * @param su 表示是否是超级管理员,su 为true 时,为超级管理员
     * @return
     */
    List<User> queryAllManagerUser(
            @Param("su") Boolean... su
    );
<!--    修改用户信息,根据用户id查询一条出来-->
    <select id="queryManagerById" resultType="user">
<!--        SELECT * FROM user_manager WHERE id = #{id}-->
        SELECT * FROM
        (SELECT
        user_manager.id,
        user_role.id AS userRoleId,user_role.role_id AS typeId,user_role.user_id AS userId,
        user_manager.username,user_manager.password,user_manager.tel,
        user_manager.notes AS userNotes,user_manager.create_time,user_manager.update_time,
        user_manager.operator,user_manager.status,user_manager.firstLog,
        user_role_type.name AS typeName,user_role_type.notes AS typeNotes
        FROM user_manager
        LEFT JOIN user_role ON user_manager.id = user_role.user_id
        LEFT JOIN user_role_type ON user_role.role_id = user_role_type.id
        ) t
        WHERE t.id = #{id}
        <!--        一般人员只能查询在职员工,超级管理员可以查所有员工-->
        <choose>
            <when test="su==null or su.length &lt; 1">
                AND firstLog !='100' AND status
            </when>
        </choose>

    </select>

在这里插入图片描述

6.多个条件排序 ORDER BY num,unit

在这里插入图片描述

controller层

    @RequestMapping("/list")
    @ResponseBody
    public ResData buildingList(
            @RequestParam(value = "pageNum",defaultValue = "1")Integer pageNum,
            @RequestParam(value = "pageSize",defaultValue = "10")Integer pageSize,
            @RequestParam(value = "order",defaultValue = "false")Boolean order){
        PageInfo<Building> pageInfo = buildingService.queryAll(pageSize, pageNum,order);
        System.out.println(pageInfo);
        return new ResData(200,"ok",pageInfo);

    }

service层

    @Override
    public PageInfo<Building> queryAll(Integer pageSize,Integer pageNum,Boolean order) {
        PageHelper.startPage(pageNum, pageSize);
        List<Building> list = buildingMapper.queryAll(order);
        return new PageInfo<>(list);
    }

mapper层

List<Building> queryAll(Boolean order);

xml文件的sql

    <select id="queryAll" resultType="building">
        SELECT * FROM c_building
        <if test="order">
            ORDER BY num,unit
        </if>
    </select>

7.一条新增影响多个数据库的表@Transactional

在这里插入图片描述

controller层

// 3.处理新增用户信息
    Boolean addFlag = true;
    @RequestMapping("/add")
    @ResponseBody
    public ResData addOwner(
            @RequestBody Owner owner
    ){
        System.out.println(owner);
        // 1.判断前端输入的数据全不
        // 2.如果确认不新添房子,则只修改user_owner表,则该用户不能登陆账号
        // 3.如果新增了房子,则修改user_owner表,c_house表,c_owner_house表
        if (StringUtils.isBlank(owner.getUsername(),owner.getRealname(),owner.getIdentity(),owner.getGender())){
            return new ResData(1001, "必填项为空", null);
        }

        if (!StringUtils.isLegalPhone(owner.getTel())){
            return new ResData(1002, "手机号码不合法", null);
        }

        // TODO:这样可以吗???
        if (addFlag){
            if (owner.getHouseList().size()<1){
                addFlag = false;
                return new ResData(2001, "确定新增一条未选择房间的业主?", null);
            }
        }

        // 初始化一下密码,手机号前4位
        owner.setPassword(owner.getTel().substring(0,4));
        // 获取session
        User user = (User) session.getAttribute("user");
        if (user!=null){
            owner.setOperator(user.getOperator());
        }
        Integer flag = ownerService.add(owner);
        if (flag<1){
            return new ResData(3001, "系统繁忙,请稍后重试", null);
        }
        addFlag = true; // 新增成功再置为true,下一条就会再次判断是不是没填
        return new ResData(200,"新增用户信息成功",null);


    }

service层@Transactional

    @Transactional
	@Override
    public Integer add(Owner owner) {
        // 如果新用户只是意向客户
        if (owner.getHouseList().size()<1){
            owner.setHouseNum(0);
            return ownerMapper.add(owner);
        }
        // 买房的客户变成了业主
        Integer num = 0; //影响数据库的数据的行数
        ownerMapper.add(owner);
        for (House house:owner.getHouseList()){
            // 房间状态改为入住的状态
            houseMapper.intoHouse(house.getId());
            // 房屋-业主对应表新增一条数据
            houseMapper.addOwnerHouse(owner.getId(), house.getId());
            num +=2;
        }
        return num;
    }
    <update id="intoHouse">
        UPDATE c_house SET into_date = NOW(),status=1 WHERE id=#{id}
    </update>

    <insert id="addOwnerHouse">
        INSERT INTO c_owner_house(ownerId,houseId) VALUES (#{ownerId},#{houseId})
    </insert>

mybatis使用报错集锦

1.忘记加 resultType=“opus”

报错信息:

Error querying database. Cause: org.apache.ibatis.executor.ExecutorException: A query was run and no Result Maps were found for the Mapped Statement ‘com.tianju.dao.OpusMapper.queryByAuthorIdAndLikeName’. It’s likely that neither a Result Type nor a Result Map was specified.

报错原因:

查询返回值类型没写

在这里插入图片描述

解决方案:

加上resultType=“opus”,如下:

<!--    多种条件查询-->
    <select id="queryByAuthorIdAndLikeName" resultType="opus">
        SELECT t_opus.*,tt.name AS typename,tu.username
        FROM t_opus
        LEFT JOIN t_types tt ON t_opus.typeId = tt.id
        LEFT JOIN user_tab tu ON t_opus.authorId = tu.id
        <where>
            <if test="authorId!=null">
                authorId = #{authorId}
            </if>
            <if test="name!=null and name!=''">
                AND t_opus.name LIKE #{name}
            </if>
        </where>
        ORDER BY t_opus.id DESC
    </select>

2.前端显示list页面数据为空

问题描述:

pageHelper里面的没有数据,前端显示为空:

PageInfo{pageNum=0, pageSize=0, size=0, startRow=0, endRow=0, total=0, pages=0, list=null, firstPage=0, prePage=0, nextPage=0, lastPage=0, isFirstPage=false, isLastPage=false, hasPreviousPage=false, hasNextPage=false, navigatePages=0, navigatepageNums=null}

原因分析:

pageHelper有顺序要求

在这里插入图片描述

解决方案:
在这里插入图片描述

    /**
     * 分页在service层完成,利用pageHelper 在service层做完分页的事情
     */
    @Override
    public PageInfo<Opus> queryByAuthorIdAndLikeName(String name, Integer authorId, Integer pageNum,Integer pageSize) {
        // 先要用pageHelper的 分页,然后执行查询
        PageHelper.startPage(pageNum, pageSize);
        List<Opus> list = opusMapper.queryByAuthorIdAndLikeName(name, authorId);
        PageInfo<Opus> pageInfo = new PageInfo<>(list);
        return pageInfo;
    }

3.由于mybatis版本引起的问题

报错信息:

org.apache.ibatis.exceptions.PersistenceException:

Error querying database. Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named ‘username’ in ‘class java.lang.String’

Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named ‘username’ in ‘class java.lang.String’

报错原因

如果3.4.5版本,3.4开头的就是低版本,并且username要在动态sql中使用,即,if,where,中使用,则一个参数也需要加@param,如果是3.5.9以上版本,则可以不加

在这里插入图片描述

解决方案:

1.UserMapper里面加@param

// 如果3.4.5版本,3.4开头的就是低版本,并且username要在动态sql中使用
    // 即,if,where,中使用,则一个参数也需要加@param,如果是3.5.9以上版本,则可以不加
    List<User> findByUsernameSQL(@Param("username") String username);

2.mybatis版本换成3.5以上

<!--   mybatis 框架-->
<!--        小版本会导致userMapper findByUsernameSQL-->
<!--        3.5.9-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>

在项目中的案例:

在这里插入图片描述

接口java文件

List<Company> queryByLikeName(@Param("name") String name);

xml文件

    <select id="queryByLikeName" resultType="company">
        SELECT com_company.*,com_type.name AS typename,com_user.username AS username FROM com_company
        LEFT JOIN com_type ON com_company.type_id=com_type.id
        LEFT JOIN com_user ON com_company.user_id=com_user.id
        <where>
            <if test="name!=null and name !=''">
                com_company.name LIKE #{name}
            </if>
        </where>
    </select>

可以在这里看相关的包

https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api

在这里插入图片描述

4. but found: 11:返回值类型错误

报错信息:

org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 11

报错原因:

类型错误,如果查询出的结果可能是多个,则需要用List进行接收

在这里插入图片描述

解决方案:

改成list

List<User> findByUsernameSQL(@Param("username") String username);

5.Mapper.XML文件中的id重复

报错信息

15-Jun-2023 10:59:46.335 信息 [http-nio-8080-exec-7] org.apache.catalina.core.ApplicationContext.log 将servlet[com.tianju.servlet.user.LoginServiceVue]标记为不可用
15-Jun-2023 10:59:46.335 严重 [http-nio-8080-exec-7] org.apache.catalina.core.StandardWrapperValve.invoke 分配异常的servlet [com.tianju.servlet.user.LoginServiceVue]
java.lang.IllegalArgumentException: Mapped Statements collection already contains value for com.tianju.dao.OpusMapper.removeById

报错原因:

mapper中存在id重复的值

在这里插入图片描述

解决方案:

修改其中一个mapper的id

6.删除不生效:Mapper.xml文件中的<delete写成了select

问题描述:

在前端点击删除按钮,刷新前端后该数据依然存在,数据库中的数据确实被删除;如果项目重启,前端才没有该条数据,开始以为数缓存的问题;

这个问题比较奇怪,尝试了清除mybatis的缓存等方法都没有解决,

在这里插入图片描述

错误原因:

这里的delete,写成了select

在这里插入图片描述

解决方案:改成delete

<!--    TODO:如果这里的delete,写成了select-->
    <delete id="removeById" parameterType="integer">
        DELETE FROM t_opus WHERE id = #{id}
    </delete>

7.修改数据:报500异常,update 的逗号,没加

报错信息:

Error updating database. Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘intro=‘123’,
updateTime=now() WHERE id=47’ at line 11

SQL: UPDATE t_opus SET name=?, typeId=? intro=?, updateTime=now() WHERE id=?
Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'intro=‘123’,

updateTime=now() WHERE id=47’ at line 11

排查bug的顺序:

(1)先看前端,报500异常,说明java代码有问题;

在这里插入图片描述

(2)在后台的tomcat localhost log里面定位错误是SQL语句的问题;

在这里插入图片描述

(3)在service这里可以看到执行的sql,如下

UPDATE t_opus SET name=?, typeId=? intro=?, updateTime=now() WHERE id=? 

在这里插入图片描述

(4)最终的原因是OpusMapper.xml里面的动态sql写错了;

在这里插入图片描述

解决方法:加上逗号

OpusMapper.java接口

    /**
     * 修改一条图书信息
     */
    Integer updateById(@Param("opus") Opus opus);

OpusMapper.xml中对应的动态sql

<!--    如果@Param("opus") 和上面对比-->
    <update id="updateById">
        UPDATE t_opus SET
        <if test="opus.name!=null and opus.name!=''">
            name=#{opus.name},
        </if>
        <!--            如果的Integer类型的id,加上了不为''的条件-->
        <if test="opus.typeId!=null">
<!--            如果这里没有加 逗号 -->
            typeId=#{opus.typeId},
        </if>
        <if test="opus.intro!=null and opus.intro!=''">
            intro=#{opus.intro},
        </if>
        updateTime=now() WHERE id=#{opus.id}
    </update>

8.xml文件和java文件名不一致:

报错信息:

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.tianju.dao.CompanyMapper.queryByLikeName

在这里插入图片描述

在这里插入图片描述

原因解析:

后台显示sql并没有执行,再结合 Invalid bound statement (not found):的报错信息,检查文件名确实不一致

在这里插入图片描述

在这里插入图片描述

9.在xml文件中的resultType写错映射对象

报错信息:

org.apache.catalina.core.StandardWrapperValve.invoke 分配异常的servlet [com.tianju.servlet.company.CompanyListServlet]
java.lang.ClassNotFoundException: Cannot find class: user

在这里插入图片描述

10.日期类型判断时加了!=''引发的异常

报错信息:

Cause: java.lang.IllegalArgumentException: invalid comparison: java.util.Date and java.lang.String

在这里插入图片描述

<!--            TODO:数字和日期类型不要加 createTime!=''-->
            <if test="createTime!=null and createTime!=''">
                create_time=#{createTime},
            </if>

解决方案:

<if test="createTime!=null">
	create_time=#{createTime},
</if>

总结

1.mybatis可以实现SQL语句和Java代码的解耦;
2.mybatis使用,tomcat的war包,和spring的jar包,分页的拦截器;
3.mybatis项目应用初步,SqlSessionUtils工具类,泛型的方式;
4.多条件查询<where,choose,when,otherwise,foreach,if>,排序order;
5.事务回滚,要么都成功,要么都失败,@Transactional注解;
6.在tomcat和spring框架下,常见的报错集锦

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Arya's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值