java程序员入行科目一之CRUD轻松入门教程(四)

news2024/11/15 15:50:23

三层架构

所谓的三层:

  • DAO层
    • 也可以叫做Mapper或者是Repository层,名字无所谓,但是要清楚这层就是专门和数据库做交互的,一般指的是关系型数据库
  • Service层
    • 数据的加工处理,调动DAO层去完成一些具体的业务实现,包括事务的控制。
  • Web层
    • 现在还没有学习关于Web的知识,现在基本都是根据main方法或者是控制台输入来实现一些交互,到了后期,会通过前端的页面或者其他的方式和这层做基本交互。
    • 手机用户的数据和需求,并且给用户返回需要展示的数据。

其次是关于包的命名

image.png

  • dao:放和数据库交互的DAO层接口和实现类
  • entity:放实体类,实体类是和数据库中的表做映射的。
  • service:放做业务处理的service层的接口和实现类。
  • utils:一般存放一些通用性的工具。
  • web:后期会存放Servlet的东西,完成和页面之间的交互(现在还没学!!)

DAOUtils

在DAO层中,现在依然有大量的冗余代码在,比如获取连接,什么PreparedStatement等等的操作,都是重复性的,能不能再次封装一封,让DAO层的操作变的更简单

  • 写操作:
    • SQL语句是什么?
    • 占位符要赋什么值?
  • 读操作:
    • SQL语句是什么?
    • 占位符要赋什么值?
    • 返回结果的封装?

封装写操作

声明一个DaoUtils的工具类,在这个工具类中完成写操作的封装。

package com.jimihua.utils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * 将写操作和读操作再做一层封装!
 */
public class DaoUtils {


    /**
     * 公共处理增删改三个操作的方法
     * @param sql   SQL语句
     * @param args  占位符要赋的值
     * @return
     */
    public static int commonUpdate(String sql,Object... args) throws SQLException {
        Connection conn = null;
        PreparedStatement ps = null;
        int count = 0;
        try {
            //1、获取连接
            conn = DatabaseUtils.getConnection();
            //2、构建PreparedStatement对象
            ps = conn.prepareStatement(sql);
            //3、给占位符赋值
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1, args[i]);
            }
            //4、执行SQL,获取返回结果
            count = ps.executeUpdate();
        }finally {
            //5、释放资源
            DatabaseUtils.closeAll(null,ps);
        }
        //6、响应返回结果
        return count;
    }

}

去优化PersonDao和AccountDao中的各种写操作进行优化,简化代码!

封装读操作

读操作需要考虑一下执行SQL语句之后,会拿到ResultSet,需要将Result封装对应实体类。

需要提供一个方法,这个方式可以基于泛型指定具体的类型,不同的实体类各自去重写方法实现即可。

因为之前DaoUtils的工具,提供了static方法,但是static方法不能和泛型一起用,为了解决,将之前的写操作的通用方法改造成非静态方法。

上述操作完成后,优先声明一个接口,RowMapper。

package com.jimihua.rowmapper;

import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * 向上抽取一个RowMapper接口,提供一个将resultSet封装为对应实体类的方法
 * @param <T>
 */
public interface RowMapper<T> {


    /**
     * 这个方法需要不同的Dao,不同的实体类各自去实现即可。
     * @param rs
     * @return
     * @throws SQLException
     */
    T rowMapper(ResultSet rs) throws SQLException;

}


在DaoUtils工具类中,声明对应的公共查询方法

package com.jimihua.utils;

import com.jimihua.rowmapper.RowMapper;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * 将写操作和读操作再做一层封装!
 */
public class DaoUtils<T> {


    /**
     * 公共处理增删改三个操作的方法
     * @param sql   SQL语句
     * @param args  占位符要赋的值
     * @return
     */
    public int commonUpdate(String sql,Object... args) throws SQLException {
        Connection conn = null;
        PreparedStatement ps = null;
        int count = 0;
        try {
            //1、获取连接
            conn = DatabaseUtils.getConnection();
            //2、构建PreparedStatement对象
            ps = conn.prepareStatement(sql);
            //3、给占位符赋值
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1, args[i]);
            }
            //4、执行SQL,获取返回结果
            count = ps.executeUpdate();
        }finally {
            //5、释放资源
            DatabaseUtils.closeAll(null,ps);
        }
        //6、响应返回结果
        return count;
    }



    public List<T> commonSelect(String sql, RowMapper<T> rowMapper,Object... args) throws SQLException {
        //1、获取连接
        Connection conn = DatabaseUtils.getConnection();

        //2、基于SQL构建PreparedStatement
        PreparedStatement ps = conn.prepareStatement(sql);

        //3、占位符赋值
        for (int i = 0; i < args.length; i++) {
            ps.setObject(i + 1,args[i]);
        }

        //4、执行SQL,拿到ResultSet结果集
        ResultSet resultSet = ps.executeQuery();

        //5、封装结果集
        List<T> list = new ArrayList<T>();
        while(resultSet.next()){
            T t = rowMapper.rowMapper(resultSet);
            list.add(t);
        }

        //6、释放资源
        DatabaseUtils.closeAll(conn,ps,resultSet);

        //7、响应返回结果
        return list;
    }
}


为了可以在具体的Dao中使用DaoUtils的公共查询方法,需要将RowMapper做对应的实现

PersonRowMapper:

package com.jimihua.rowmapper.impl;

import com.jimihua.entity.Person;
import com.jimihua.rowmapper.RowMapper;

import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * 将ResultSet结果集封装为Person对象的方法
 */
public class PersonRowMapper implements RowMapper<Person> {


    @Override
    public Person rowMapper(ResultSet resultSet) throws SQLException {
        Person person = new Person();
        person.setId(resultSet.getInt("id"));
        person.setName(resultSet.getString("name"));
        person.setAge(resultSet.getInt("age"));
        person.setBornDate(resultSet.getDate("born_date"));
        person.setEmail(resultSet.getString("email"));
        person.setAddress(resultSet.getString("address"));
        return person;
    }
}

AccountRowMapper:

package com.jimihua.rowmapper.impl;

import com.jimihua.entity.Account;
import com.jimihua.rowmapper.RowMapper;

import java.sql.ResultSet;
import java.sql.SQLException;

/**
 *  将ResultSet封装为Account对象的RowMapper
 */
public class AccountRowMapper implements RowMapper<Account> {

    @Override
    public Account rowMapper(ResultSet rs) throws SQLException {
        Account account = new Account();
        account.setId(rs.getInt("id"));
        account.setName(rs.getString("name"));
        account.setCardNo(rs.getString("card_no"));
        account.setPwd(rs.getString("pwd"));
        account.setMoney(rs.getBigDecimal("money"));
        return account;
    }
}


改造对应的DaoImpl实现的查询方法

PersonDaoImpl:

package com.jimihua.dao.impl;

import com.jimihua.dao.PersonDao;
import com.jimihua.entity.Person;
import com.jimihua.rowmapper.impl.PersonRowMapper;
import com.jimihua.utils.DaoUtils;

import java.sql.SQLException;
import java.util.List;

/**
 * 实现PersonDao接口,实现内5个方法。
 */
public class PersonDaoImpl implements PersonDao {


    private DaoUtils<Person> daoUtils = new DaoUtils();

    // 省略写操作代码

    @Override
    public Person findById(Integer id) throws Exception {
        //1、编写SQL语句
        String sql = "select * from person where id = ?";

        //2、调用DaoUtils
        List<Person> list = daoUtils.commonSelect(sql, new PersonRowMapper(), id);

        //3、返回结果
        return list != null && list.size() > 0 ? list.get(0) : null;
    }

    @Override
    public List<Person> findAll() throws SQLException {

        //1、编写SQL语句
        String sql = "select * from person";

        //2、调用DaoUtils
        List<Person> list = daoUtils.commonSelect(sql, new PersonRowMapper());

        //3、返回结果
        return list;
    }
}

AccountDaoImpl:

package com.jimihua.dao.impl;

import com.jimihua.dao.AccountDao;
import com.jimihua.entity.Account;
import com.jimihua.rowmapper.impl.AccountRowMapper;
import com.jimihua.utils.DaoUtils;

import java.sql.SQLException;
import java.util.List;

public class AccountDaoImpl  implements AccountDao {

    private DaoUtils<Account> daoUtils = new DaoUtils();

    @Override
    public Account findByCardNo(String cardNo) throws SQLException {
        //1、编写SQL语句
        String sql = "select * from account where card_no = ?";

        //2、执行daoUtils查询
        List<Account> list = daoUtils.commonSelect(sql, new AccountRowMapper(), cardNo);

        //8、返回结果
        return list != null && list.size() > 0 ? list.get(0) : null;
    }
    // 省略修改操作

}

Druid连接池

在和数据库交互的时候,需要基于DriverManager去构建一个Connection对象,与数据库交互完毕后,还需要将这个Connection对象释放掉。这样 频繁的创建和释放Connection对象有点消耗资源。

连接池的池化技术就是来解决这个问题的。

Ps:其次连接池也可以更好的对Connection的操作做监控,以及根据项目的情况更好的去指定Connection的个数。

准备Druid连接池jar文件

依然是去mvnrepository.com中去下载对应的Druid连接池的jar文件

https://mvnrepository.com/artifact/com.alibaba/druid/1.2.15

下载完毕后,记得将jar包添加到工程中。

image.png

改造DatabaseUtils

之前获取连接对象,依然是通过DriverManager去get的,现在需要先初始化好 DruidDataSource 对象,并且设置好他需要的核心信息,后期再获取Connection时,要基于 DruidDataSource 去get到。

其次,在执行连接池提供的Connection的close方法时,会从原来的释放资源,变为归还给连接池,释放资源的代码不需要动!

初始化 DruidDataSource时,需要将连接数据库的几个信息交给 DruidDataSource ,最核心的是四个。

  • 驱动类的全路径
  • 连接数据库的url信息
  • 用户名
  • 密码

DatabaseUtils改造后的内容:

package com.jimihua.utils;


import com.alibaba.druid.pool.DruidDataSource;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

/**
 * 数据库操作的一个通用的内容
 */
public class DatabaseUtils {

    private static DruidDataSource dataSource = new DruidDataSource();

    private static final Properties PROP = new Properties();

    private static final ThreadLocal<Connection> tl = new ThreadLocal<>();

    // 加载database.properties文件。 同时将信息设置给dataSource   
    static{
        try {
            // 优先通过PROP对象,加载database.properties文件
            InputStream is = DatabaseUtils.class.getResourceAsStream("/database.properties");
            PROP.load(is);
            // 给dataSource设置四个核心信息
            dataSource.setDriverClassName(PROP.getProperty("jdbc.driver"));
            dataSource.setUrl(PROP.getProperty("jdbc.url"));
            dataSource.setUsername(PROP.getProperty("jdbc.username"));
            dataSource.setPassword(PROP.getProperty("jdbc.password"));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

 

    /**
     * 获取Connection对象方法
     * @return
     */
    public static Connection getConnection() {
        //1、先尝试从ThreadLocal中获取。
        Connection conn = tl.get();
        if (conn == null) {
            //2、没获取到,基于DriverManager获取Connection,然后添加到ThreadLocal中
            try {
                 // 基于连接池获取Connection对象
                conn = dataSource.getConnection();
            } catch (SQLException e) {
                throw new RuntimeException("获取Connection出错!");

            }
            tl.set(conn);
        }
        //3、获取到了,直接返回
        return conn;
    }
    // 省略部分功能代码
}

Apache的DBUtils

Commons DBUtils的工具,是Apache组织提供的一个对JDBC进行简单封装的开源工具类库。他可以简化咱们和数据库交互的操作,让更多的精力都放在编写SQL语句上。

DBUtils基本使用(增删改)

1、下载jar包。

2、将jar导入到项目当中。

3、需要给DatabaseUtils提供一个返回dataSource的静态方法。

4、可以在需要操作的DaoImpl类中,声明一个QueryRunner的类,传入dataSource。

5、完成基本的写操作。


下载jar包:直接去mvnrepository.com去下载。

https://mvnrepository.com/artifact/commons-dbutils/commons-dbutils/1.8.1

将下载好的jar文件,添加到项目中………………

导入完毕后,测试一下,在任意类中编写QueryRunner得有提示…………


给DatabaseUtils提供返回DataSource的静态方法

-public class DatabaseUtils {

 private static DruidDataSource dataSource = new DruidDataSource();

 public static DataSource getDataSource(){
     return dataSource;
 }
 // 省略其他代码
}

根据数据库中仅存的user表,提供对应的UserDao以及UserDaoImpl。

在UserDaoImpl中去初始化QueryRunner,并传入DataSource

package com.jimihua.dao.impl;

import com.jimihua.dao.UserDao;
import com.jimihua.entity.User;
import com.jimihua.utils.DatabaseUtils;
import org.apache.commons.dbutils.QueryRunner;

public class UserDaoImpl  implements UserDao {

 // 初始化QueryRunner
 private QueryRunner queryRunner = new QueryRunner(DatabaseUtils.getDataSource());

 @Override
 public int insert(User user) {
     return 0;
 }
}


在这完成增删改操作。

package com.jimihua.dao.impl;

import com.jimihua.dao.UserDao;
import com.jimihua.entity.User;
import com.jimihua.utils.DatabaseUtils;
import org.apache.commons.dbutils.QueryRunner;

import java.sql.SQLException;

public class UserDaoImpl  implements UserDao {

 private QueryRunner queryRunner = new QueryRunner(DatabaseUtils.getDataSource());

 @Override
 public int insert(User user) throws SQLException {
     // 准备SQL
     String sql = "insert into user(username,password) values(?,?)";
     // 基于QueryRunner执行SQL语句,传入占位符参数,获取结果
     int count = queryRunner.update(sql, user.getUsername(), user.getPassword());
     // 返回几行受影响
     return count;
 }

 @Override
 public int updateById(User user) throws SQLException {
     String sql = "update user set username=?,password=? where id=?";
     int count = queryRunner.update(sql, user.getUsername(), user.getPassword(), user.getId());
     return count;
 }

 @Override
 public int deleteById(Long id) throws SQLException {
     String sql = "delete from user where id=?";
     int count = queryRunner.update(sql, id);
     return count;
 }
}

查询操作

查询操作相比写操作,只需要多关注一下返回的ResultSet结果集如何封装为对应的entity对象。

这个操作在DBUtils中,他已经提供对应的方式。

首先DBUtils的查询操作要调用QueryRunner的query方法执行SQL。

至于返回结果的封装,可以在query方法中基于传入不同Bean,可以让DBUtils做不同封装。

  • BeanHandler:将一行返回结果,封装为一个对象。当前query方法返回一个entity实例。
  • BeanListHandler:将多行返回结果,封装为一个集合。当前query方法返回一个List<entity>实例。
  • ScalarHandler:只返回第一行第一列的一个结果。

直接完成针对UserDaoImpl中的查询操作

package com.jimihua.dao.impl;

import com.jimihua.dao.UserDao;
import com.jimihua.entity.User;
import com.jimihua.utils.DatabaseUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;

import java.sql.SQLException;
import java.util.List;

public class UserDaoImpl  implements UserDao {

 private QueryRunner queryRunner = new QueryRunner(DatabaseUtils.getDataSource());

 // 省略写操作代码

 @Override
 public User selectById(Long id) throws SQLException {
     String sql = "select * from user where id = ?";
     User user = queryRunner.query(sql, new BeanHandler<User>(User.class), id);
     return user;
 }

 @Override
 public List<User> selectAll() throws SQLException {
     String sql = "select * from user";
     List<User> userList = queryRunner.query(sql, new BeanListHandler<User>(User.class));
     return userList;
 }

 @Override
 public Long selectCount() throws SQLException {
     String sql = "select count(*) from user";
     Long count = queryRunner.query(sql, new ScalarHandler<Long>());
     return count;
 }
}

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

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

相关文章

【浅谈】单片机基本概念

这里写目录标题 一 引言二 存储器1 ROM&#xff08;read only memory&#xff09;2 RAM (random access memory)3 累加器&#xff08;ACC : accumulator&#xff09; 三 I/O口四 堆栈五 定时/计数器六 中断1、一些中断词汇&#xff1a;2 中断的响应过程&#xff1a;3 作用&…

提示词工程与 AI 使用的紧密关系

随着人工智能&#xff08;AI&#xff09;技术的快速发展&#xff0c;特别是生成式模型&#xff08;如 GPT 系列模型&#xff09;的广泛应用&#xff0c;提示词工程&#xff08;Prompt Engineering&#xff09;成为了 AI 使用中至关重要的环节。提示词工程是指通过精心设计和优化…

踩最深的坑,教会自己出海的前端选型

目录 引言 1. 前端 1.1 Next.js 1.2 Remix 1.3 Nuxt 2. 样式与UI库 2.1 Tailwind CSS 2.2 NextUI 2.3 Framer Motion 2.4 Shadcn/UI 引言 在经历了刻骨铭心的合伙创业经历后&#xff0c;我意识到是时候该独立出海了。 捡起早已深埋在心里的创业想法&#xff0c;开始…

Unity2D游戏入门

1.导入资源 在Assets下新建文件夹 Res&#xff0c;将相关素材拖入其中&#xff08;本文中的素材仅为学习使用&#xff09;。 2.菜单 设置页面大小 选择素材&#xff0c;查看素材大小。 设置游戏视图大小。 调整工作布局方便查看 记得给场景改名为MenuScene&#xff0c;与其他…

HTTPS安全吗?3个理由告诉你HTTPS为什么是安全的!

从保护银行网站到在线购物期间保护数据&#xff0c;HTTPS协议在互联网上随处可见。例如当您访问racent.com时&#xff0c;你会发现你是通过安全加密链接来访问网站的。这种安全链接会触发浏览器的网址栏旁边显示安全挂锁图标。 但HTTPS真的安全吗? 如果你想快速得到答案&…

item_get京东获取商品详情SKU数据接口||关于京东商品采集接口

jd.item_get 公共参数 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;secretString是调用密钥api_nameString是API接口名称&#xff08;包括在请求地址中&#xff09;[item_search,item_get,item_search_shop等]cacheString否[yes,no]默…

【网络安全】漏洞挖掘:php代码审计

未经许可,不得转载。 文章目录 正文正文 在应用程序中,通过一个 JavaScript 注释发现了一个备份 ZIP 文件。解压后,获取了应用程序的代码,其中包含如下代码片段: 代码首先检查变量 $action 是否等于 convert,如果是,则继续执行。随后对传入的变量 $data 使用 trim() 函…

github高级搜索

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐&#xff1a;「storm…

memcmp函数的使用

目录 1.头文件 2.memcmp函数讲解 小心&#xff01;VS2022不可直接接触&#xff0c;否则&#xff01;没这个必要&#xff0c;方源面色淡然一把抓住&#xff01;顷刻炼化&#xff01; 1.头文件 memcmp函数的使用需要包括头文件 #include<string.h> 2.memcmp函数讲解 简述…

0基础转行AI产品经理,终于有人说清楚了!

当AI成为趋势&#xff01;越来越多的产品已经或正在高度AI化&#xff0c;这个趋势正如已经完成的产品移动化一样不可阻挡。产品经理要想让自己保值增值&#xff0c;必须积极拥抱AI的大趋势。 . 学习 AI 产品经理可以参考以下书籍&#xff1a; 《人工智能产品经理——AI时代P…

AI绘画入门教程(全网最详细)从零基础入门到精通,看完这一篇就够了!

前言 AI绘画的出现&#xff0c;让越来越多的人可以轻松画出美丽的插画作品。在本篇文章中&#xff0c;我们将会使用AI绘画软件&#xff1a;触站&#xff0c;轻松创建属于自己的作品。从零开始学AI绘画&#xff01; 从零开始学AI绘画关键步骤&#xff1a; 第一步&#xff1a;…

NC 环形链表的约瑟夫问题

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 描述 编号为 1 到 …

助力汽车半导体产业发展,2025 广州国际新能源汽车功率半导体技术展览会与您相约“羊城”广州

助力汽车半导体产业发展&#xff0c;2025 广州国际新能源汽车功率半导体技术展览会与您相约“羊城”广州 随着半导体技术的升级与发展&#xff0c;功率半导体已经成为推动新能源汽车和智能汽车产业升级的关键因素。汽车不再只是单纯的交通工具&#xff0c;而是逐渐演变为一个智…

QT 串口上位机读卡显示

目录 一. QT创建工程 二. 软件更换图标 三. QT打包 一. QT创建工程 文件新建&#xff0c;选择创建一个桌面QT。 重命名RFID,并选择工程保存路径 RFID.pro QT core gui serialport #串行串口greaterThan(QT_MAJOR_VERSION, 4): QT widgetsTARGET RFID TE…

SX_VMware联网_23

利用Nat模式联网&#xff0c;NAT模式&#xff08;Network Address Translation&#xff09;&#xff1a; 在NAT模式下&#xff0c;虚拟机通过主机的网络接口访问外部网络。 虚拟机之间可以相互通信&#xff0c;也可以访问主机网络以及互联网。 虚拟机使用私有IP地址&#xff0c…

健身管理|基于java的健身管理系统小程序(源码+数据库+文档)

健身管理系统|健身管理系统小程序 目录 基于java的健身管理系统小程序 一、前言 二、系统设计 三、系统功能设计 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大厂码农|毕设布道师&…

磁盘内存大小文件树WizTree(找内存分布)

背景 我想要清理C盘&#xff0c;但是不知道那些地方占据内存最多 https://www.diskanalyzer.com/downloadWizTree is the fastest disk space analyzer for Windows. Download the latest version here. Use it to quickly locate and remove space hogs from your hard driv…

k8s(kubernetes)的PV / PVC / StorageClass(理论+实践)

NFS总是不支持PVC扩容 先来个一句话总结&#xff1a;PV、PVC是K8S用来做存储管理的资源对象&#xff0c;它们让存储资源的使用变得可控&#xff0c;从而保障系统的稳定性、可靠性。StorageClass则是为了减少人工的工作量而去自动化创建PV的组件。所有Pod使用存储只有一个原则&…

数据库的索引是什么?

索引就是类似书本的目录一样&#xff0c;拿字典来说&#xff0c;索引存储的记录地址相当于字典的页数&#xff0c;索引存储的键值相等于字典的某个字。我们可以在目录里面快速地浏览&#xff0c;找到某个关键字&#xff0c;我们在翻到具体的页数看字的解释。举例&#xff1a;我…

机器学习特征构建与特征筛选

前言 上一篇文章讲述了原始特征分析和处理&#xff0c;保障后续拿到的是干净的特征变量&#xff0c;但实际这些特征对于建模不一定是有效的&#xff0c;所以需要在原始特征的基础上&#xff0c;结合业务场景做特征变量的衍生&#xff0c;提升数据的表达能力。此外&#xff0c;…