Java零基础-Mybatis篇

news2024/12/28 4:35:35

【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();
    }
} 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于SSM的网盘管理系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

企业办公文件数据防泄密系统 | 文件、文档、设计图纸、源代码、音视频等核心数据资料防止外泄!

天锐绿盾防泄密软件采用智能透明加密技术&#xff0c;对文件、文档、图纸、源代码、音视频等数据进行加密保护&#xff0c;防止数据泄露。这种加密技术是内核级透明加密技术&#xff0c;可以在不影响员工正常工作的情况下&#xff0c;对需要保护的数据进行加密操作。 PC端访问地…

DCDC同步降压控制器SCT82A30\SCT82630

SCT82A30是一款100V电压模式控制同步降压控制器&#xff0c;具有线路前馈。40ns受控高压侧MOSFET的最小导通时间支持高转换比&#xff0c;实现从48V输入到低压轨的直接降压转换&#xff0c;降低了系统复杂性和解决方案成本。如果需要&#xff0c;在低至6V的输入电压下降期间&am…

一种可度量的测试体系-精准测试

行业现状 软件行业长期存在一个痛点&#xff0c;即测试效果无法度量。通常依赖于测试人员的能力和经验&#xff0c;测试结果往往不可控&#xff0c;极端情况下同一个业务功能&#xff0c;即使是同一个人员在不同的时间段&#xff0c;测试场景和过程也可能不一致&#xff0c;从而…

十大最常用电子元器件基础知识

对于电子行业的工程师来说&#xff0c;电子元件&#xff0c;就像人们入口的大米&#xff0c;每天都需要接触和使用。但事实上&#xff0c;许多工程师可能都不完全理解里面的门道。以下是工程师常用的十种电子元件以及相关的基本概念和知识。让我们一起学习了解一下。 一&#…

护眼灯亮度多少合适?亮度适合学生的护眼台灯推荐

护眼灯亮度满足国AA级标准就好了。可以肯定的是&#xff0c;护眼灯一般可以达到护眼的效果。 看书和写字时&#xff0c;光线应适度&#xff0c;不宜过强或过暗&#xff0c;护眼灯光线较柔和&#xff0c;通常并不刺眼&#xff0c;眼球容易适应&#xff0c;可以防止光线过强或过…

【HarmonyOS】低代码平台组件拖拽使用技巧之页签容器

【关键字】 HarmonyOS、低代码平台、Tabs、TabContent、页签导航 1、写在前面 前面几篇分别介绍了低代码平台中的堆叠容器、滚动容器、网格布局等三种容器的使用&#xff0c;实际开发中我们经常会有这样的需求&#xff0c;页面底部是三个Tab按钮点击时会分别切换不同的视图内…

【点云上采样】基于移动最小二乘(MLS)的上采样

文章目录 声明简介代码参考 声明 示例结果比较奇怪&#xff0c;可能是参数没调好&#xff0c;如有问题&#xff0c;望指正&#xff01; 简介 基于MLS&#xff08;Moving Least Squares&#xff09;的上采样是一种常用的点云处理方法&#xff0c;用于增加稀疏点云数据的密度和…

「Qt Widget中文示例指南」如何创建一个计算器?(一)

Qt 是目前最先进、最完整的跨平台C开发工具。它不仅完全实现了一次编写&#xff0c;所有平台无差别运行&#xff0c;更提供了几乎所有开发过程中需要用到的工具。如今&#xff0c;Qt已被运用于超过70个行业、数千家企业&#xff0c;支持数百万设备及应用。 本文将展示如何使用…

Django 入门学习总结8-管理页面的生成

修改polls/admin.py文件为&#xff1a; from django.contrib import admin from .models import Choice, Question class ChoiceInline(admin.StackedInline): model Choice extra 3 class QuestionAdmin(admin.ModelAdmin): fieldsets [ (None, {&q…

如何理解String是不可变的

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a;每天一个知识点 ✨特色专栏&#xff1a…

天猫超市电商营销系统:无代码开发实现API连接集成

无代码开发实现天猫超市与电商系统的高效连接 天猫超市&#xff0c;作为天猫推出的网络零售超市&#xff0c;为广大网购消费者提供了一站式的购物服务。而通过无代码开发的方式&#xff0c;天猫超市能够实现与各种电商系统的连接和集成&#xff0c;这种方式无需进行繁琐的API开…

最前端|低代码平台轻松设计可视化图表【内含网站资源】

在前端设计中&#xff0c;我们经常需要使用可视化图表来呈现数据和信息。然而&#xff0c;每次都要自己从头开始设计图表未免太过繁琐。为了解决这个问题&#xff0c;我们调研了low code平台上的可视化图表功能。 本篇文章为大家带来以下问题的解答&#xff1a; &#xff08;1&…

Spark---介绍及安装

一、Spark介绍 1、什么是Spark Apache Spark 是专为大规模数据处理而设计的快速通用的计算引擎。Spark是UC Berkeley AMP lab (加州大学伯克利分校的AMP实验室)所开源的类Hadoop MapReduce的通用并行计算框架&#xff0c;Spark拥有Hadoop MapReduce所具有的优点&#xff1b;但…

python数据结构与算法-07_哈希表

哈希表 不知道你有没有好奇过为什么 Python 里的 dict 和 set 查找速度这么快呢&#xff0c;用了什么黑魔法吗&#xff1f; 经常听别人说哈希表(也叫做散列表)&#xff0c;究竟什么是哈希表呢&#xff1f;这一章我们来介绍哈希表&#xff0c;后续章节我们会看到 Python 中的字…

申请注册苹果iOS企业级开发者证书需要公司拥有什么规模条件

在全球范围内&#xff0c;iOS应用市场的规模和影响力不断增长&#xff0c;企业级应用在其中扮演着重要角色。为了在苹果设备上开发和发布内部应用&#xff0c;企业需要获取苹果的iOS企业级开发者证书。文章旨在探讨企业在申请此证书时需要满足的条件、注册流程&#xff0c;以及…

QT 搭建opencv 环境

1. 准备工具CMake 一、CMake介绍 CMake是一个被广泛使用的、开源免费并且完全跨平台的构建工具&#xff0c;可以用简单的语句来描述所有平台的安装(编译过程)。它能够输出各种各样的makefile或者project文件&#xff0c;能测试编译器所支持的C特性&#xff0c;类似UNIX下的aut…

代码随想录 11.20 || 单调栈 LeetCode 503.下一个更大元素Ⅱ、42.接雨水

503.下一个更大元素Ⅱ 本题与 496.下一个更大元素Ⅰ 如出一辙&#xff0c;求在给定一维数组中&#xff0c;每个元素右起第一个更大元素。不同之点在于&#xff0c;本题要求将一维数组视为首尾相连的&#xff0c;最后一个元素的下一个元素是第一个元素。问题的重点在于 如何使用…

正版软件|PhotoPad 照片/图像编辑工具

编辑照片/图像从未如此简单&#xff01; 关于PhotoPad PhotoPad是本世纪最稳定、最易用且功能最全面的照片编辑器之一。 轻松编辑数码照片和其他图片。支持所有流行的图像格式快速裁剪、旋转、调整大小和翻转照片。 PhotoPad旨在快速打开和编辑照片&#xff0c;轻松无忧&…

Echarts设置环形图中心文字

方式一&#xff1a;title 通过在最外层设置title 配置项 title 同时也是环形图标题的配置项&#xff0c;只是通过left和top强制让其移动到环形图中间位置&#xff1b;提前设置好环形图 title: {// 图形标题(如果想要换行则使用ES6 模板字符串)// 例如&#xff1a; 示例// 这…