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 mapperPUBLIC "-//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 {@Testpublic 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 mapperPUBLIC "-//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.mzph.cn/news/776119.shtml

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

相关文章

康耐视visionpro-CogAcqFifoTool工具详细说明

CogAcqFifoTool操作说明&#xff1a; ① 打开工具栏&#xff0c;双击或点击鼠标拖拽 添加CogAcqFifoTool ②.从图片采集设备/图像采集卡列表里选择对应的相机&#xff0c;视频格式选择图像格式。 Mono表示黑白图像&#xff0c;RGB表示彩色相机。点击初始化取相初始化相机。 ③…

【元器件-电阻篇】0Ω电阻在电路中有什么作用

电路设计与调试过程中,我们很多时候要用到0Ω电阻(如下图),那么0Ω电阻到底在这过程中充当了什么样的角色呢? 0R电阻在电路中有什么作用? #创作灵感#:脑海存留的如下几点疑惑需要解开。 1、电路初次设计使用串接电阻为什么是0R电阻; 2、单板调试时为什么有时候会用到0R…

阿里云倚天服务器是什么?倚天服务器c8y、g8y和r8y详细介绍

阿里云倚天云服务器CPU采用倚天710处理器&#xff0c;租用倚天服务器c8y、g8y和r8y可以享受优惠价格&#xff0c;阿里云服务器网aliyunfuwuqi.com整理倚天云服务器详细介绍、倚天710处理器性能测评、CIPU架构优势、倚天服务器使用场景及生态支持&#xff1a; 阿里云倚天云服务…

Python中的杨辉三角

杨辉三角&#xff0c;也被称为帕斯卡三角&#xff0c;是一个非常有趣的数学结构&#xff0c;它在组合数学中扮演着重要的角色。在这篇博客中&#xff0c;我们将探讨如何在Python中生成杨辉三角&#xff0c;并讨论不同方法的优缺点。 杨辉三角简介 杨辉三角是一个由数字构成的…

OceanBase中NOT EXISTS是否需要被改写

作者简介 张瑞远&#xff0c;曾经从事银行、证券数仓设计、开发、优化类工作&#xff0c;现主要从事电信级IT系统及数据库的规划设计、架构设计、运维实施、运维服务、故障处理、性能优化等工作。 持有Orale OCM,MySQL OCP及国产代表数据库认证。 获得的专业技能与认证包括 Oce…

保障校园网络安全用堡垒机的几个原因分析

校园&#xff0c;人人都熟悉的地方&#xff0c;梦想知识开始的地方。在互联网数字化快速发展的今天&#xff0c;网络安全的学习环境是非常必要的。所以采购保障校园网络安全工具是必要的。那为什么一定要用堡垒机呢&#xff1f;这里我们一起来简单分析一下原因。 保障校园网络…

Tuxera for Mac2024软件产品密钥及下载安装教程

Tuxera for Mac在安全性和稳定性方面表现出色&#xff0c;为用户提供了可靠的数据保障和无忧的使用体验。 首先&#xff0c;从安全性角度来看&#xff0c;Tuxera for Mac采用了先进的技术来保护用户的数据。它支持快速全面的数据保护&#xff0c;通过智能缓存技术确保文件传输…

Godot 学习笔记(5):彻底的项目工程化,解决GodotProjectDir is null+工程化范例

文章目录 前言GodotProjectDir is null解决方法解决警告问题根本解决代码问题测试引用其实其它库的输出路径无所谓。 工程化范例环境命名规范Nuget项目结构架构代码ISceneModelIOC服务 测试GD_Extension 通用扩展TestUtils GD_ProgramTestServiceMainSceneModel Godot对应的脚本…

STM32存储左右互搏 SPI总线FATS文件读写SD/MicroSD/TF卡

STM32存储左右互搏 SPI总线FATS文件读写SD/MicroSD/TF卡 SD/MicroSD/TF卡是基于FLASH的一种常见非易失存储单元&#xff0c;由接口协议电路和FLASH构成。市面上由不同尺寸和不同容量的卡&#xff0c;手机领域用的TF卡实际就是MicroSD卡&#xff0c;尺寸比SD卡小&#xff0c;而…

Leo赠书活动-21期 《一篇讲明白 Hadoop 生态的三大部件》

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

【算法刷题】链表笔试题解析(1)

一、链表分割 题目描述&#xff1a; 链接&#xff1a;链表分割 题目分析&#xff1a; 这题直接处理并不好做&#xff0c;我们可以构建前后两个链表&#xff0c;将小于x值的结点放在链表a内&#xff0c;将其它结点放在链表b内&#xff0c;这样将原链表遍历完后&#xff0c;原链…

Day23 代码随想录(1刷) 二叉树

669. 修剪二叉搜索树 给你二叉搜索树的根节点 root &#xff0c;同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树&#xff0c;使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 (即&#xff0c;如果没有被移除&#xff0c;原有的父代…

PHP图床程序优化版:图片外链服务、图床API服务、图片CDN加速与破解防盗链

图片免费上传 支持本地储存、FTP储存、第三方云储存&#xff08;阿里云 OSS、腾讯云 COS、七牛云等&#xff09;。 图片外链加速 一键转换第三方网站的图片外链地址为图床可分享的图片地址&#xff08;支持CDN&#xff09;。 图片解析服务 直接将第三方外链图片地址显示为…

oracle docker安装

修改下载的Image的REPOSITORY和TAG属性 修改下载的Image的REPOSITORY和TAG属性&#xff1a;docker tag <IMAGE ID> <REPOSITORY NAME> docker tag 3fa112fd3642 aliyun/oracle_11g 参考网址 使用docker images时&#xff0c;可能会出现REPOSITORY和TAG均为none的镜…

【教程】iOS 手机抓包工具介绍及教程

&#x1f4f1; 最近又发现APP Store一款宝藏软件&#xff0c;克魔助手抓包工具&#xff0c;app刚上架&#xff0c;功能不断迭代中&#xff0c;目前18软妹币实惠价可享受终身版&#xff01;现在是下手的最好时机。 引言 移动端开发中&#xff0c;抓包工具已成为必备的工具之一…

shell脚本发布nginx vue2 项目示例

nginx、git、node.js安装略过。 使git pull或者git push不需要输入密码操作方法 非docker安装nginx&#xff01;&#xff01;&#xff01; 姊妹篇&#xff08;docker安装nginx&#xff09;&#xff1a;shell脚本发布docker-nginx vue2 项目示例 pro_build.sh 注意&#xff1…

Linux基础IO(操作系统层面理解文件)

目录 一、认识 open 函数 1.1 理解文件 1.2 open 函数 1.3 函数选项和宏 二、 open 函数的返回值 三、 fd 的本质 3.1 各部分内容及关系 3.2 如何确定进程对应文件 四、Linux 一切皆文件&#xff1f; 一、认识 open 函数 在C语言中学习文件操作时&#xff0c;我们学…

基于SpringBoot和Vue的课程作业管理系统的设计与实现

今天要和大家聊的是一款基于SpringBoot和Vue的课程作业管理系统的设计与实现。 &#xff01;&#xff01;&#xff01; 有需要的小伙伴可以通过文章末尾名片咨询我哦&#xff01;&#xff01;&#xff01; &#x1f495;&#x1f495;作者&#xff1a;李同学 &#x1f495;&am…

element表格 加滚动,监听底部实现分页加载

表格要实现滚动很简单&#xff0c;给他加一个高度即可 height"300" 然后是监听事件 mounted() {this.lazyLoading();}, methods:{lazyLoading(){let dom document.querySelector(".el-table__body-wrapper");dom.addEventListener("scroll", (…

踩坑uniapp中打包Andiord app,在真机调试时地图以及定位功能可以正常使用,打包成app后失效的问题

首先看到这是uni官网提出的&#xff0c;app上建议使用高德地图。 下面就用高德地图进行配置。 步骤一&#xff1a;登陆高德地图控制台 名称和类型根据自己情况填写选择即可 步骤二&#xff1a; 添加key 步骤三&#xff1a;取到SHA1 进入uniapp开发官网 点击应用名称&#…