Mybatis-获取参数值的两种方式

news2024/11/17 13:43:07

1. ${ } 和 #{ }

MyBatis获取参数值的两种方式:${ } 和 #{ }


对于初学者来说,理解MyBatis中获取参数值的两种方式——#{}${},关键在于明白它们如何影响SQL语句的构建以及为何在安全性、灵活性上有显著差异。下面我将用简单易懂的语言来解释这两者的本质、工作原理及使用注意事项。

1. ${}:字符串拼接

本质${}在MyBatis中被视为字符串替换符。当你在SQL语句中使用${}时,MyBatis会直接将它包围的变量名替换为实际传入的值。这种方式类似于在编程语言中进行字符串拼接操作,即将传入的值作为文本片段插入到SQL语句中。

工作原理

SELECT * FROM users WHERE username = '${username}'

假设传入的username参数为 'zhangsan',MyBatis会将上述SQL语句中的${username}替换为 'zhangsan',生成如下已拼接好的SQL:

SELECT * FROM users WHERE username = 'zhangsan'

特点与注意事项

  • 手动处理引号:由于${}直接参与字符串拼接,因此对于字符串类型或日期类型的字段值,你需要确保传入的值已经正确地加上了单引号。例如,如果你传入的是 'zhangsan' 而不是 zhangsan,MyBatis不会帮你自动加上单引号。
  • SQL注入风险:由于${}直接将传入的值作为原始文本插入到SQL语句中,没有进行任何预处理或转义,因此它不提供对SQL注入攻击的防护。如果传入的值来自不可信的用户输入,恶意用户可能通过构造特定的输入来篡改SQL语句的结构和意图,从而对数据库造成潜在威胁。因此,除非有明确需求且能够确保输入安全,否则应避免在条件查询等涉及用户输入的地方使用${}
  • 适用场景${}通常用于那些需要原样插入到SQL语句中且不会引起SQL注入风险的情况,如动态表名、列名(虽然不推荐),或者在你已经确保传入值安全的情况下。

2. #{}:预编译占位符

本质#{}在MyBatis中是一个预编译占位符。当SQL语句中包含#{}时,MyBatis会将其替换为一个问号(?),并在执行SQL时使用PreparedStatement来设置参数值。这种方式利用数据库的预编译机制,确保了参数值的安全性和类型正确性。

工作原理

SELECT * FROM users WHERE username = #{username}

同样假设传入的username参数为 'zhangsan',MyBatis会将上述SQL语句中的#{username}替换为 ?,然后使用PreparedStatement向数据库发送如下预编译SQL:

SELECT * FROM users WHERE username = ?

同时,MyBatis会将 'zhangsan' 作为参数值,通过PreparedStatement的set方法安全地绑定到SQL语句中的对应位置。

特点与注意事项

  • 自动处理引号:使用#{}时,MyBatis会根据参数的实际类型自动为其添加合适的引号或进行其他必要的类型转换。对于字符串和日期类型,MyBatis会自动加上单引号。所以你不需要担心引号问题,只需传入值即可。
  • 防SQL注入#{}利用预编译机制,确保参数值与SQL语句主体分离,由数据库在执行时负责正确的参数绑定。这从根本上杜绝了SQL注入攻击的可能性,极大地增强了系统的安全性。
  • 适用场景#{}是推荐的参数传递方式,适用于所有需要动态传入值的场景,特别是涉及到用户输入或敏感信息的条件查询、更新操作等。

举个代码例子:

1. ${}:字符串拼接

想象一下,我们有一个简单的executeQuery()方法,它接受一个用户输入的字符串(比如用户名),然后拼接到SQL查询语句中。

public List<User> executeQuery(String unsafeUsername) {
    // 直接拼接字符串,模拟${}的行为
    String sql = "SELECT * FROM users WHERE username = '" + unsafeUsername + "'";

    // 假设这里有代码执行SQL查询并返回结果...
    // executeSQL(sql);

    return null; // 这里仅作演示,实际应返回查询结果
}

现在,假设用户输入了一个恶意的用户名,如:

String maliciousUsername = "' OR '1'='1"; // 模拟SQL注入攻击

调用executeQuery(maliciousUsername)后,生成的SQL语句将是:

SELECT * FROM users WHERE username = '' OR '1'='1'

由于直接拼接字符串,恶意用户成功篡改了SQL语句的结构,导致查询结果不受控制,暴露了SQL注入风险。


2. #{}:预编译占位符

接下来,我们用类似的方式模拟#{}的工作原理,使用PreparedStatement来设置参数值。

public List<User> executePreparedQuery(String safeUsername) {
    String sql = "SELECT * FROM users WHERE username = ?"; // 使用占位符

    try (Connection conn = getConnection();
         PreparedStatement pstmt = conn.prepareStatement(sql)) {

        pstmt.setString(1, safeUsername); // 安全地设置参数值

        // 假设这里有代码执行PreparedStatement并返回结果...
        // ResultSet rs = pstmt.executeQuery();

        // return processResultSet(rs); // 这里仅作演示,实际应处理结果集并返回结果
    } catch (SQLException e) {
        throw new RuntimeException(e);
    }

    return null; // 这里仅作演示,实际应返回查询结果
}

同样,假设用户输入了恶意的用户名:

String maliciousUsername = "' OR '1'='1";

调用executePreparedQuery(maliciousUsername)时,尽管恶意用户名被传递给了PreparedStatement,但由于预编译机制,数据库会将它视为一个独立的字符串值,而不是SQL语句的一部分。生成并执行的SQL实际上等同于:

SELECT * FROM users WHERE username = ''' OR ''1''=''1''

可以看到,尽管用户输入了恶意内容,但预编译机制确保了参数值被正确地转义和隔离,有效地防止了SQL注入攻击。


小总结

  • ${}就像在Java代码中直接拼接字符串,将用户输入的值原样插入到SQL语句中,需要手动处理引号,并且存在SQL注入风险。仅在确保输入安全且有特殊需求时考虑使用。
  • #{}则是通过预编译机制(如Java中的PreparedStatement),将参数值与SQL语句主体分离,由数据库在执行时负责正确的参数绑定。这样既自动处理了引号和类型转换,又从根本上杜绝了SQL注入攻击,是所有常规参数传递场景的推荐选择。

2. 单个字面量类型参数

当MyBatis的mapper接口中的方法参数仅为一个字面量类型(如Integer、String、Date等基本类型或其包装类),并且您需要在对应的SQL映射文件中引用这个参数时,可以使用${}#{}来获取该参数的值。


考虑到使用#{}更加安全可靠,所以就用它举个代码例子

UserMapper.java接口

package com.sakurapaid.mybatis3.demo01.mapper;

import com.sakurapaid.mybatis3.demo01.bean.User;

import java.util.List;

public interface UserMapper {
    // 1.添加用户
    public int addUser(User user);

    // 2.修改用户
    public int updateUser(User user);

    // 3.查询所有用户
    public List<User> findAllUser();

    // 4.根据id删除指定用户
    public int deleteUserById(int id);

    // 5. 根据用户名查询用户
    public User findUserByName(String name);
}

UserMapper.xml映射文件

这里的parameterType不是全类名,是因为我在配置文件中做了取别名操作

<?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">
<!-- 定义mapper接口的命名空间 -->
<mapper namespace="com.sakurapaid.mybatis3.demo01.mapper.UserMapper">
    <!--1.添加用户-->
    <insert id="addUser" parameterType="User">
        insert into user(name,age,sex) values(#{name},#{age},#{sex})
    </insert>
    
    <!--2.修改用户-->
    <update id="updateUser" parameterType="User">
        update user set name=#{name},age=#{age},sex=#{sex} where id=#{id}
    </update>
    
    <!--3.查询所有用户-->
    <select id="findAllUser" resultType="User">
        select * from user
    </select>
    
    <!--4.根据id删除指定用户-->
    <delete id="deleteUserById" parameterType="int">
        delete from user where id=#{id}
    </delete>
    
    <!--5.根据用户名查询用户-->
    <select id="findUserByName" resultType="User">
        select * from user where name=#{name}
    </select>
</mapper>

测试输出

package com.sakurapaid.mybatis3.demo01.test;

import com.sakurapaid.mybatis3.demo01.bean.User;
import com.sakurapaid.mybatis3.demo01.mapper.UserMapper;
import com.sakurapaid.mybatis3.demo01.utils.SqlSessionUtils;
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 org.junit.jupiter.api.Test;

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

/**
 * 用户测试类
 */
public class UserTest {
    @Test
    public void test() throws IOException {
        // 从SqlSessionUtils工具类获取SqlSession对象
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        // 获取UserMapper接口的代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        // 1.添加用户
        /*User user1 = new User(0, "小明", 18, "男");
        int i = userMapper.addUser(user1);
        if (i > 0) {
            System.out.println("添加成功");
        } else {
            System.out.println("添加失败");
        }*/

        // 2.修改用户
        /*User user2 = new User(1, "萨达姆", 26, "男");
        int i = userMapper.updateUser(user2);
        if (i > 0) {
            System.out.println("修改成功");
        } else {
            System.out.println("修改失败");
        }*/

        // 3.查询所有用户
        /*List<User> users = userMapper.findAllUser();
        if (!users.isEmpty()) {
            for (User user : users) {
                System.out.println(user);
            }
        } else {
            System.out.println("没有数据");
        }*/

        // 4.根据id删除指定用户
        /*int i = userMapper.deleteUserById(4);
        if (i > 0) {
            System.out.println("删除成功");
        } else {
            System.out.println("删除失败");
        }*/

        // 5.根据用户名查询用户
        User user = userMapper.findUserByName("萨达姆");
        if (user != null) {
            System.out.println("查询成功");
            System.out.print(user);
        } else {
            System.out.println("没有数据");
        }
    }
}


3. 多个字面量类型参数

当MyBatis的mapper接口中的方法参数有多个字面量类型(如Integer、String、Date等基本类型或其包装类),MyBatis会以特定方式组织这些参数,以便在SQL映射文件中引用它们。


参数组织方式

  • 默认键名MyBatis会自动将这些参数放入一个Map集合中。每个参数以其在方法参数列表中的位置作为键名,arg0, arg1, arg2, ...。例如,对于方法getUserInfo(int id, String name),参数id对应的键为arg0,参数name对应的键为arg1
  • 自定义键名:如果在方法参数上使用@Param("paramName")注解,MyBatis会使用注解中指定的名称作为键。例如,@Param("userId") int id@Param("userName") String name,则键分别为userIduserName

引用参数

  • 使用${}#{}:在SQL映射文件中,可以通过${argN}#{argN}(或使用自定义键名的${paramName}#{paramName})来访问Map集合中对应的值。其中,N表示参数在方法参数列表中的位置(从0开始计数),paramName为使用@Param注解指定的名称。
  • 手动添加单引号:对于${},与单个参数情况相同,对于字符串或日期类型的值,需要手动添加单引号。而对于#{},仍然无需手动添加任何引号,MyBatis会自动处理。

示例

假设mapper接口方法为:

public User getUserInfo(@Param("userId") int id, @Param("userName") String name);

对应的SQL映射文件片段:

SELECT * FROM users WHERE id = #{userId} AND username = '${userName}'

再举一个代码例子

现在我要传入两个参数,id和name,来查询对应的用户信息

UserMapper.java

package com.sakurapaid.mybatis3.demo01.mapper;

import com.sakurapaid.mybatis3.demo01.bean.User;

import java.util.List;

public interface UserMapper {
    // 1.添加用户
    public int addUser(User user);

    // 2.修改用户
    public int updateUser(User user);

    // 3.查询所有用户
    public List<User> findAllUser();

    // 4.根据id删除指定用户
    public int deleteUserById(int id);

    // 5.根据用户名查询用户
    public User findUserByName(String name);

    // 6.输入id和姓名,查询用户
    public User findUserByIdAndName(int id, String name);
}

UserMapper.xml

<?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">
<!-- 定义mapper接口的命名空间 -->
<mapper namespace="com.sakurapaid.mybatis3.demo01.mapper.UserMapper">
    <!--1.添加用户-->
    <insert id="addUser" parameterType="User">
        insert into user(name,age,sex) values(#{name},#{age},#{sex})
    </insert>
    
    <!--2.修改用户-->
    <update id="updateUser" parameterType="User">
        update user set name=#{name},age=#{age},sex=#{sex} where id=#{id}
    </update>
    
    <!--3.查询所有用户-->
    <select id="findAllUser" resultType="User">
        select * from user
    </select>
    
    <!--4.根据id删除指定用户-->
    <delete id="deleteUserById" parameterType="int">
        delete from user where id=#{id}
    </delete>
    
    <!--5.根据用户名查询用户-->
    <select id="findUserByName" resultType="User">
        select * from user where name=#{name}
    </select>
    
    <!--6.输入id和姓名,查询用户-->
    <select id="findUserByIdAndName" resultType="User">
        <!--select * from user where id=#{param1} and name=#{param2}-->
        select * from user where id=#{arg0} and name=#{arg1}
    </select>
</mapper>

如果此时sql语句还像单个字面量直接写标识符,编译就会报错

<!--6.输入id和姓名,查询用户-->
<select id="findUserByIdAndName" resultType="User">
    <!--select * from user where id=#{param1} and name=#{param2}-->
    <!--select * from user where id=#{arg0} and name=#{arg1}-->
    select * from user where id=#{id} and name=#{name}
</select>
// 6.输入id和姓名,查询用户
User user = userMapper.findUserByIdAndName(1, "萨达姆");
if (user != null) {
    System.out.println("查询成功");
    System.out.print(user);
} else {
    System.out.println("没有数据");

报错的关键简单总结就是,在执行MyBatis的数据库查询时,查询语句中引用了一个名为id的参数,但在对应的Mapper接口方法调用时并没有提供这个参数。MyBatis在处理SQL映射时未能找到与#{id}占位符相对应的参数值,因此抛出了org.apache.ibatis.binding.BindingException: Parameter 'id' not found的错误。这意味着在编写Mapper接口方法时,应当确保方法签名中包含所有在映射文件中使用的参数,同时在实际调用该方法时,也需要正确传递这些参数。

如何解决,此时就可以使用,也就是我注释上写的代码

默认键名:MyBatis会自动将这些参数放入一个Map集合中。每个参数以其在方法参数列表中的位置作为键名,即arg0, arg1, arg2, ...。或者param1, param2, param3, ...。

<!--6.输入id和姓名,查询用户-->
<select id="findUserByIdAndName" resultType="User">
    <!--select * from user where id=#{param1} and name=#{param2}-->
    <!--select * from user where id=#{arg0} and name=#{arg1}-->
</select>

还有有种方法是--自定义键名:如果在方法参数上使用@Param("paramName")注解,MyBatis会使用注解中指定的名称作为键。例如,@Param("userId") int id@Param("userName") String name,则键分别为userIduserName

这个到文章的后面再讲


4. map集合类型的参数

若mapper接口中的方法需要的参数为多个时,此时可以手动创建map集合,将这些数据放在map中 只需要通过${}和#{}访问map集合的键就可以获取相对应的值,注意${ }需要手动加单引号


又举个代码例子

map集合方式输入id和姓名,查询用户

在 MyBatis 中,当一个 Mapper 接口方法需要接收多个参数时,可以使用 Map 集合作为参数类型,将各个参数以键值对的形式封装在 Map 中。这样,只需一个参数即可传递多个值,使得接口签名更为简洁。以下是具体的步骤和示例:

1. 定义 Mapper 接口方法

首先,在 UserMapper.java 中定义一个使用 Map 类型参数的方法,如:

public User findUserByIdAndName2(Map<String, Object> map);

此方法表示我们要根据 idname 两个属性查询用户信息。

2. 编写 XML 映射文件

接着,在 UserMapper.xml 中编写对应的 SQL 查询语句,使用 #{} 占位符来引用 Map 中的键值:

<!-- 7.map集合方式输入id和姓名,查询用户 -->
<select id="findUserByIdAndName2" resultType="User">
    select * from user where id = #{id} and name = #{name}
</select>

这里,#{id}#{name} 分别代表 Map 参数中键为 "id""name" 的值。MyBatis 会在执行时自动从传入的 Map 中取出对应的值,并将其作为预编译参数插入 SQL 查询中,保证安全性。

3. 准备参数与调用查询

最后,在应用程序中创建一个 HashMap 实例,填充所需的 idname 值,然后调用 Mapper 方法执行查询:

// 7.map集合方式输入id和姓名,查询用户
Map<String, Object> map = new HashMap<>();
map.put("id", 1);
map.put("name", "萨达姆");

User userByIdAndName2 = userMapper.findUserByIdAndName2(map);

if (userByIdAndName2 != null) {
    System.out.println("查询成功");
    System.out.println(userByIdAndName2); // 修改为 println 输出整行
} else {
    System.out.println("没有数据");
}

这段代码首先创建了一个 HashMap 并放入 "id""name" 的键值对。然后,调用 userMapper.findUserByIdAndName2(map) 执行查询。根据查询结果,判断是否找到了匹配的用户,并打印相应的信息。


总结

通过上述步骤,我们展示了如何使用 Map 类型参数在 MyBatis 中实现多参数查询。关键要点如下:

  • 使用 Map 集合封装多个查询参数,简化接口定义。
  • 在 Mapper 接口中声明接受 Map 参数的方法。
  • 在 XML 映射文件中,使用 #{mapKey} 格式引用 Map 中的键值,确保 SQL 安全性。
  • 在应用中创建 Map 实例,填充参数,调用 Mapper 方法执行查询,并处理查询结果。

这种做法尤其适用于参数数量不确定或变动频繁的场景,有助于保持代码的灵活性和可维护性。初学者在实际编程中可以借鉴此模式,根据需求适配自己的查询逻辑。


5. 实体类类型的参数

若mapper接口中的方法参数为实体类对象时 此时可以使用${}和#{},通过访问实体类对象中的属性名获取属性值,注意${}需要手动加单引号


在 MyBatis 中,当一个 Mapper 接口方法的参数为实体类对象时,可以直接使用实体类属性作为 SQL 语句中的占位符值。这种方式简化了参数传递,无需手动构建 Map 或其他数据结构。

以User实体类作为参数,添加用户

1. 定义 Mapper 接口方法

首先,在 UserMapper.java 中定义一个使用 User 实体类作为参数的方法,如:

public int addUser2(User user);

此方法表示我们要根据 User 实例的属性值来添加一个新的用户记录到数据库。

2. 编写 XML 映射文件

接着,在 UserMapper.xml 中编写对应的 SQL 插入语句,使用 #{属性名} 占位符来引用实体类对象的属性:

<!-- 8.以User实体类作为参数,添加用户 -->
<insert id="addUser2">
    insert into user values(0, #{name}, #{age}, #{sex})
</insert>

这里,#{name}, #{age}, 和 #{sex} 分别代表 User 对象的 name, age, 和 sex 属性值。MyBatis 会在执行时自动从传入的 User 实例中取出对应的属性值,并将其作为预编译参数插入 SQL 插入语句中,保证安全性。

3. 准备参数与调用添加方法

最后,在应用程序中创建一个 User 实例,填充所需的属性值,然后调用 Mapper 方法执行添加操作:

// 8.以User实体类作为参数,添加用户
User user = new User(0, "小明", 18, "男");

int i = userMapper.addUser2(user);

if (i > 0) {
    System.out.println("添加成功");
} else {
    System.out.println("添加失败");
}

这段代码首先创建了一个 User 对象,并设置了 name, age, 和 sex 属性。然后,调用 userMapper.addUser2(user) 执行添加操作。根据返回的受影响行数判断添加是否成功,并打印相应的信息。


总结

通过上述步骤,我们展示了如何使用实体类类型参数在 MyBatis 中实现用户添加操作。关键要点如下:

  • 直接使用实体类对象作为 Mapper 方法的参数,简化参数传递。
  • 在 Mapper 接口中声明接受实体类对象的方法。
  • 在 XML 映射文件中,使用 #{属性名} 格式引用实体类对象的属性,确保 SQL 安全性。
  • 在应用中创建实体类实例,填充属性值,调用 Mapper 方法执行添加操作,并根据返回结果判断操作是否成功。

这种做法适用于参数与实体类属性紧密关联的场景,能够简化代码结构,提高代码的可读性和一致性。初学者在设计数据操作接口时,可以优先考虑使用实体类作为参数,以充分利用 MyBatis 对实体类属性的自动映射功能。


6. 使用@Param标识参数

可以通过@Param注解标识mapper接口中的方法参数 此时,会将这些参数放在map集合中,以@Param注解的value属性值为键,以参数为值;以 param1,param2...为键,以参数为值;只需要通过${}和#{}访问map集合的键就可以获取相对应的值, 注意${}需要手动加单引号


在 MyBatis 中,@Param 注解可以用于为 Mapper 接口方法的参数提供别名,特别是在方法包含多个参数时,有助于清晰地标识每个参数的作用,并在 XML 映射文件中通过别名引用它们。以下是如何使用 @Param 注解查询用户的步骤和示例:

使用@Param输入id和姓名,查询用户

1. 定义 Mapper 接口方法

首先,在 UserMapper.java 中定义一个带有 @Param 注解的方法,为 idname 参数赋予别名:

// 9.使用@Param输入id和姓名,查询用户
public User findUserByIdAndName3(@Param("id") int id, @Param("name") String name);

这里,@Param("id")@Param("name") 分别为 int idString name 参数指定了别名 "id""name"。这些别名将在 XML 映射文件中作为占位符的键来使用。

2. 编写 XML 映射文件

接着,在 UserMapper.xml 中编写对应的 SQL 查询语句,使用 #{别名} 占位符来引用带有 @Param 注解的参数:

<!--9.使用@Param输入id和姓名,查询用户-->
<select id="findUserByIdAndName3" resultType="User">
    select * from user where id = #{id} and name = #{name}
</select>

这里,#{id}#{name} 分别代表方法参数中被 @Param 注解标记为 "id""name" 的值。MyBatis 会在执行时自动从方法参数中取出对应的值,并将其作为预编译参数插入 SQL 查询中,保证安全性。

不写id和name的话,也可以以 param1,param2...为键,以参数为值;

3. 调用查询方法

最后,在应用程序中直接调用 Mapper 方法,传入 idname 参数值:

// 9.使用@Param输入id和姓名,查询用户
User userByIdAndName3 = userMapper.findUserByIdAndName3(1, "萨达姆");

if (userByIdAndName3 != null) {
    System.out.println("查询成功");
    System.out.println(userByIdAndName3); // 修改为 println 输出整行
} else {
    System.out.println("没有数据");
}

这段代码直接调用 userMapper.findUserByIdAndName3(1, "萨达姆"),传入 idname 的具体值。根据查询结果,判断是否找到了匹配的用户,并打印相应的信息。


总结

通过上述步骤,我们展示了如何使用 @Param 注解在 MyBatis 中实现多参数查询。关键要点如下:

  • 使用 @Param 注解为 Mapper 接口方法的参数指定别名,增加代码可读性。
  • 在 Mapper 接口中声明带有 @Param 注解的方法。
  • 在 XML 映射文件中,使用 #{别名} 格式引用 @Param 注解标记的参数,确保 SQL 安全性。
  • 在应用中直接调用 Mapper 方法,传入相应参数值,并处理查询结果。

这种做法特别适用于方法参数较多且需要清晰标识其用途的场景,增强了代码的可理解性和维护性。初学者在编写多参数的 MyBatis 查询方法时,应优先考虑使用 @Param 注解来提升代码质量。注意,这里并不涉及将参数放入 Map 集合中,而是直接使用注解提供的别名与方法参数关联。

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

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

相关文章

SpringBoot+Prometheus+Grafana实现应用监控和报警

一、背景 SpringBoot的应用监控方案比较多&#xff0c;SpringBootPrometheusGrafana是目前比较常用的方案之一。它们三者之间的关系大概如下图&#xff1a; 关系图 二、开发SpringBoot应用 首先&#xff0c;创建一个SpringBoot项目&#xff0c;pom文件如下&#xff1a; <…

java数据结构与算法刷题-----LeetCode540. 有序数组中的单一元素

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目录 1. 异或运算2. 全数组二分查找异或奇偶3. 偶数下标二分查找 1. 异…

Python学习之-正则表达式

目录 前言&#xff1a;1.re.serach1.1例子&#xff1a; 2.re.match2.1示例1&#xff1a;2.2 示例2&#xff1a; 3.re.findall3.1 示例 4.re.fullmatch4.1 示例1&#xff1a;4.2 示例2: 5.re.split5.1 示例1:5.2 示例2&#xff1a;5.3 示例3&#xff1a; 6.re.sub6.1 示例&#…

puzzle(1122)连线迷宫

目录 一&#xff0c;连线迷宫-经典模式 1&#xff0c;规则 2&#xff0c;策略 3&#xff0c;调整的局部性 4&#xff0c;八连通端点的线条合并 taptap小游戏 迷宫解谜 连线迷宫模式 一&#xff0c;连线迷宫-经典模式 1&#xff0c;规则 2&#xff0c;策略 分2步&#x…

脚本应使用项目的主要语言编写

原文&#xff1a;Joo Freitas - 2024.03.24 这是我长时间以来的一个深感赞同的观点。 我参与过的几乎所有项目&#xff0c;都有我们编写的用于自动化重复性过程的脚本。然而&#xff0c;大多数脚本在几周后变得过时且难以维护&#xff0c;因为我们要么不再需要它们&#xff0…

Golang hash/crc32 库实战指南:从基础到优化

Golang hash/crc32 库实战指南&#xff1a;从基础到优化 引言理解CRC32hash/crc32库概览实战技巧数据校验性能优化多线程应用 错误处理与调试错误处理调试 实际案例分析结论 总结重点回顾 引言 在现代软件开发中&#xff0c;数据的完整性和安全性至关重要。无论是数据库存储、…

计算机组成原理 — 指令系统

指令系统 指令系统指令的概述指令的格式指令的字长取决于 操作数类型和操作种类操作数的类型数据在存储器中的存放方式操作类型 寻址方式指令寻址数据寻址立即寻址直接寻址隐含寻址间接寻址寄存器寻址寄存器间接寻址基址寻址变址寻址堆栈寻址 RISC 和 CISC 技术RISC 即精简指令…

K8s Pod亲和性、污点、容忍度、生命周期与健康探测详解(上)

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《Kubernetes航线图&#xff1a;从船长到K8s掌舵者》 &#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 在上一章节中&#xff0c;我们详细探讨了Pod的概念、创建、…

linux X11窗口开发

X11 控制窗口 窗口信息查询 检索窗口信息工具 xprop xprop执行后会出现一个光标&#xff0c;这个光标可以获取到窗口信息 X11 获取窗口信息&#xff0c;操作窗口 X11 遍历获取当前显示窗口的标题 _NET_WM_NAME, 置顶模拟键盘输入操作 开发依赖 apt install libX11-dev l…

【前端Vue】社交信息头条项目完整笔记第2篇:二、登录注册,准备【附代码文档】

社交媒体-信息头条项目完整开发笔记完整教程&#xff08;附代码资料&#xff09;主要内容讲述&#xff1a;一、项目初始化使用 Vue CLI 创建项目,加入 Git 版本管理,调整初始目录结构,导入图标素材,引入 Vant 组件库,移动端 REM 适配,关于 , 配置文件,封装请求模块。十、用户关…

LeetCode-1669题:合并两个链表(原创)

【题目描述】 给你两个链表 list1 和 list2 &#xff0c;它们包含的元素分别为 n 个和 m 个。请你将 list1 中下标从 a 到 b 的全部节点都删除&#xff0c;并将list2 接在被删除节点的位置。下图中蓝色边和节点展示了操作后的结果&#xff1a; 请你返回结果链表的头指针。 【…

10 - Debian如何让特定用户切换root身份

作者&#xff1a;网络傅老师 特别提示&#xff1a;未经作者允许&#xff0c;不得转载任何内容。违者必究&#xff01; Debian如何让特定用户切换root身份 《傅老师Debian小知识库系列之10》——原创 前言 傅老师Debian小知识库特点&#xff1a; 1、最小化拆解Debian实用技能…

LNMP架构之mysql数据库实战

mysql安装 到官网www.mysql.com下载源码版本 实验室使用5.7.40版本 tar xf mysql-boost-5.7.40.tar.gz #解压 cd mysql-boost-5.7.40/ yum install -y cmake gcc-c bison #安装依赖性 cmake -DCMAKE_INSTALL_PREFIX/usr/local/mysql -DMYSQL_DATADIR/data/mysql -DMYSQL_…

MySQL为什么会选错索引

在平时不知道一有没有遇到过这种情况&#xff0c;我明明创建了索引&#xff0c;但是MySQL为何不用索引呢&#xff1f;为何要进行全索引扫描呢&#xff1f; 一、对索引进行函数操作 假设现在维护了一个交易系统&#xff0c;其中交易记录表 tradelog 包含交易流水号(tradeid)、交…

【分布式计算框架】Hadoop伪分布式安装

&#x1f57a;作者&#xff1a; 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux &#x1f618;欢迎 ❤️关注 &#x1f44d;点赞 &#x1f64c;收藏 ✍️留言 文章目录 Hadoop伪分布式安装一、实验目的二、实验环境三、实验内容基本任务1&#xff1a;安装Linux虚拟机(至…

[Vue3] 配置 Pinia 并存储、读取、修改数据 | 集中式状态(数据)管理

安装 npm i pinia main.ts import ./assets/main.cssimport { createApp } from vue import App from ./App.vue import { createPinia } from pinia // 引入Pinia// 创建一个应用 const app createApp(App)const pina createPinia() app.use(pina) // 挂载整个应用到app容器…

『Apisix进阶篇』动态负载均衡:APISIX的实战演练与策略应用

&#x1f680;『Apisix系列文章』探索新一代微服务体系下的API管理新范式与最佳实践 【点击此跳转】 &#x1f4e3;读完这篇文章里你能收获到 &#x1f3af; 掌握APISIX中多种负载均衡策略的原理及其适用场景。&#x1f4c8; 学习如何通过APISIX的Admin API和Dashboard进行负…

AIGC趋势下软件工程强智能编码来临了么?

一、背景 在AIGC&#xff08;AI Generated Content&#xff0c;人工智能生成内容&#xff09;的趋势下&#xff0c;软件工程领域的“强智能编码”是指通过深度学习、自然语言处理等前沿技术&#xff0c;使AI具备理解、学习、推理和生成代码的能力&#xff0c;从而实现自动化或…

操作系统:经典进程同步问题的高级探讨

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

ChatGPT提升工作生产力方法和技巧ChatGPT enhances work productivity methods and techniques

使用ChatGPT提升工作效率的一些详细步骤和技巧&#xff1a; 1. 快速撰写和编辑文档 撰写文档&#xff1a;当需要撰写报告、方案、邮件等内容时&#xff0c;可以直接向ChatGPT提出请求&#xff0c;例如&#xff1a;“请帮我写一份关于第一季度销售业绩的总结报告。”之后&#x…