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

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 集合中,而是直接使用注解提供的别名与方法参数关联。

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

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

相关文章

Flutter开发之下标

Flutter开发之下标 在iOS开发中使用下标就很方便&#xff0c;本文主要是记录一下Flutter中系统自带的下标&#xff0c;还可以通过对应的方法编写自己的下标。 在Objective-C中的下标 关键字Subscript。 NSArray - (ObjectType)objectAtIndexedSubscript:(NSUInteger)idx A…

Abaqus周期性边界代表体单元Random Sphere RVE 3D (Mesh)插件

插件介绍 Random Sphere RVE 3D (Mesh) - AbyssFish 插件可在Abaqus生成三维具备周期性边界条件(Periodic Boundary Conditions, PBC)的随机球体骨料及骨料-水泥界面过渡区(Interfacial Transition Zone, ITZ)模型。即采用周期性代表性体积单元法(Periodic Representative Vol…

vscode上编辑vba

安装xvba插件更换vscode的工作目录启动扩展服务器在config.json中添加目标工作簿的名称加载excel文件&#xff08;必须带宏的xlsm&#xff09;这个扩展就会自动提取出Excel文件中的代码Export VBA&#xff08;编辑完成的VBA代码保存到 Excel文件 &#xff09;再打开excel文件可…

java中的单例模式

一、描述 单例模式就是程序中一个类只能有一个对象实例 举个例子: //引出单例模式&#xff0c;一个类中只能由一个对象实例 public class Singleton1 {private static Singleton1 instance new Singleton1();//通过这个方法来获取实例public static Singleton1 getInstance…

标定系列——预备知识-OpenCV中与标定板处理相关的函数(四)

标定系列——预备知识-OpenCV中与标定板处理相关的函数&#xff08;四&#xff09; 说明记录棋盘格圆网格 说明 记录了OpenCV中与标定板处理相关的函数用法 记录 棋盘格 圆网格

Qt源程序编译及错误问题解决

Error 5 while parsing C:/qt-everywhere-src-6.6.2/qt-build/qtdeclarative/src/qmlmodels/meta_types/qt6qmlmodels_release_metatypes.json: illegal value .json 文件为空文件0字节&#xff0c;加 “[]”&#xff0c;不要引号。可以解决这类错误。 Qt编译 Qt for Windows…

[BT]BUUCTF刷题第9天(3.27)

第9天&#xff08;共2题&#xff09; [护网杯 2018]easy_tornado 打开网站就是三个txt文件 /flag.txt flag in /fllllllllllllag/welcome.txt render/hints.txt md5(cookie_secretmd5(filename))当点进flag.txt时&#xff0c;url变为 http://b9e52e06-e591-46ad-953e-7e8c5f…

WPF 命名空间解释

在C#中有命名空间的概念&#xff0c;我们可以使用using引入&#xff0c;就可以使用其中的类&#xff0c;在xaml中&#xff0c;也同样有命名空间&#xff0c;在window标签中用xmlns声明的这几行&#xff0c;这就是本页面引入的命名空间。 一般的情况下&#xff0c;我们引入命名空…

左手医生:医疗 AI 企业的云原生提效降本之路

相信这样的经历对很多人来说并不陌生&#xff1a;为了能到更好的医院治病&#xff0c;不惜路途遥远奔波到大城市&#xff1b;或者只是看个小病&#xff0c;也得排上半天长队。这些由于医疗资源分配不均导致的就医问题已是老生长谈。 云计算、人工智能、大数据等技术的发展和融…

qt-C++笔记之QSpinBox控件

qt-C笔记之QSpinBox控件 code review! 文章目录 qt-C笔记之QSpinBox控件1.运行2.main.cpp3.main.pro4.《Qt6 C开发指南》&#xff1a;4.4 QSpinBox 和QDoubleSpinBox 1.运行 2.main.cpp #include <QApplication> #include <QSpinBox> #include <QPushButton&g…

数据结构——排序算法

1、排序的概念 排序是指的是将一组数据&#xff08;如数字、单词、记录等&#xff09;按照某种特定的顺序&#xff08;升序或降序&#xff09;进行排列的过程。排序算法是实现排序的程序或方法&#xff0c;它们在软件开发和数据处理中扮演着至关重要的角色。 排序算法可以根据…

人脸68关键点与K210疲劳检测

目录 人脸68关键点检测 检测闭眼睁眼 双眼关键点检测 计算眼睛的闭合程度&#xff1a; 原理: 设置阈值进行判断 实时监测和更新 拓展&#xff1a;通过判断上下眼皮重合程度去判断是否闭眼 检测嘴巴是否闭合 提取嘴唇上下轮廓的关键点 计算嘴唇上下轮廓关键点之间的距…

案例分析-IEEE 754浮点标准

案例一&#xff1a; 请分析IEEE 754双精度浮点数规格化数的表示范围。 案例二&#xff1a; 规格化浮点数的Bias为什么采用2k-1-1而不是2k-1​&#xff1f;非规范数的指数E1-Bias而不是0-Bias&#xff1f; &#xff08;1&#xff09; ① bias 127时 E e - 127 &#xff08;00…

Remote Desktop Manager for Mac:远程桌面管理软件

Remote Desktop Manager for Mac&#xff0c;是远程桌面管理的理想之选。它集成了多种远程连接技术&#xff0c;无论是SSH、RDP还是VNC&#xff0c;都能轻松应对&#xff0c;让您随时随地安全访问远程服务器和工作站。 软件下载&#xff1a;Remote Desktop Manager for Mac下载…

DevSecOps平台架构系列-互联网企业私有化DevSecOps平台典型架构

目录 一、概述 二、私有化DevSecOps平台建设思路 2.1 采用GitOps公有云建设 2.2 采用GitOps私有云建设 2.3 总结 三、GitOps及其生态组件 3.1 采用GitOps的好处 3.1.1 周边生态系统齐全 3.1.2 便于自动化的实现 3.1.3 开发人员属性GitOps 3.2 GitOps部分生态组件介绍…

动态内存操作函数使用过程中会遇见的问题

越界访问 首先我们上一个代码&#xff0c;看看这个的代码的问题 这个代码的问题显而易见 &#xff0c;就是在循环里面&#xff0c;产生了越界访问的问题&#xff0c;这里你开辟了10个整形空间&#xff0c;但是从0-10一共是11个整形空间。导致访问不合法的空间&#xff0c;从而…

【C++练级之路】【Lv.17】【STL】set类和map类的模拟实现

快乐的流畅&#xff1a;个人主页 个人专栏&#xff1a;《C语言》《数据结构世界》《进击的C》 远方有一堆篝火&#xff0c;在为久候之人燃烧&#xff01; 文章目录 引言一、红黑树&#xff08;改造版&#xff09;1.1 结点1.2 迭代器1.2.1 operator1.2.2 operator- - 1.3 本体1.…

【LeetCode热题100】105. 从前序与中序遍历序列构造二叉树(二叉树)

一.题目要求 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 二.题目难度 中等 三.输入样例 示例 1: 输入: preorder [3,9,20,15,7], inorder…

考研数学|第一轮刚完,刷1800惨不忍睹怎么办?

1800题惨不忍睹&#xff0c;我有办法救你 1800题算是所有习题册里面难度比较低的题集了&#xff0c;特别是1800题基础阶段的题目&#xff0c;我一般推荐基础不好的同学在基础阶段做这个。如果1800题都觉得很难的话&#xff0c;那么其他题集就更不用说了 刷1800题错的很多&…

[CISCN2019 华北赛区 Day2 Web1]Hack World

本题首先考察的是sql注入 拿过滤字符集先跑一遍 发现以上字符集都被过滤了 尝试id1 id11 尝试id(1)(2) 这里就已经给出了个思路我们可以尝试盲注去打 id(select(ascii(mid(flag,1,1))102)from(flag)) 这里表跟列已经给了我们了&#xff0c;所以我们可以写脚本了 import reque…