Java零基础-Mybatis篇

【Mybatis】

1.JDBC不足

JDBC作为Java操作数据库的模板,如果想要对数据库进行操作,必须使用JDBC,但是在使用JDBC进行数据库操作时,重复代码多,动态SQL构建繁琐,将查询结果转化为对象,相当麻烦,开发效率低。

基于JDBC开发效率相对低的情况,市面上各个组织,对JDBC进行封装,产生各种数据库操作层解决方案:

Hibernate 重量级的ORM框架

ibatis 轻量级ORM框架 与2010-06-16 改名 mybatis

Spring JPA

Mybatis plus

Spring JDBCTemplate

以上框架都是对JDBC的封装,处理Java操作数据库数据的问题。

2.为什么要学习框架

2.1.1 提高开发效率

在Java中,框架在一定程度就是对某些功能的封装,对外暴露统一操作API,可以简化开发难度,提高开发效率。

2.1.2 提高代码的可维护性

由于框架在一定程度上说,就是模板,所有基于某个框架进行开发的项目,肯定要遵循模板规则,那么在维护时,只需要了解模板规则即可。

2.1.3 可以提高代码的健壮性

市面上相对比较流行的框架,被大多人使用,出现的问题能够及时暴露,问题也能得到较快的修复。

3.Mybatis概述

3.1 简介

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

Mybtais是一个ORM框架,轻量级的ORM框架。相对于重量级的ORM框架Hibernate而言,mybatis是一个半自动框架,而Hibernate是一个全自动框架。并且,Hibernate提出跨"平台",Hinernate的跨平台是指Hinernate可以在多种数据库下进行操作,同一套代码支持多种数据库,HQL语句,Hibernate方言,对数据库操作进行翻译,根据不同的数据,将API翻译成不同的SQL,对数据进行操作,依赖ORM思想。

Mybatis是一个半自动的框架,早期Hibernate在流行时,开发者发现Hibernate虽然功能强大,但是由于如果想使用全自动功能,将Hibernate和数据库关心进行配置,配置很繁琐,其二,Hibernate对功能进行全方面的封装,将用户的操作,转化为SQL语句,然后进行数据库操作,整个转换过程是Hibernate,开发无法控制,如果要进行SQL语句优化是没法实现的。所以,在数据库压力逐渐增大的情况下,Hibernate框架性能问题就出现了。基于这样的情况,Mybatis框架应运而生,mybatis将SQL语句的定义控制权,完全交给了开发者,并且暴露一套API,对JDBC中:事务,参数,查询结果等进行配置处理。Mybatis也是基于ORM思想.

Mybatis(半自动化):还是需要书写sql

Hibernate(全自动化):不需要写sql就能操作数据库—>HQL

3.2 ORM

ORM : Object relation mapping

对象关系映射

将数据库信息和Java中实体类进行映射

users: 1 张三 20 User: id、uName、age

4.Mybatis入门使用

在这里插入图片描述

4.1 创建数据库和表

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.2 创建空项目

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

4.3 创建Maven的java项目

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.4 引入Mybatis相关jar包依赖

1、导入依赖

2、书写mybatis的主配置文:连接信息 mybatis.xml

3、书写存放sql语句的局部配置文件 xxx.xml(与dao层类的类名保持一致)

4、关联主配置与局部配置

5、启动mybatis,调用核心类测试

<dependencies>
    <!--引入mybatis的依赖-->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.7</version>
    </dependency>
    <!--引入mysql的驱动包-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.49</version>
    </dependency>
</dependencies>

4.5 编写实体类User

package com.bjpowernode.domain;
import java.util.Date;

/**
 * @项目 code-mybatis
 * @描述 用户实体类
 * @时间 2021-07-15 22:23
 **/
public class User {
    private Integer id;
    private String name;
    private Integer age;
    private String sex;
    private String address;
    private Date birth;

    public User(Integer id, String name, Integer age, String sex, String address, Date birth) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.sex = sex;
        this.address = address;
        this.birth = birth;
    }

    public User(String name, Integer age, String sex, String address, Date birth) {
        this.name = name;
        this.age = age;
        this.sex = sex;
        this.address = address;
        this.birth = birth;
    }

    public User() {
    }

    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 Integer getAge() {
        return age;
    }

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

    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 Date getBirth() {
        return birth;
    }

    public void setBirth(Date birth) {
        this.birth = birth;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                ", birth=" + birth +
                '}';
    }
}

4.6 编写映射文件UserMapper接口

package com.bjpowernode.mapper;
import com.bjpowernode.domain.User;

import java.util.Map;

public interface UserMapper {
    /**
     * 根据ID查询 返回 Map
     * @param id
     * @return
     */
    Map<String,Object> queryById1(Integer id);

    /**
     * 根据ID查询  返回 自定义对象
     * @param id
     * @return
     */
    User queryById2(Integer id);
}

4.7 编写映射文件UserMapper.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.bjpowernode.mapper.UserMapper">
    <select id="queryById1"  resultType="java.util.Map">
        select * from user where id = 1
    </select>

    <select id="queryById2" resultType="com.bjpowernode.domain.User">
        select * from user where id = 1
    </select>
</mapper>

4.8 编写mybatis.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>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&useSSL=false&characterEncoding=UTF8&serverTimezone=UTC" />
                <property name="username" value="root" />
                <property name="password" value="123456" />
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>
</configuration>

4.9 编写测试类

public class Test {
    public static void main(String[] args) throws Exception {
        //1.引入配置文件
        String config =  "mybatis.xml";
        InputStream in = Resources.getResourceAsStream(config);
        //2.解析配置文件 构建 SqlSession 构建对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        //3.使用构建对象构建 SqlSessionFactory 对象  创建Sql会话对象  程序和数据库进行SQL会话对象
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(in);
        //4.创建一个具体的SqlSession 对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //5.进行数据库具体sql会话
        User user = sqlSession.selectOne("com.example.mapper.UserMapper.queryById2",1);
        System.out.println(user);
        //6.关闭会话
        sqlSession.close();
    }
}

4.10 执行过程简单分析

1.解析了xml 配置文件 —> configuration

  1. 将映射文件信息 解析 mappedStatements map结构 每个 mapper文件中的指令 被封装为MappedStatement

  2. SqlSession 根据传入 statement 的key 从mappedStatements 中获取到对应 MappedStatement

  3. MappedStatement 中已经定义了好了参数类型 返回结果的类型 还有原生sql

  4. 获取原生sql 参数类型 返回结果的类型 ,根据参数类型 原生sql 可以内部预处理 sql 将sql 发送给数据库

  5. 接收sql 执行的结果 ,将其封装为对应的返回类型
    在这里插入图片描述

5.Mybatis进行CRUD

5.1 方式 一:指令ID方式

根据mybatis将映射信息封装为:MappedStatement对象,存在Map中,将namespace和指令ID当做key

根据key获取对应MappedStatement对象,进行数据库操作

5.1.1 创建项目修改pom.xml引入相关依赖

在这里插入图片描述

<dependencies>
    <!--引入mybatis的依赖-->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.6</version>
    </dependency>
    <!--引入mysql的驱动包-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.22</version>
    </dependency>
</dependencies>
5.1.2 复制上个项目里面的所有类和配置

在这里插入图片描述

5.1.3 SqlSessionUtils工具类
/**
 * 创建SqlSession的工具类
 */
public class SqlSessionUtils {
    private static final SqlSessionFactory sqlSessionFactory ;

    static {
        //配置文件
        String config = "mybatis.xml";
        //将配置文件转化为流对象
        InputStream in = null;

        //创建SqlSessionFactory的构建对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = null;

        try {
            //将配置文件转化为流对象
            in = Resources.getResourceAsStream(config);
            //创建SqlSessionFactory的构建对象
            sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        } catch (IOException e) {
            e.printStackTrace();
        }
        //通过配置信息 构建 SqlSessionFactory工厂
        sqlSessionFactory = sqlSessionFactoryBuilder.build(in);
    }
    /**
     * 获取SQL会话对象
     * @return
     */
    public  static SqlSession getSession(){
        return  sqlSessionFactory.openSession();
    }
}
5.1.4 数据库数据操作类UserDao
package com.bjpowernode.dao;

import com.bjpowernode.domain.User;
import com.bjpowernode.utils.SqlSessionUtils;
import org.apache.ibatis.session.SqlSession;

import java.util.List;

/**
 * @项目 code-mybatis
 * @描述 TODO
 * @时间 2021-07-15 22:39
 **/
public class UserDao {
    public List<User> selectAll() {
        SqlSession session = SqlSessionUtils.getSession();
        List<User> list = session.selectList("com.bjpowernode.mapper.UserMapper.selectAll");
        session.close();
        return list;
    }

    public User queryById(int id) {
        SqlSession session = SqlSessionUtils.getSession();
        User user = session.selectOne("com.bjpowernode.mapper.UserMapper.selectUser", id);
        session.close();
        return user;
    }

    public int addUser(User user) {
        SqlSession session = SqlSessionUtils.getSession();
        int result = session.insert("com.bjpowernode.mapper.UserMapper.addUser", user);
        session.commit();
        session.close();
        return result;
    }

    public int updateUserById(User user) {
        SqlSession session = SqlSessionUtils.getSession();
        int result = session.update("com.bjpowernode.mapper.UserMapper.updateUser", user);
        session.commit();
        session.close();
        return result;
    }

    public int deleteUserById(int id) {
        SqlSession session = SqlSessionUtils.getSession();
        int result = session.delete("com.bjpowernode.mapper.UserMapper.deleteUser", id);
        session.commit();
        session.close();
        return result;
    }
}
5.1.5 数据库数据操作映射配置UserMapper.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.bjpowernode.mapper.UserMapper">
    <!-- 查询所有用户 -->
    <select id="selectAll" resultType="com.bjpowernode.domain.User">
        select * from user
    </select>
    <!-- 查询单个用户 -->
    <select id="queryById" resultType="com.bjpowernode.domain.User">
        select * from user where id = #{id}
    </select>
    <!-- 添加用户 -->
    <insert id="addUser" parameterType="com.bjpowernode.domain.User"
            useGeneratedKeys="true">
        insert into user(name,age,sex,address,birth) values(#{name},#{age},#{sex},#{address},#{birth})
    </insert>
    <!-- 更新用户信息 -->
    <update id="updateUserById" parameterType="com.bjpowernode.domain.User">
        update user set name=#{name},age=#{age},sex=#{sex},address=#{address},birth=#{birth} where id=#{id}
    </update>
    <!-- 删除用户 -->
    <delete id="deleteUserById">
        delete from user where id=#{id}
    </delete>
</mapper>
5.1.6 测试类
public class Test {
    public static void main(String[] args) throws Exception {
        UserDao userDao = new UserDao();
        List<User> userList = userDao.selectAll();
        userList.forEach(System.out::println);
    }
}
 

5.2 方式二:接口代理方式

使用Mapper接口的代理方式,根据方法名和映射文件中指令ID进行绑定,动态调用相关指令信息

5.2.1 创建项目修改pom.xml引入相关依赖

在这里插入图片描述

<dependencies>
    <!--引入mybatis的依赖-->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.6</version>
    </dependency>
    <!--引入mysql的驱动包-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.22</version>
    </dependency>
</dependencies>
5.2.2 复制上个项目里面的所有类和配置文件

在这里插入图片描述

删除 dao

5.2.3 创建UserMapper接口
package com.bjpowernode.mapper;

import com.bjpowernode.domain.User;
import com.bjpowernode.utils.SqlSessionUtils;
import org.apache.ibatis.session.SqlSession;

import java.util.List;
import java.util.Map;

public interface UserMapper {
    /**
     * 全查询
     * @return
     */
    public List<User> selectAll();
    /**
     * 根据ID查询一个
     * @return
     */
    public User queryById(int id);
    /**
     * 添加用户
     * @return
     */
    public int addUser(User user);
    /**
     * 修改用户
     * @return
     */
    public int updateUserById(User user);

    /**
     * 删除用户
     * @return
     */
    public int deleteUserById(int id);
}
5.2.4 UserMapper.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.bjpowernode.mapper.UserMapper">
    <!-- 查询所有用户 -->
    <select id="selectAll" resultType="com.bjpowernode.domain.User">
        select * from user
    </select>
    <!-- 查询单个用户 -->
    <select id="queryById" resultType="com.bjpowernode.domain.User">
        select * from user where id = #{id}
    </select>
    <!-- 添加用户 -->
    <insert id="addUser" parameterType="com.bjpowernode.domain.User"
            useGeneratedKeys="true"  keyProperty="aaa">
        insert into user(name,age,sex,address,birth) values(#{name},#{age},#{sex},#{address},#{birth})
    </insert>
    <!-- 更新用户信息 -->
    <update id="updateUserById" parameterType="com.bjpowernode.domain.User">
        update user set name=#{name},age=#{age},sex=#{sex},address=#{address},birth=#{birth} where id=#{id}
    </update>
    <!-- 删除用户 -->
    <delete id="deleteUserById">
        delete from user where id=#{id}
    </delete>
</mapper>
5.2.5 测试类
public class Test {
    public static void main(String[] args) throws Exception {
        SqlSession session = SqlSessionUtils.getSession();
        //获取UserMapper接口的具体代理对象
        UserMapper mapper = session.getMapper(UserMapper.class);

        List<User> userList = mapper.selectAll();
        userList.forEach(System.out::println);
    }
}

6.Mybatis核心配置文件详解

6.1 数据源配置详解

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <!-- JDBC – 这个配置直接使用JDBC 的提交和回滚功能。它依赖于从数据源获得连接来管理
                   事务的生命周期。
               • MANAGED – 这个配置基本上什么都不做。它从不提交或者回滚一个连接的事务。而是让
                   容器(例如:Spring 或者J2EE 应用服务器)来管理事务的生命周期
            -->
            <transactionManager type="JDBC" />
            <!--
           数据源类型:
               UNPOOLED – 这个类型的数据源实现只是在每次需要的时候简单地打开和关闭连接。
               POOLED – 这个数据源的实现缓存了JDBC 连接对象,用于避免每次创建新的数据库连接时都初始
               化和进行认证,加快程序响应。并发WEB 应用通常通过这种做法来获得快速响应。
               JNDI – 这个数据源的配置是为了准备与像Spring 或应用服务器能够在外部或者内部配置数据
               源的容器一起使用,然后在JNDI 上下文中引用它
        -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&useSSL=false&characterEncoding=UTF8&serverTimezone=UTC" />
                <property name="username" value="root" />
                <property name="password" value="123456" />
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>
</configuration>

6.2 引入db.properties配置文件详解

6.2.1 创建db.properties文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&useSSL=false&characterEncoding=UTF8&serverTimezone=UTC
jdbc.username=root
jdbc.password=123456
6.2.2 修改mybatis.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>

    <!--外部配置文件配置-->
    <properties resource="db.properties"></properties>
    <!--环境配置-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>
</configuration>

6.3 别名优化详解

【注意】:别外配置必须是在enviroments之前

<typeAliases>
    <!-- 为指定类型指名 别名  使得在mapper映射文件中可以简化引用
    <typeAlias type="com.bjpowernode.domain.User" alias="User"/>
    -->
    <!-- 为某个包下的所有类指定别名  默认别名是对应的类名 -->
    <package name="com.bjpowernode.domain"/>
</typeAliases>

6.4 日志配置详解:有可能会报找不到日志类的异常

日志,就是将程序运行时,一些关键信息进行保存文件中的操作.当问题发生时,是无法观察控制台,将关键的信息保存在文件,便于后期检查。

日志主要分为以下级别:

debug调试模式时进行记录的调试信息

info具体信息

error 发生异常时可以进行记录的方法

debug级别最低

info级别高于DEBUG

error级别最高

6.4.1 引入日志相关jar包
<!-- 日志相关依赖 -->
<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.13.3</version>
</dependency>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
6.4.2 创建log4j.properties的日志配置文件
# 全局日志配置
log4j.rootLogger=DEBUG, stdout,D,E
# MyBatis 日志配置
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# 控制台输出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

### 输出DEBUG 级别以上的日志到=E://logs/error.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = E://logs/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

### 输出DEBUG 级别以上的日志到=E://logs/error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =E://logs/error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n
6.4.3 修改mybatis.xml配置文件
<settings>
    <setting name="logImpl" value="LOG4J"/>
</settings>
6.4.4 测试并查看E盘的logs目录

在这里插入图片描述

7.Mybatis映射配置文件详解

7.1 结果映射

在mybatis中,默认将查询的结果返回一个实体类对象,查询结果和实体类根据结果列别名和类中属性名一一对应。当查询结果列别名和类属性名不一致。解决方案有两种:

l 将别名修改为一致

l 使用mybatis内置结果映射处理器

7.1.1 在映射文件中新增映射关系

在这里插入图片描述

<!--
    id 结果映射关系的唯一标识
    type 对应的类
-->
 <resultMap id="BaseResultMap" type="com.example.domain.User">
     <!-- id 一般在逻辑上标识 是主键
         column : 查询结果的列别名
         property : 类中属性名
     -->
     <id column="id" property="id"/>
     <!--
         result 和 id 是没有区别的
         只是  id  用于标识是主键
     -->
     <result column="user_name" property="name" />
     <result column="pwd" property="pwd" />
 </resultMap>
 <!-- 查询一个 -->
 <select id="selectOne" resultMap="BaseResultMap">
     select  id,name as user_name,pwd from user where id = #{id}
 </select>
 
7.1.2 注意

l 映射关系中,column查询结果的列别名,不是原始列名

l 在查询指令中,resultMap属性,指向定义的映射关系的ID

7.2 #和$(重点)

在mybatis中,会将开发者定义的sql进行解析,解析分为了2类sql:

静态sql ,在解析时,直接将参数拼接到sql中,这种就是静态sql conn.createStatement()

动态sql,在解析时,会使用?这个占位符,替代参数

conn.prepareStatement()

这两种解析方式,mybatis是根据${}和#{}进行区分的

${}的sql是静态sql ${uName}

#{}的sql是动态sql #{uName} #{arg0} #{param1} #{0}

不论是静态sql,还是动态sql都能获取传递参数,但是${}是使用的字符拼接,#{}使用PreparedStatement进行参数的预处理。

在一定程度上说,${}能实现的功能,#{}都能实现,并且由于#{}PreparedStatement进行SQL的预处理,一定程度上可以防止SQL注入攻击。所以在开发中,能使用#{}尽量使用#{}。
PreparedStatement预处理的本质是将参数进行转换为字符串,当做参数字符串处理。所以,如果参数信息是一个特殊的关键字,例如: 数据库名,表名,函数名,内置关键字,使用预处理,则关键字转为了字符串,无效了,此时必须使用字符串拼接。

7.2.1 $示例
<!-- 查询一个 -->
<select id="selectOne" resultMap="BaseResultMap">
    select  id,name as user_name,pwd from user where id = ${id}
</select>
7.2.2 #示例
<select id="selectUser" resultType="com.example.domain.User">
    select * from user where id = #{id}
</select>

7.3 SQL片段

在开发中,需要书写大量的SQL语句,并且这些SQL数据很大部分内容是重复内容,基于这样的情况,mybatis提供模板,可以在模板中定义需要使用sql语句的部分内容,然后在需要使用到这个部分内容的地方直接引入。

使用sql标签,定义SQL片段,使用include引入,sql片段

<sql id="BASE_COLUMN">
    id,name,pwd
</sql>
<!-- 查询单个用户 -->
<select id="selectUser" resultType="com.example.domain.User">
    select
        <include refid="BASE_COLUMN">
        </include>
    from user where id = #{id}
</select>

注意:

l 在sql片段中 id,是这个sql片段的唯一标识

l 在引入时,include标签中,refid关联sql片段的id

7.4 模糊查询(重点)

在mybatis中,模糊查询主要有3种方式:

l like concat(‘%’,#{关键字},‘%’) 推荐

l like ‘%${关键字}%’

l mybatis推荐的bind标签

7.4.1 方案一[推荐]
<!-- 查询一个 -->
<select id="selectByName" resultMap="BaseResultMap">
    select
    <include refid="BASE_COLUMN" />
    from user where name like concat('%',#{name},'%')
</select>
7.4.2 方案二[拼接]

使用$存在sql注入攻击安全问题

<select id="selectByName" resultMap="BaseResultMap">
    select
    <include refid="BASE_COLUMN" />
    from user where name like '%${name}%'
</select>
7.4.3 方案三[bind]
<select id="selectByName" resultMap="BaseResultMap">
    <bind name="keyWord" value="new String('%'+name+'%')"/>
    select
    <include refid="BASE_COLUMN" />
    from user where name like  #{keyWord}
</select>

7.5 多个参数问题(重点)

当mybatis传递参数存在多个时,mybatis支持三种方案:

argx 形式:arg 表示参数,x表示参数索引

paramX形式: param表示参数,x表示第几个参数

使用注解为参数取别名 @Param

方案三

/**
 * 根据名字和ID进行搜索
 * @param name
 * @param id
 * @return
 */
public List<User> selectByNameAndId(@Param("name") String name, @Param("id") Integer id);

<select id="selectByNameAndId" resultMap="BaseResultMap">
    select
    <include refid="BASE_COLUMN" />
    from user where name like  concat('%',#{name},'%') and id = #{id}
</select>

7.6 分页查询(重点)

在mybatis中,分页查询解决方案最本质上回归原生SQL进行处理。

l 查询总页数,使用limit函数进行查询相应的数据

l mybatis中,提供了一个RowBounds这个类,用于进行分页查询数据

l 使用分页插件:PageHelper(推荐)

7.6.1 方案一[rowBounds]

使用RowBounds这种分页查询方式,在实际开发中是远远不够使用的,这种方式只是返回的相应的数据,总数据条数,页数,分页起始页都需要开发者自己处理。

/**
 * mybatis 推荐的分页方式
 * @param rowBounds
 * @return
 */
public List<User> selectPage(RowBounds rowBounds);

<!-- 查询所有 -->
<select id="selectPage" resultType="User">
    select    <include refid="BASE_COLUMN" />  from user
</select>

public class Test {
    public static void main(String[] args) throws Exception {
        SqlSession session = SqlSessionUtil.getSession();
        //获取UserMapper接口的具体代理对象
        UserMapper mapper = session.getMapper(UserMapper.class);
        RowBounds rowBounds = new RowBounds(0,5);
        List<User> users = mapper.selectPage(rowBounds);
        users.forEach((u) -> System.out.println(u));
        session.commit();
        session.close();
    }
}
7.6.2 方案二[分页插件]

市面主流分页插件: PageHelper

导入PageHelper的相关jar包

<!-- 分页插件jar -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.2.0</version>
</dependency>

或者

jsqlparser-3.2.jar
pagehelper-5.2.0.jar

在mybatis核心配置文件中开启插件

<!-- 插件 -->
<plugins>
    <!--
        在mybatis 为了让开发者能够更好扩展mybatis功能 ,mybatis提供了插件接口
        这样开发者可以通过实现接口 重写方法,对mybatis 功能进行扩展(通过拦截进行扩展)
     -->
    <!--
        分页插件 :PageHelper
     -->
    <!-- 配置分页插件 -->
    <plugin interceptor="com.github.pagehelper.PageInterceptor" />
</plugins>

在代码显示声明后面紧邻(一次)查询语句需要使用分页

在PageHelper,每次开启分页,只对最近的一次查询有效。如果要多次分页查询,需要多次开启。

PageHelper是利用,拦截器原理,拦截最近的一次查询,然后在其基础拼接分页信息。PageHelper只对最近一次查询有效,这种模式,对本身的程序没有侵入,所以,本身程序该怎么写还是怎么写。

public static void main(String[] args) throws Exception {
    SqlSession session = SqlSessionUtil.getSession();
    //获取UserMapper接口的具体代理对象
    UserMapper mapper = session.getMapper(UserMapper.class);
    //开启分页   2  页码  5  每页数据条数
    Page<User> userPage = PageHelper.startPage(2, 5);
    //进行查询  PageHelper 会对查询进行拦截处理  将将要进行的查询语句进行分页处理
    mapper.selectAll();
    System.out.println("总页数:"+userPage.getPages());
    System.out.println("页码:"+userPage.getPageNum());
    System.out.println("每页条数:"+userPage.getPageSize());
    System.out.println("总条数:"+userPage.getTotal());
    List<User> users = userPage.getResult();
    users.forEach((u) -> System.out.println(u));
    session.commit();
    session.close();
}

7.7 新增自增长(重点)

在mybatis中,支持数据库的自增长功能,因为在某些特殊的业务场景中,当前数据的ID,可能是另外某些数据的业务ID。

例如:

订单:总订单和子订单

每个子订单会有总订单ID

先插入总订单,并且要获取总订单ID

插入子订单

Mybatis在insert指令中,提供了2个属性:useGeneratedKeys、keyProperty、keyColumn

useGeneratedKeys:表示使用数据库自增长

keyProperty:自增长的列对应的类中的属性

keyColumn:自增长的列

Mybatis会自动将增长的值,封装到传入参数的属性中。

<insert id="insert" parameterType="com.example.domain.User"  useGeneratedKeys="true" keyProperty="id" keyColumn="id">
    insert  into user (name,pwd) value (#{name},#{pwd})
</insert>

public static void main(String[] args) throws Exception {
    SqlSession session = SqlSessionUtil.getSession();
    //获取UserMapper接口的具体代理对象
    UserMapper mapper = session.getMapper(UserMapper.class);
    User user = new User();
    user.setName("Lucy");
    user.setPwd("123456");
    System.out.println("新增后user对象:" + user.getId());
    session.commit();
    session.close();
}

注意:

自增长值,会被自动封装在对象参数中

7.8 动态SQL(重点)

动态SQL是指根据不同的参数,产生不同的SQL语句。这样的SQL就是动态SQL。

Mybatis提供了一下标签,用于动态SQL的生成:

if

foreach

choose

where: 可以去除前置多余的and、or等挂关键字;不能去除后置

set

trim

7.8.1 if标签

在if标签中,test属性是必须有,test属性值是一个表达式,如果表达式值为true,则if标签包裹的内容会拼接在当前sql上。

and 并且

or 或者

== 等于

!= 不等于

示例:

<!-- 条件查询 -->
<select id="selectList" resultType="com.example.domain.User">
    select  <include refid="BASE_COLUMN" /> from user where 1=1
    <if test="name != null and name !=''">
        and name like concat('%',#{name},'%')
    </if>
</select>
7.8.2 foreach

循环标签,循环标签多用于批量操作。

例如:批量新增,批量删除等等

collection 待循环的容器

item 指代 每次循环容器中的元素

open 开始循环是拼接字符串

close 循环结束拼接字符串

separator 每次循环之间拼接的字符串

index 循环索引

(#{user.name},#{user.pwd}) delete from user where id in #{id} choose

多条件分支标签:choose.

在choose标签,自上而下执行when中表达式,如果表达式为true,则将相应的字符串拼接在sql后面,且终止判断。如果所有的表达式都为false,则将otherwise中字符串,拼接在sql后面

select from user where age > 19 age > 18 18 > age

List selectByAge(@Param(“age”) Integer age);

public static void main(String[] args) {

    SqlSession session = SqlSessionUtil.getSession();
    //获取UserMapper接口的具体代理对象
    UserMapper mapper = session.getMapper(UserMapper.class);
    Integer age = 17;
    List<User> users = mapper.selectByAge(age);
    session.commit();
    session.close();
}
7.8.3 where

在mybatis中,存在sql条件,当有多个sql条件时,需要处理and关键字问题,因为where后面的第一个条件不需要and,解决方案:

l 在where后面 拼接 1=1 类似的条件,这样其他条件都不是第一个条件,都需要拼接and

l mybatis 提供了where标签,取代where关键字,默认去掉第一个条件 and

注意:

建议,只在查询语句中使用where标签,因为当where标签中的条件都不成立时,会没有where关键字。

select from user and name like concat('%',#{name},'%') and sex = #{sex} delete from user name = #{name}
7.8.4 set标签

set标签是取代sql语句中的set关键字。set表示后面数据的更新。各个字段,需要使用逗号分隔。

set标签可以去掉最后一个逗号。

update user name = #{name}, pwd = #{pwd}, where id = #{id}

8. 缓存

mybatis为了提高查询效率、性能。提供了缓存,缓存分为2类:

l 一级缓存

l 二级缓存

一级缓存:是指SqlSession级别的缓存,在同一个SqlSession,同样的SQL语句只会执行一次,不是第一次执行的SQL会从缓存中获取数据。

二级缓存: 是指SqlSessionFactory级别的缓存,在同一个SqlSessionFactory中,同样的SQL语句,只会执行一次,不是第一次执行的SQL会从缓存中获取数据。

注意:

不论一级缓存还是二级缓存,否是JVM中缓存。当服务器有多台时,缓存容易发生无效,数据发生了更新。缓存没有更新。在实际使用,如果数据可能发生比较频繁的更新不建议使用mybatis。

8.1 一级缓存

public static void main(String[] args) {
SqlSession session = SqlSessionUtil.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
//第一次查询
System.out.println(“==================================”);
List users = mapper.selectAll();

//第二次查询
session.commit();
session.close();
session = SqlSessionUtil.getSession();
mapper = session.getMapper(UserMapper.class);
System.out.println("************************************");
users = mapper.selectAll();
users.forEach(user -> System.out.println(user.getName()));
session.commit();
session.close();

}

8.2 二级缓存

二级缓存需要在映射文件中,开启缓存:

并且被缓存的数据需要支持,序列化

9.Mybatis注解

mybatis配置虽然相对简单,但是还是麻烦。所以,mybatis为了简化配置,也提供了注解。

常用注解:

l @Select

l @Insert

l @Delete

l @Update

public interface UserMapper {
@Insert(“insert into user (name,pwd) value (#{name},#{pwd})”)
Integer insert(User user);

@Delete("delete  from user where id = #{id}")
Integer delete(Integer id);

@Select("select  id,name,pwd from user ")
List<User> selectAll();


/* 动态sql 写法 */
@Select("<script>select  id,name,pwd from user <where> <if test='id != null'>id > 18</if> </where></script>")
List<User> selectList(@Param("age") Integer age);

}

10.逆向工程

1、 快速帮我们生成实体类

2、生成单表的crud

11.联表查询(难点)

11.1 多表查询解决方案

1.写实体类,通过定义实体类,将多表的所有字段都涵盖,解决多表查询问题,但是,这种方案只能解决多对一,但是无法解决一对多问题。

例如:写实体类可以解决,一个学生类中班级信息问题,但是无法解决班级类中学生信息问题。

学生信息是一个数组。

2.多次查询。将需要结果拆分成多个简单的单表查询,连续查询多次。使用Java代码处理查询结果。

例如:学生班级信息。

查询某个学生学生信息及其班级信息。

根据ID查询学生信息

从学生信息中获取班级ID

根据班级ID查询班级信息

查询所有学生的信息及其班级信息

查询所有学生(100)(注意学生数据量)

查询所有班级(Map 使用ID 作为key 对象就是value)

循环学生,获取班级ID,从班级数据中,获取班级信息

注意:

mybatis解决联表查询的方法就是基于以上方案

CREATE TABLE student (
id int(11) NOT NULL AUTO_INCREMENT,
sname varchar(255) DEFAULT NULL,
cid int(11) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE classes (
id int(11) NOT NULL,
cname varchar(255) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

11.2 多对一

11.2.1 将查询结果进行封装
<?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.example.mapper.StudentMapper">

    <resultMap id="STUDENT_CLASSES_MAP" type="Student">
        <result column="id" property="id" />
        <result column="sname" property="sname" />
        <result column="cid" property="cid" />
        <!--
            mybatis 提供的关联标签 多对一
            property :当前类中关联属性
            javaType : 属性所属的类
         -->
        <association property="classes" javaType="com.example.domain.Classes">
            <result column="cid" property="cid" />
            <result column="cname" property="cname" />
        </association>
    </resultMap>

    <!-- 使用实体类的思想 -->
    <select id="selectOne" resultType="map">
        select  s.id,s.sname,s.cid,c.cname from student s left  join classes c on s.cid = c.cid
        where s.id = #{id}
    </select>

    <select id="selectOne2" resultMap="STUDENT_CLASSES_MAP">
        select  s.id,s.sname,s.cid,c.cname from student s left  join classes c on s.cid = c.cid
        where s.id = #{id}
    </select>

</mapper>



package com.example.mapper;

import com.example.domain.Student;
import org.apache.ibatis.annotations.Param;

import java.util.Map;

public interface StudentMapper {

    Map<String,Object> selectOne(@Param("id") Integer id);

    Student selectOne2(int i);
}

package com.example.test;

import com.example.domain.Classes;
import com.example.domain.Student;
import com.example.mapper.StudentMapper;
import com.example.util.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;

import java.util.Map;

/**
 * @Description:

 * @date 2020-09-24 09:32
 */
public class Test {

    public static void main(String[] args) {
        SqlSession session = SqlSessionUtil.getSession();
        StudentMapper mapper = session.getMapper(StudentMapper.class);
        //自己将map数据处理成封装的对象
        Map<String, Object> map = mapper.selectOne(1);
        Student student = new Student();
        student.setId((Integer) map.get("id"));
        student.setSname(map.get("sname").toString());
        student.setCid((Integer) map.get("cid"));
        Classes classes = new Classes();
        classes.setCid((Integer) map.get("cid"));
        classes.setCname(map.get("cname").toString());
        student.setClasses(classes);
        System.out.println("转化的student:"+student);
        System.out.println(map);
        System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
        //直接获取mybatis的封装对象
        Student student1 = mapper.selectOne2(1);
        System.out.println(student1);

        session.commit();
        session.close();
    }
}

package com.example.domain;

import lombok.Data;

/**
 * @Description: 学生类
 * @date 2020-09-24 10:55
 */
@Data
public class Student {

    private Integer id; //学生ID

    private String sname;//学生名称

    private Integer cid;//班级ID

    private  Classes classes;//班级信息
}

package com.example.domain;

import lombok.Data;

import java.util.List;

/**
 * @Description: 班级类
 * @date 2020-09-24 10:56
 */
@Data
public class Classes {

    private Integer cid;//班级ID

    private String cname;//班级名称

    private  List<Student> students; //学生信息
}
11.2.2 进行多次查询
<resultMap id="STUDENT_CLASSES_MAP2" type="Student">
        <result column="id" property="id" />
        <result column="sname" property="sname" />
        <result column="cid" property="cid" />
        <association property="classes" javaType="com.example.domain.Classes" select="com.example.mapper.ClassesMapper.selectById" column="cid" />
    </resultMap>

<select id="selectAll" resultMap="STUDENT_CLASSES_MAP2">
        select
            s.id,s.sname,s.cid
        from
            student s
    </select>

<?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.example.mapper.ClassesMapper">

    <select id="selectById" resultType="Classes">
        select cid ,cname from classes where cid = #{cid}
    </select>
</mapper>

 List<Student> selectAll();

package com.example.test;

import com.example.domain.Student;
import com.example.mapper.StudentMapper;
import com.example.util.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;

import java.util.List;

/**
 * @Description:
 * @date 2020-09-24 09:32
 */
public class Test {

    public static void main(String[] args) {
        SqlSession session = SqlSessionUtil.getSession();
        StudentMapper mapper = session.getMapper(StudentMapper.class);
        List<Student> students = mapper.selectAll();
        System.out.println(students);
        session.commit();
        session.close();
    }
}

11.3 一对多

11.3.1 将查询结果封装
<?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.example.mapper.ClassesMapper">

    <resultMap id="CLASSES_STUDENT_MAP" type="Classes">
        <result column="cid" property="cid" />
        <result column="cname" property="cname" />
        <!--
            collection 表示 一对多
                ofType 表示集合中 存储数据类型化
                    result 映射关系
        -->
        <collection property="students"  ofType="com.example.domain.Student">
            <result column="id" property="id" />
            <result column="sname" property="sname" />
            <result column="cid" property="cid" />
        </collection>
    </resultMap>

 <select id="selectById" resultType="Classes">
        select cid ,cname from classes where cid = #{cid}
    </select>

 <select id="selectAll" resultMap="CLASSES_STUDENT_MAP">
        select c.cid ,c.cname,s.id,s.sname from classes c left  join student s on c.cid = s.cid
    </select>
</mapper>



package com.example.mapper;

import com.example.domain.Classes;

import java.util.List;

/**
 * @Description: TODO
 * @date 2020-09-24 10:58
 */
public interface ClassesMapper {

    List<Classes> selectAll();
}

 package com.example.test;

import com.example.domain.Classes;
import com.example.mapper.ClassesMapper;
import com.example.util.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;

import java.util.List;

/**
 * @Description:
 * @date 2020-09-24 09:32
 */
public class Test {

    public static void main(String[] args) {
        SqlSession session = SqlSessionUtil.getSession();
        ClassesMapper mapper = session.getMapper(ClassesMapper.class);
        List<Classes> classes = mapper.selectAll();
        System.out.println(classes);
        session.commit();
        session.close();
    }
}
11.3.2 多次查询
<resultMap id="CLASSES_STUDENT_MAP2" type="Classes">
        <result column="cid" property="cid" />
        <result column="cname" property="cname" />
        <!--
            collection 表示 一对多
                ofType 表示集合中 存储数据类型化
                    result 映射关系
                property   关联的属性值
                select 获取property 执行 select指定的查询  将查询结果设置给property 属性
        -->
        <collection property="students" ofType="com.example.domain.Student" select="com.example.mapper.StudentMapper.selectAllByCId" column="cid" />
    </resultMap>

 <select id="selectAll2" resultMap="CLASSES_STUDENT_MAP2">
        select c.cid ,c.cname from classes c
    </select>

    <select id="selectAllByCId" resultType="Student">
        select
            id,sname,cid
        from
            student s where cid = #{cid}
    </select>

package com.example.mapper;

import com.example.domain.Classes;

import java.util.List;

/**
 * @Description: TODO
 * @date 2020-09-24 10:58
 */
public interface ClassesMapper {

    List<Classes> selectAll();

    List<Classes> selectAll2();

}

package com.example.test;

import com.example.domain.Classes;
import com.example.mapper.ClassesMapper;
import com.example.util.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;

import java.util.List;

/**
 * @Description:
 * @date 2020-09-24 09:32
 */
public class Test {

    public static void main(String[] args) {
        SqlSession session = SqlSessionUtil.getSession();
        ClassesMapper mapper = session.getMapper(ClassesMapper.class);
        List<Classes> classes = mapper.selectAll2();
        System.out.println(classes);
        session.commit();
        session.close();
    }
} 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/170814.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

hologres 索引与查询优化

hologres 优化部分 1 hologres 建表优化1.1 建表中的配置优化1.1 字典索引 dictionary_encoding_columns1.2 位图索引 bitmap_columns1.2.2 Bitmap和Clustering Key的区别 1.3 聚簇索引Clustering Key 1 hologres 建表优化 1.1 建表中的配置优化 根据 holo的 存储引擎部分的知…

【Python测试开发】:切换窗口和表单

一、多窗口切换 浏览器打开的窗口其实会有一个叫做句柄的概念。 句柄就类似于每一个标签页的ID一样&#xff0c;具有唯一性。 1.1 语法 获取当前窗口句柄&#xff0c;注意后面没有括号哦~ driver.current_window_handle获取所有窗口句柄&#xff0c;结果以列表格式存储&am…

Simulia 2022 新功能

增材制造 达索系统增材制造解决方案实现了端到端一体化全流程解决方案&#xff0c;可以实现从原材料研究到创成式设计、工艺设计、工艺仿真仿真、并且还延续到增材制造完成后的热处理、线切割等工艺&#xff0c;涵盖了各个方面的内容。 达索系统针对增材制造各个环节在每一个…

一整个分析模型库,大数据分析工具都这么玩了吗?

一整个分析模型库&#xff0c;100张BI报表&#xff0c;覆盖销售、财务、采购、库存等多个分析主题。只需对接ERP&#xff0c;就能自动生成BI报表&#xff0c;完成对海量数据的系统化分析。现在大数据分析工具都发展到这种程度了吗&#xff1f; 放眼看去&#xff0c;现阶段能做…

在线客服系统源码 聊天记录实时保存 附带完整的搭建教程

在线客服系统是一个企业网站进行网络营销的最重要的工具。企业进行网络宣传后&#xff0c;会有很多访客进入到网站&#xff0c;这时候网站就需要有在线客服人员进行接待&#xff0c;及时的与访客进行沟通&#xff0c;才能留住访客&#xff0c;变流量为销量。 在线客服系统可以…

销售团队可以借助CRM系统做什么?

销售主管都想有一支效率高、质量高的销售团队&#xff0c;无论对于初创企业还是大型企业销售团队都是企业盈利的主力部门&#xff0c;直接为企业带了业绩。如何提升销售团队水平&#xff1f;离不开CRM系统的辅助&#xff0c;CRM软件能为销售团队提供哪些支持&#xff1f;下面我…

队列OJ--循环队列

目录 题目链接&#xff1a;622. 设计循环队列 - 力扣&#xff08;LeetCode&#xff09;​​​​​ 题解&#xff1a; ​编辑 代码实现&#xff1a; 完整代码&#xff1a; 题目链接&#xff1a;622. 设计循环队列 - 力扣&#xff08;LeetCode&#xff09;​​​​​ 题解&#x…

高质量简历写作求职通关-前言

(点击即可收听) 在如今大内卷的环境下 无论哪个行业,都竞争激烈 2023年的毕业生人数已达到1158万人&#xff0c;本科毕业人数约700万人&#xff0c;研究生毕业人数约119万人 其中,北京市的就有28.5万名高校毕业生中&#xff0c;硕博毕业生人数首次超过本科生... 可见学历的通胀…

各类Linux操作系统如何选择?

各类Linux操作系统如何选择&#xff1f; 企业级应用&#xff1a;RHEL/CentOS 桌面平台&#xff1a;Ubuntu 开源服务器&#xff1a;CentOS 1.1 RedHart 1.1.1RHEL RHEL是指Red Hat Enterprise Linux&#xff0c;是由Red Hat公司开发和维护的一款商业Linux操作系统。它是基于…

【Unity细节】如何调节标签图标的大小(select icon)—标签图标太大遮住了物体

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 秩沅 原创 &#x1f636;‍&#x1f32b;️收录于专栏&#xff1a;unity细节和bug &#x1f636;‍&#x1f32b;️优质专栏 ⭐【…

teambition迁移云效

由于TB(行云)停止运营了&#xff0c;可惜了&#xff0c;非常好用的一个工具&#xff0c;项目管理&#xff0c;代码管理&#xff0c;自动化构建等&#xff0c;都支持。现需要切换到云效(https://codeup.aliyun.com/)。这个工作量确实挺大的&#xff0c;像我有N个公司*N个项目的&…

企业要满足什么条件才能实施CRM系统?

CRM的作用相信大家也所有了解&#xff0c;但并不是所有的企业都适合实施CRM。或者说&#xff0c;大部分企业实施CRM并不会100%的成功。那么&#xff0c;企业实施CRM的条件是什么&#xff1f;下面我们就来说一说。 1、业务规模 如果您的客户数量较少&#xff0c;没有复杂的客户…

.skip() 和 .only() 的使用

.skip() 和 .only() 的使用 说明 在做自动化测试中&#xff0c;跳过执行某些测试用例&#xff0c;或只运行某些指定的测试用例&#xff0c;这种情况是很常见的Cypress中也提供了这种功能 如何跳过测试用例 通过describe.skip() 或者 context.skip() 来跳过不需要执行的测试…

微信表情太大怎么缩小?一分钟教会你!

在微信的较早版本中&#xff0c;单个表情的最大体积限制为500KB&#xff0c;而在后续版本中&#xff0c;这一限制已经放宽。目前&#xff0c;微信允许上传的单个表情最大体积为2MB。所以&#xff0c;我们只需要把图片或者GIF缩小到2MB即可&#xff0c;下面就向大家介绍三种实用…

2023最新国内外项目进度管理软件排行榜(推荐)

介绍8款优秀的在线项目管理软件&#xff0c;其中进度猫、Trello、Clarizen、Asana、MeisterTask、ClickUp和Wrike都是以甘特图为核心进行项目管理&#xff0c;而monday则是低代码项目管理软件&#xff0c;提供一站式的工作记录和管理。这些软件都可以帮助项目经理更有效地管理项…

打工人必备!6个超级实用的办公软件,让你高效完成工作

在现代职场中&#xff0c;办公软件已经成为我们工作中不可或缺的利器&#xff0c;能够让我们的工作变得更加高效和便捷。今天就给大家分享6个超级实用的办公软件&#xff0c;让你高效完成工作&#xff01; 1、滴答清单&#xff08;待办事项软件&#xff09; 滴答清单是一款功能…

什么样的企业可以使用免费版的CRM?

市面上大部分的免费CRM不需要付费即可使用&#xff0c;但是对于使用人数和功能进行了部分限制。下面我们就来说说&#xff0c;免费CRM的适用对象是谁&#xff1f; 1、初创/小微企业 这种小微企业没有太多的资金&#xff0c;也没有复杂的客户管理需求&#xff0c;仅仅需要一款…

最新企业服务总线ESB的国内主要厂商和开源厂商排名,方案书价格多少

企业服务总线ESB是什么&#xff1f; ESB平台&#xff08;企业服务总线&#xff0c;Enterprise Service Bus&#xff09;是一种企业级集成平台&#xff0c;它提供了一种开放的、基于标准的消息机制&#xff0c;通过简单的标准适配器和接口&#xff0c;来完成粗粒度应用&#xff…

深度学习之生成唐诗案例(Pytorch版)

主要思路&#xff1a; 对于唐诗生成来说&#xff0c;我们定义一个"S" 和 "E"作为开始和结束。 示例的唐诗大概有40000多首&#xff0c; 首先数据预处理&#xff0c;将唐诗加载到内存&#xff0c;生成对应的word2idx、idx2word、以及唐诗按顺序的字序列。…

【HarmonyOS】低代码平台组件拖拽使用技巧之常用基础组件(上)

【关键字】 HarmonyOS、低代码平台、组件拖拽、常用基础组件、基础容器 1、写在前面 之前是花了一些时间介绍了在低代码平台中滚动容器、网格布局、页签容器、列表这几种容器的拖拽技巧及使用方法&#xff0c;今天我会继续来介绍咱们在应用开发中可能会经常用到的一些基础容器…