【MyBatis Plus】深入探索 MyBatis Plus 的条件构造器,自定义 SQL语句,Service 接口的实现

文章目录

  • 前言
  • 一、条件构造器
    • 1.1 什么是条件构造器
    • 1.2 QueryWrapper
    • 1.3 UpdateWrapper
    • 1.4 LambdaWrapper
  • 二、自定义 SQL 语句
    • 2.1 自定义 SQL 的基本用法
    • 2.2 自定义 SQL 实现多表查询
  • 三、Service 接口
    • 3.1 对 Service 接口的认识
    • 3.2 实现 Service 接口
    • 3.3 实现增删改查功能
    • 3.4 Lambda 功能


前言

在前文中,介绍了 MyBatis Plus 的一些基础功能,我们发现使用MyBatis Plus 可以非常简单的就实现对单表的增删改查操作。但是这些操作对应的 SQL 语句都非常简单,如果是面对一些复杂的 SQL 或者多表查询这样的情况,就需要我们自己来重新组织 SQL 语句了。

本文将重点探索 MyBatis Plus 的条件构造器、自定义SQL语句来解决这些复杂情况,然后实现 MyBatis Plus 提供的通用的 Service 接口,以展示如何在项目中使用 MyBatis Plus。

一、条件构造器

1.1 什么是条件构造器

在数据库中,除了新增操作之外,修改、删除、查询的 SQL 语句中都需要指定 where 条件。在 BaseMapper中提供的相关的方法除了提供以 id 作为 where 的条件之外,也都支持更加复杂的 where 条件。


我们可以看到,在上面的BaseMapper这些方法中,有很多方法都有一个 Wrapper 参数,而这个 Wrapper 就是条件构造器的抽象类,这个类有许多的实现类:


这些类就是条件构造器,它是 MyBatis Plus 中的一种查询条件封装方式,用于构建SQL语句中的WHERE条件部分。这些构造器的主要作用是根据给定的条件构建数据库操作的条件语句,以实现查询、更新、删除等操作。

其中,Wrapper 的子类 AbstractWrapper 提供了where中包含的所有条件构造方法:

QueryWrapperAbstractWrapper的基础上拓展了一个select方法,允许指定查询字段:

UpdateWrapperAbstractWrapper的基础上拓展了一个set方法,允许指定SQL中的SET部分:

另外,我们还发现有一组与Lambda相关的条件构造器,为什么会存在这组构造器将在下文进行说明。

下面,让我来演示一下其中一些构造器主要的用法。

1.2 QueryWrapper

QueryWrapper 顾名思义,就是用于构建查询条件的WHERE子句的条件构造器。它提供了多种方法来构建条件,包括等值条件、范围条件、模糊查询等。

示例一:现在要求查询出user表中名字带 o,并且存款不少于 1000 的用户:

@Test
void testQueryMapper() {
    // 1. 构造查询条件 where name like "%o%" and balance >= 1000
    QueryWrapper<User> wrapper = new QueryWrapper<User>()
            .select("id", "username", "info", "balance")
            .like("username", "o")
            .ge("balance", 1000);
    
    // 2. 根据 where 条件进行查询
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}

以下是对上述代码示例的说明:

  • 在此代码中,我们首先创建了一个QueryWrapper<User>对象,用于构建查询条件的WHERE子句。然后,通过链式方法调用来定义查询条件:

    • .select("id", "username", "info", "balance"):指定了要选择的列,包括idusernameinfobalance

    • .like("username", "o"):设置了查询条件,要查找用户名中包含字母’o’的记录,类似于SQL中的 WHERE username LIKE '%o%'

    • .ge("balance", 1000):设置了查询条件,筛选出余额大于或等于1000的记录。

  • 接下来,使用userMapper执行查询操作,通过调用selectList(wrapper)方法执行带有指定条件的SQL查询。

运行结果:

查询的结果被存储在一个List<User>对象中,然后通过users.forEach(System.out::println)遍历列表,将每个用户的详细信息打印到控制台。执行代码后,运行结果如下所示:

运行结果

上述示例演示了如何使用QueryWrapper构建查询条件以检索数据库表中符合特定条件的记录。这对于执行复杂的查询操作非常有用。

另外,QueryWrapper 也可以用于 Update 操作。

示例二:使用 QueryWrapper 进行 update 操作,更新用户名为Jack的用户的余额为 1000:

@Test
void testUpdateByQueryWrapper() {
    // 1. 构造查询条件:where name = "Jack"
    QueryWrapper<User> wrapper = new QueryWrapper<User>()
            .eq("username", "Jack");
    // 2. 准备更新数据,user 对象中的非空数据会作为 SET 的内容
    User user = new User();
    user.setBalance(1000);
    // 3. 更新
    userMapper.update(user, wrapper);
}

对上述代码的说明:

  1. 首先,构建了一个QueryWrapper<User>对象,用于构建查询条件。在这个例子中,使用了.eq("username", "Jack")来设置查询条件,即要查找用户名为Jack的记录。这相当于SQL中的 WHERE username = 'Jack'

  2. 接下来,准备要更新的数据。创建一个User对象,并设置了要更新的字段,其中user.setBalance(1000)将余额设置为1000。在user对象中,非空数据会作为UPDATE语句的SET部分内容。

  3. 最后,使用userMapper.update(user, wrapper)来执行更新操作。这将更新满足QueryWrapper条件的记录,将usernameJack的用户的余额更新为1000。

运行结果

执行代码后,运行结果如下所示:

运行结果

上述示例演示了如何使用QueryWrapper构建查询条件并执行更新操作,将指定条件的记录进行更新。这是一个示例,演示了使用QueryWrapper来进行数据库更新操作。

1.3 UpdateWrapper

UpdateWrapper是用于构建更新操作的WHERE条件构造器。它允许设置更新的字段和更新的条件。以下是一些UpdateWrapper的主要用法示例:

示例:更新id为 1, 2, 4 的用户的余额,每个人的余额都扣 200:

@Test
void testUpdateQuery() {
    // 1. 准备查询条件:where id in (1, 2, 4)
    UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
            .setSql("balance = balance - 200")
            .in("id", Arrays.asList(1, 2, 4));

    // 2. 更新,注意此时 update 的第一个参数为 null
    userMapper.update(null, wrapper);
}

对上述代码的说明:

  1. 首先,创建了一个UpdateWrapper<User>对象,用于构建更新操作的WHERE条件。在这个例子中,我们使用.in("id", Arrays.asList(1, 2, 4))来设置查询条件,即要更新id在1、2、4范围内的记录。这相当于SQL中的 WHERE id IN (1, 2, 4)

  2. 接下来,使用.setSql("balance = balance - 200")来设置更新操作的内容。这表示要将balance字段减少200。这相当于SQL中的 SET balance = balance - 200

  3. 最后,使用userMapper.update(null, wrapper)来执行更新操作。需要注意的是,在这里update方法的第一个参数为null,因为在UpdateWrapper中已经设置了更新的条件和内容。

执行代码后,运行结果如下所示:

运行结果

上述示例演示了如何使用UpdateWrapper构建更新操作的WHERE条件,并执行更新操作,以减少满足条件的记录的余额。这是一个示例,演示了使用UpdateWrapper进行数据库更新操作。

1.4 LambdaWrapper

通过上面的几个示例可以发现,无论是QueryWrapper还是UpdateWrapper在构造条件的时候都出现了硬编码,导致使用了字符串魔法值。这在编程规范中显然是不推荐的。那怎么样才能不写字段名,又能知道字段名呢?

其中一种办法是基于变量的gettter方法结合反射技术。因此我们只要将条件对应的字段的getter方法传递给条件构造器,就能计算出对应的变量名了。而传递方法可以使用JDK8中的方法引用和Lambda表达式。因此Mybatis Plus又提供了一套基于 LambdaWrapper,包含两个类:

  • LambdaQueryWrapper
  • LambdaUpdateWrapper

分别对应QueryWrapperUpdateWrapper。、

下面以 LambdaQueryWrapper 为例,演示如何使用Lambda条件构造器进行上文的查询操作。

    @Test
    void testLambdaQueryMapper() {
        // 1. 准备查询条件:where name like "%o%" and balance >= 1000
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
                .select(User::getId, User::getUsername, User::getInfo, User::getBalance)
                .like(User::getUsername, "o")
                .ge(User::getBalance, 1000);
        // 2. 查询
        List<User> users = userMapper.selectList(wrapper);
        users.forEach(System.out::println);
    }

对上述代码的说明:

  1. 创建了一个LambdaQueryWrapper<User>对象,用于构建查询条件。在这个例子中,使用了Lambda表达式,通过User::getXXX方式来引用实体类User的字段,而不需要写字段名的字符串。这大大提高了代码的可读性和可维护性。

  2. 设置了查询条件,使用like方法进行模糊查询,匹配username字段中包含字母"o"的记录,使用ge方法筛选balance字段大于或等于1000的记录。同时,通过select方法指定了查询结果中的字段。

  3. 最后,使用userMapper.selectList(wrapper)执行查询操作,将满足条件的记录存储在users列表中,并使用forEach遍历并打印每个用户的详细信息。

运行代码后,运行结果如下所示:

运行结果

这个示例演示了如何使用LambdaQueryWrapper构建查询条件,不需要硬编码字段名,而是使用Lambda表达式引用实体类的字段,提高了代码的可读性和可维护性。

二、自定义 SQL 语句

在上面的演示 UpdateWrapper 的例子中,直接在代码中编写了更新的 SQL 语句:

这种写法在实际开发中一般都是不允许的,因为 SQL 语句最好都维护在持久层,而不是业务层。就从这个案例来说,由于条件是in语句,只能将SQL写在Mapper.xml文件,利用foreach来生成动态SQL。这实在是太麻烦了,假如查询条件更复杂,动态SQL的编写也会更加复杂。

所以,Mybatis Plus 提供了自定义 SQL 功能,可以让我们利用 Wrapper 生成查询条件,再结合Mapper.xml 编写 SQL 语句。

2.1 自定义 SQL 的基本用法

对于上面的例子,我们可以改写成自定义 SQL 的形式。

首先,在UserMapper 接口中新增一个方法updateBalanceByIds

public interface UserMapper extends BaseMapper<User> {
    void updateBalanceByIds(@Param("ew") QueryWrapper<User> wrapper, @Param("money") int money);
}

然后,在 UserMapper.xml 中编写该方法对应的 SQL 语句:

<update id="updateBalanceByIds">
    UPDATE user SET balance = balance - ${money} ${ew.customSqlSegment}
</update>

注意,最后使用${ew.customSqlSegment}拼接由 Wrapper 构造的查询条件。

然后,创建一个单元测试方法testCustomSQLForUpdate来测试我们的自定义SQL是否生效:

@Test
void testCustomSQLForUpdate() {
    // 1. 要扣减的金额
    int amount = 200;
    // 2. 准备查询条件
    QueryWrapper<User> wrapper = new QueryWrapper<User>().in("id", Arrays.asList(1, 2, 4));
    // 3. 执行更新
    userMapper.updateBalanceByIds(wrapper, amount);
}

运行结果:

2.2 自定义 SQL 实现多表查询

MyBatis Plus 本身不支持多表查询,但我们可以使用 Wrapper 来自定义多表查询的条件以达到多表查询的效果。例如,现在有一个地址表,其结构如下:

通过用户id与之关联,现在要查询出地址在北京,并且用户id在 1,2,4 之间的用户,如果使用 MyBatis,要编写的SQL语句如下:

<select id="queryUserByIdAndAddr" resultType="com.demo.mp.domain.po.User">
      SELECT *
      FROM user u
      INNER JOIN address a ON u.id = a.user_id
      WHERE u.id
      <foreach collection="ids" separator="," item="id" open="IN (" close=")">
          #{id}
      </foreach>
      AND a.city = #{city}
  </select>

可以看出其中最复杂的就是WHERE条件的编写,如果业务复杂一些,这里的SQL会更加复杂。但是基于自定义 SQL 结合 Wrapper 的写法,我们就可以利用 Wrapper 来构建查询条件,然后手写SELECTFROM部分,实现多表查询。

首先,查询条件可以像下面这样:

@Test
void testCustomJoinAddress() {
    // 1. 准备查询条件
    QueryWrapper<User> wrapper = new QueryWrapper<User>()
            .in("u.id", Arrays.asList(1, 2, 4))
            .eq("a.city", "北京");

    // 2. 调用自定义方法
    List<User> users = userMapper.queryUserByIdAndAddr(wrapper);
    users.forEach(System.out::println);
}

其中,u代表user表,a 代表 address 表。

然后,在UserMapper接口中实现queryUserByIdAndAddr方法:

@Select("SELECT u.* FROM user u INNER JOIN address a ON u.id = a.user_id ${ew.customSqlSegment}")
List<User> queryUserByIdAndAddr(@Param("ew") QueryWrapper<User> wrapper);

注意,此时可以直接将SQL语句以注解的方式写在对应的方法上面。当然,也可以在UserMapper.xml中写 SQL:

<select id="queryUserByIdAndAddr" resultType="com.demo.mp.domain.po.User">
    SELECT * FROM user u INNER JOIN address a ON u.id = a.user_id ${ew.customSqlSegment}
</select>

三、Service 接口

3.1 对 Service 接口的认识

Mybatis Plus 不仅提供了 BaseMapper接口,还提供了通用的Service层接口及默认实现,封装了一些常用的service模板方法。

其中通用接口为IService,默认实现为ServiceImpl,其中封装的方法可以分为以下几类:

  • save:新增
  • remove:删除
  • update:更新
  • get:查询单个结果
  • list:查询集合结果
  • count:计数
  • page:分页查询

首先让我们来看看 IService接口中增删改查相关功能的方法:
新增:

  • save是新增单个元素
  • saveBatch是批量新增
  • saveOrUpdate是根据id判断,如果数据存在就更新,不存在则新增
  • saveOrUpdateBatch是批量的新增或修改

删除:

  • removeById:根据id删除
  • removeByIds:根据id批量删除
  • removeByMap:根据Map中的键值对为条件删除
  • remove(Wrapper<T>):根据Wrapper条件删除

修改:

  • updateById:根据id修改
  • update(Wrapper<T>):根据UpdateWrapper修改,Wrapper中包含setwhere部分
  • update(T,Wrapper<T>):按照T内的数据修改与Wrapper匹配到的数据
  • updateBatchById:根据id批量修改

Get 相关方法:

  • getById:根据id查询1条数据
  • getOne(Wrapper<T>):根据Wrapper查询1条数据
  • getBaseMapper:获取Service内的BaseMapper实现,某些时候需要直接调用Mapper内的自定义SQL时可以用这个方法获取到Mapper

获取 List 相关方法:

  • listByIds:根据id批量查询
  • list(Wrapper<T>):根据Wrapper条件查询多条数据
  • list():查询所有

Count计数相关方法:

  • count():统计所有数量
  • count(Wrapper<T>):统计符合Wrapper条件的数据数量

3.2 实现 Service 接口

由于Service层中经常需要定义与业务有关的自定义方法,因此我们不能直接使用IService,而是自定义Service接口,然后继承IService以拓展方法。同时,让自定义的Service实现类继承ServiceImpl,这样就不用自己实现IService中的接口了。

首先,定义IUserService,继承IService接口:

public interface IUserService extends IService<User> {
}

然后,编写UserServiceImpl类,继承ServiceImpl类,实现UserService接口:

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
}

最终,Service的目录结构如下:

3.3 实现增删改查功能

上面已经实现了Service层的功能,接下来我们就能够快速实现下面 4 个接口:

编号接口请求方式请求路径请求参数返回值
1新增用户POST/users用户表单实体
2删除用户DELETE/users/{id}用户id
3根据id查询用户GET/users/{id}用户id用户 VO
4根据id批量查询GET/users用户id集合用户 VO 集合

首先,需要为这些接口实现两个实体类:

  • UserFormDTO:代表新增时的用户表单;
  • UserVO:代表查询的返回结果。

首先是UserFormDTO

@Data
@ApiModel(description = "用户表单实体")
public class UserFormDTO {

    @ApiModelProperty("id")
    private Long id;

    @ApiModelProperty("用户名")
    private String username;

    @ApiModelProperty("密码")
    private String password;

    @ApiModelProperty("注册手机号")
    private String phone;

    @ApiModelProperty("详细信息,JSON风格")
    private String info;

    @ApiModelProperty("账户余额")
    private Integer balance;
}

然后是UserVO

@Data
@ApiModel(description = "用户VO实体")
public class UserVO {

    @ApiModelProperty("用户id")
    private Long id;

    @ApiModelProperty("用户名")
    private String username;

    @ApiModelProperty("详细信息")
    private String info;

    @ApiModelProperty("使用状态(1正常 2冻结)")
    private Integer status;

    @ApiModelProperty("账户余额")
    private Integer balance;
}

最后,按照RESTFul风格编写Controller接口方法:

@Api(tags = "用户管理接口")
@RequestMapping("/user")
@RestController
public class UserController {
    @Autowired
    private IUserService userService;

    @PostMapping
    @ApiOperation("新增用户接口")
    public void saveUser(@RequestBody UserFormDTO userFormDTO){
        // 1. 将 DTO 转为 PO
        User user = BeanUtil.copyProperties(userFormDTO, User.class);
        // 2. 保存
        userService.save(user);
    }

    @DeleteMapping("/{id}")
    @ApiOperation("根据id删除用户接口")
    public void removeUserById(@PathVariable("id") Long id){
        userService.removeById(id);
    }
    
    @GetMapping("/{id}")
    @ApiOperation("根据id查询用户接口")
    public UserVO queryUserById(@PathVariable("id") Long id){
        // 1. 查询 PO 对象
        User user = userService.getById(id);
        // 2. 将 PO 转换为 VO 并返回
        return BeanUtil.copyProperties(user, UserVO.class);
    }

    @GetMapping
    @ApiOperation("根据id批量查询用户接口")
    public List<UserVO> queryUserByIds(@RequestParam("ids") List<Long> ids){
        // 1. 查询 PO 对象集合
        List<User> users = userService.listByIds(ids);
        // 2. 将 PO 转换为 VO 并返回
        return BeanUtil.copyToList(users, UserVO.class);
    }
}

可以看到上述接口都直接在 controller 即可实现,无需编写任何service代码,非常方便。

通过上面的controller类可以发现实现单表简单的增删改查操作非常简单,不过,一些带有业务逻辑的接口则需要在service中自定义实现了。

例如这个需求: 根据id扣减用户余额。

这看起来是个简单修改功能,只要修改用户余额即可。但这个业务包含一些业务逻辑处理:

  • 判断用户状态是否正常;
  • 判断用户余额是否充足。

这些业务逻辑都要在service层来做,另外更新余额需要自定义 SQL,要在mapper中来实现。因此,我们除了要编写controller以外,具体的业务还要在servicemapper中编写。

  1. 首先在UserController中定义一个方法:
@PostMapping("{id}/deduct/{money}")
@ApiOperation("根据id扣减用户余额")
public void deductBalanceById(@PathVariable("id") Long id, @PathVariable("money") Integer money){
    userService.deductBalanceById(id, money);
}
  1. 然后是UserService接口:
public interface IUserService extends IService<User> {
    void deductBalanceById(Long id, Integer money);
}
  1. 再然后是UserServiceImpl实现类:
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Override
    public void deductBalanceById(Long id, Integer money) {
        // 1. 查询用户
        User user = getById(id);
        // 2. 判断用户状态
        if(user == null || user.getStatus() == 2){
            throw new RuntimeException("用户状态异常!");
        }
        // 3. 判断用户余额
        if(user.getBalance() < money){
            throw new RuntimeException("用户余额不足!");
        }

        // 4. 扣减余额
        baseMapper.deductBalanceById(id, money);
    }
}
  1. 最后是 Mapper
public interface UserMapper extends BaseMapper<User> {
    @Update("UPDATE user SET balance = balance - #{money} WHERE id = #{id}")
    void deductBalanceById(@Param("id") Long id, @Param("money") Integer money);
}

3.4 Lambda 功能

IService接口中也提供了Lambda功能来简化我们的复杂查询及更新功能。让我们通过下面两个案例来了解一下如何使用这个 Lambda 功能。

案例一:使用lambdaUpdate 改写上述扣减余额的功能

此时,我们只需要修改 UserServiceImpldeductBalanceById方法的实现代码即可:

@Override
public void deductBalanceById(Long id, Integer money) {
    // 1. 查询用户
    User user = getById(id);
    // 2. 判断用户状态
    if(user == null || user.getStatus() == 2){
        throw new RuntimeException("用户状态异常!");
    }
    // 3. 判断用户余额
    if(user.getBalance() < money){
        throw new RuntimeException("用户余额不足!");
    }

    // 4. 扣减余额
    int remainBalance = user.getBalance() - money;
    lambdaUpdate()
            .set(User::getBalance, remainBalance)
            .set(remainBalance == 0, User::getStatus, 2) // 如果用户余额为 0 就冻结
            .eq(User::getId, id)
            .eq(User::getBalance, user.getBalance()) // CAS 乐观锁判断此期间用户余额是否被修改
            .update();
}

此时可以直接使用 lambdaUpdate 来构造我们的SQL语句,并在最后调用 update 执行这些SQL语句。对于上述代码有以下值得注意的地方:

  1. 当用户的余额为 0 的时候,就冻结该用户,即改变 status 字段。注意,set方法的第一个参数是一个条件判断语句,如果该条件为真,则在SQL执行该SET操作;
  2. 在执行update 操作之前,使用乐观锁 CAS 来判断余额是否被修改,保证在并发时的线程安全问题。

案例二:使用 lambdaQuery 实现一个根据复杂条件查询用户的接口,查询条件如下:

  • name:用户名关键字,可以为空
  • status:用户状态,可以为空
  • minBalance:最小余额,可以为空
  • maxBalance:最大余额,可以为空

可以将上述需求理解成一个用户的后台管理界面,管理员可以自己选择条件来筛选用户,因此上述条件不一定存在,需要做判断。

此时,由于前端的参数传入的可能性比较多,因此就直接使用一个 UserQuery 的实体来进行接收:

@Data
@ApiModel(description = "用户查询条件实体")
public class UserQuery {
    @ApiModelProperty("用户名关键字")
    private String name;
    @ApiModelProperty("用户状态:1-正常,2-冻结")
    private Integer status;
    @ApiModelProperty("余额最小值")
    private Integer minBalance;
    @ApiModelProperty("余额最大值")
    private Integer maxBalance;
}

Controller 方法:

@GetMapping("/list")
@ApiOperation("根据复杂条件查询用户集合接口")
public List<UserVO> queryUsers(UserQuery userQuery){
    // 1. 查询 PO 对象集合
    List<User> users = userService.queryUsers(userQuery);
    // 2. 将 PO 转换为 VO 并返回
    return BeanUtil.copyToList(users, UserVO.class);
}

IUserService接口:

public interface IUserService extends IService<User> {
    List<User> queryUsers(UserQuery userQuery);
}

UserServiceImpl实现类:

@Override
public List<User> queryUsers(UserQuery userQuery) {
    String name = userQuery.getName();
    Integer status = userQuery.getStatus();
    Integer minBalance = userQuery.getMinBalance();
    Integer maxBalance = userQuery.getMaxBalance();

    return lambdaQuery()
            .like(name != null, User::getUsername, name)
            .eq(status != null, User::getStatus, status)
            .ge(minBalance != null, User::getBalance, minBalance)
            .le(maxBalance != null, User::getBalance, maxBalance)
            .list();
}

在链式编程的最后添加一个list(),这是在告诉 MyBatis Plus 我们的调用结果需要是一个list集合。这里不仅可以用list(),可选的方法有:

  • .one():最多1个结果;
  • .list():返回集合结果;
  • .count():返回计数结果。

Mybatis Plus会根据链式编程的最后一个方法来判断最终的返回结果。

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

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

相关文章

HarmonyOS开发:探索组件化模式开发

前言 组件化一直是移动端比较流行的开发方式&#xff0c;有着编译运行快&#xff0c;业务逻辑分明&#xff0c;任务划分清晰等优点&#xff0c;针对Android端的组件化&#xff0c;之前有比较系统的总结过相关文章&#xff0c;感兴趣的朋友&#xff0c;可以查看&#xff0c;点击…

java集成海康预览抓图出现内存一直上涨问题

求助&#xff1a;在java 中集成海康sdk后批量抓图出现内存上涨问题&#xff0c;不论是预览后不关闭继续预览&#xff0c;还是预览后关闭预览&#xff0c;然后重新预览都没有解决这个问题&#xff08;抓图正常&#xff09;&#xff0c;尝试使用第三方解码器ffmpeg来进行解码&…

(PyTorch)PyTorch中的常见运算(*、@、Mul、Matmul)

1. 矩阵与标量 矩阵&#xff08;张量&#xff09;每一个元素与标量进行操作。 import torch a torch.tensor([1,2]) print(a1) >>> tensor([2, 3]) 2. 哈达玛积&#xff08;Mul&#xff09; 两个相同尺寸的张量相乘&#xff0c;然后对应元素的相乘就是这个哈达玛…

Qwt QwtLegend和QwtPlotLegendItem图例类详解

1.概述 QwtLegend类是Qwt绘图库中用于显示图例的类。图例用于标识不同曲线、绘图元素或数据的意义&#xff0c;以便用户能够更好地理解图表中的数据。通过QwtLegend类&#xff0c;可以方便地在图表中添加、删除和设置图例的位置、方向和样式等属性。 QwtPlotLegendItem类是Qwt…

浏览器事件循环 (event loop)

进程与线程 进程 进程的概念 进程是操作系统中的一个程序或者一个程序的一次执行过程&#xff0c;是一个动态的概念&#xff0c;是程序在执行过程中分配和管理资源的基本单位&#xff0c;是操作系统结构的基础。 简单的来说&#xff0c;就是一个程序运行开辟的一块内存空间&a…

听GPT 讲Rust源代码--library/std(3)

rust标准库std中的src目录主要包含以下内容和模块: alloc:内存分配相关函数,比如alloc::boxed::Box、alloc::string::String等。 ascii:ASCII相关工具函数。 char:字符相关类型和函数,如Char、char等。 cmp:比较相关trait和函数,如Ord、Eq、PartialOrd等。 env:环境变量相关功能…

Fourier分析导论——第1章——Fourier分析的起源(E.M. Stein R. Shakarchi)

第 1 章 Fourier分析的起源 (The Genesis of Fourier Analysis) Regarding the researches of dAlembert and Euler could one not add that if they knew this expansion, they made but a very imperfect use of it. They were both persuaded that an arbitrary and d…

vscode连接服务器一直retry

解决方法 打开vscode控制面板&#xff0c;输入命令remote-ssh: kill vs code server on host 选择一直连接不上的服务器端口 重新连接

STM32G030F6P6点灯闪烁

前言 &#xff08;1&#xff09;如果有嵌入式企业需要招聘湖南区域日常实习生&#xff0c;任何区域的暑假Linux驱动实习岗位&#xff0c;可C站直接私聊&#xff0c;或者邮件&#xff1a;zhangyixu02gmail.com&#xff0c;此消息至2025年1月1日前均有效 &#xff08;2&#xff0…

EtherNet/IP转profienrt协议网关连接EtherNet/IP协议的川崎机器人配置方法

EthernetIP 协议一般用于采集机器人&#xff0c;控制器等设备的数据。 下面介绍通过远创智控YC-EIPM-PN网关把EtherNet/IP协议的川崎机器人通过西门子1500PLC的控制方法。有些 EIP 的从站设备提供了 EDS 文件&#xff0c;可以从EDS 文件中获取点位信息。这些信息是需要填写到网…

Unity的live2dgalgame多语言可配置剧情框架

这段代码用于读取表格 using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using OfficeOpenXml; using System.IO; using UnityEngine.Networking; using UnityEngine.UI; using Random UnityEngine.Random;public class Plots…

unocss和tailwindcss css原子引擎

第一种tailwindcss&#xff1a; tailwindcss官网 https://tailwindcss.com/docs/grid-column 基本介绍及优点分析 Tailwind CSS 中文文档 - 无需离开您的HTML&#xff0c;即可快速建立现代网站 PostCss 处理 Tailwind Css 基本流程 PostCSS - 是一个用 JavaScript 工具和插…

java基础 集合2

前9点&#xff0c;在另一篇作品中&#xff0c;可以从集合1开始观看 9.List遍历方式&#xff1a; 10.Arraylist底层原理&#xff1a; 11.Linklist底层原理&#xff1a; 1.LinkedList做队列和栈&#xff1a; package day01;import java.util.ArrayList; import java.util.I…

深度学习与计算机视觉(一)

文章目录 计算机视觉与图像处理的区别人工神经元感知机 - 分类任务Sigmoid神经元/对数几率回归对数损失/交叉熵损失函数梯度下降法- 极小化对数损失函数线性神经元/线性回归均方差损失函数-线性回归常用损失函数使用梯度下降法训练线性回归模型线性分类器多分类器的决策面 soft…

Hadoop、Hive安装

一、 工具 Linux系统&#xff1a;Centos&#xff0c;版本7.0及以上 JDK&#xff1a;jdk1.8 Hadoop&#xff1a;3.1.3 Hive&#xff1a;3.1.2 虚拟机&#xff1a;VMware mysql&#xff1a;5.7.11 工具下载地址: https://pan.baidu.com/s/1JYtUVf2aYl5–i7xO6LOAQ 提取码: xavd…

项目经验分享|openGauss 陈贤文:受益于开源,回馈于开源

开源之夏 项目经验分享 2023 #08 # 关于 openGauss 社区 openGauss是一款开源关系型数据库管理系统&#xff0c;采用木兰宽松许可证v2发行。openGauss内核深度融合华为在数据库领域多年的经验&#xff0c;结合企业级场景需求&#xff0c;持续构建竞争力特性。同时openGauss也是…

基于FPGA的图像PSNR质量评估计算实现,包含testbench和MATLAB辅助验证程序

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 设置较大的干扰&#xff0c;PSNR15。 设置较小的干扰&#xff0c;PSNR25。 2.算法运行软件版本 matlab2022a vivado2019.2 3.部分核心程序 ti…

如何在Postman中使用静态HTTP

首先&#xff0c;打开 Postman 软件。在 Postman 的菜单栏中&#xff0c;点击 “Preferences”&#xff08;偏好设置&#xff09;。 亲身经验&#xff1a;我自己尝试了这个方法&#xff0c;发现它非常适用于需要使用HTTP的场景。 数据和引证&#xff1a;根据 Postman 官方文档…

皮卡丘RCE靶场通关攻略

皮卡丘RCE靶场通关攻略 文章目录 皮卡丘RCE靶场通关攻略RCE(remote command/code execute)概述远程系统命令执行启动环境漏洞练习第一关exec "ping"第二关 exec "eval" RCE(remote command/code execute)概述 RCE漏洞&#xff0c;可以让攻击者直接向后台服…

Maven第三章:IDEA集成与常见问题

Maven第三章:IDEA集成与常见问题 前言 本章内容重点:了解如何将Maven集成到IDE(如IntelliJ IDEA或Eclipse)中,以及使用过程中遇到的常见的问题、如何解决,如何避免等,可以大大提高开发效率。 IEAD导入Maven项目 File ->Open 选择上一章创建的Maven项目 my-app查看po…