ORM框架的发展历史

本文涉及的产品
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
简介: 最初的时候是直接通过jdbc来直接操作数据库的,如果本地数据库有一张t_user表,那么操作流程是

@[TOC]
image.png

JDBC

JDBC操作的特点

最初的时候是直接通过jdbc来直接操作数据库的,如果本地数据库有一张t_user表,那么操作流程是

// 注册 JDBC 驱动
Class.forName("com.mysql.cj.jdbc.Driver");

// 打开连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db?characterEncoding=utf-8&serverTimezone=UTC", "root", "123456");

// 执行查询
stmt = conn.createStatement();
String sql = "SELECT id,user_name,real_name,password,age,d_id from t_user where id = 1";
ResultSet rs = stmt.executeQuery(sql);

// 获取结果集
while (rs.next()) {
   
   
    Integer id = rs.getInt("id");
    String userName = rs.getString("user_name");
    String realName = rs.getString("real_name");
    String password = rs.getString("password");
    Integer did = rs.getInt("d_id");
    user.setId(id);
    user.setUserName(userName);
    user.setRealName(realName);
    user.setPassword(password);
    user.setDId(did);

    System.out.println(user);
}

具体的操作步骤是,首先在pom.xml中引入MySQL的驱动依赖,注意MySQL数据库的版本

  1. Class.forName注册驱动
  2. 获取一个Connection对象
  3. 创建一个Statement对象
  4. execute()方法执行SQL语句,获取ResultSet结果集
  5. 通过ResultSet结果集给POJO的属性赋值
  6. 最后关闭相关的资源

这种实现方式首先给我们的感觉就是操作步骤比较繁琐,在复杂的业务场景中会更麻烦。尤其是需要自己来维护管理资源的连接,如果忘记了,就很可能造成数据库服务连接耗尽。同时具体业务的SQL语句直接在代码中写死耦合性增强。每个连接都会经历这几个步骤,重复代码很多,总结上面的操作的特点:

  1. 代码重复
  2. 资源管理
  3. 结果集处理
  4. SQL耦合

JDBC优化1.0

针对常规jdbc操作的特点,可以先从代码重复和资源管理方面来优化,可以创建一个工具类来专门处理这个问题

public class DBUtils {
   
   

    private static final String JDBC_URL = "jdbc:mysql://localhost:3306/db?characterEncoding=utf-8&serverTimezone=UTC";
    private static final String JDBC_NAME = "root";
    private static final String JDBC_PASSWORD = "123456";

    private static  Connection conn;

    /**
     * 对外提供获取数据库连接的方法
     * @return
     * @throws Exception
     */
    public static Connection getConnection() throws Exception {
   
   
        if(conn == null){
   
   
            try{
   
   
                conn = DriverManager.getConnection(JDBC_URL,JDBC_NAME,JDBC_PASSWORD);
            }catch (Exception e){
   
   
                e.printStackTrace();
                throw new Exception();
            }
        }
        return conn;
    }

    /**
     * 关闭资源
     * @param conn
     */
    public static void close(Connection conn ){
   
   
        close(conn,null);
    }

    public static void close(Connection conn, Statement sts ){
   
   
        close(conn,sts,null);
    }

    public static void close(Connection conn, Statement sts , ResultSet rs){
   
   
        if(rs != null){
   
   
            try {
   
   
                rs.close();
            }catch (Exception e){
   
   
                e.printStackTrace();
            }
        }

        if(sts != null){
   
   
            try {
   
   
                sts.close();
            }catch (Exception e){
   
   
                e.printStackTrace();
            }
        }

        if(conn != null){
   
   
            try {
   
   
                conn.close();
            }catch (Exception e){
   
   
                e.printStackTrace();
            }
        }
    }
}

对应的jdbc操作代码可以简化如下

    /**
     *
     * 通过JDBC查询用户信息
     */
    public void queryUser(){
   
   
        Connection conn = null;
        Statement stmt = null;
        User user = new User();
        ResultSet rs = null;
        try {
   
   
            // 注册 JDBC 驱动
            // Class.forName("com.mysql.cj.jdbc.Driver");

            // 打开连接
            conn = DBUtils.getConnection();
            // 执行查询
            stmt = conn.createStatement();
            String sql = "SELECT id,user_name,real_name,password,age,d_id from t_user where id = 1";
            rs = stmt.executeQuery(sql);

            // 获取结果集
            while (rs.next()) {
   
   
                Integer id = rs.getInt("id");
                String userName = rs.getString("user_name");
                String realName = rs.getString("real_name");
                String password = rs.getString("password");
                Integer did = rs.getInt("d_id");
                user.setId(id);
                user.setUserName(userName);
                user.setRealName(realName);
                user.setPassword(password);
                user.setDId(did);
                System.out.println(user);
            }

        } catch (SQLException se) {
   
   
            se.printStackTrace();
        } catch (Exception e) {
   
   
            e.printStackTrace();
        } finally {
   
   
            DBUtils.close(conn,stmt,rs);
        }
    }

   /**
     * 通过JDBC实现添加用户信息的操作
     */
    public void addUser(){
   
   
        Connection conn = null;
        Statement stmt = null;
        try {
   
   
            // 打开连接
            conn = DBUtils.getConnection();
            // 执行查询
            stmt = conn.createStatement();
            String sql = "INSERT INTO T_USER(user_name,real_name,password,age,d_id)values('wangwu','王五','111',22,1001)";
            int i = stmt.executeUpdate(sql);
            System.out.println("影响的行数:" + i);
        } catch (SQLException se) {
   
   
            se.printStackTrace();
        } catch (Exception e) {
   
   
            e.printStackTrace();
        } finally {
   
   
            DBUtils.close(conn,stmt);
        }
    }

但是整体的操作步骤还是会显得比较复杂。

JDBC优化2.0

针对DML操作的方法来优化,先解决SQL耦合的问题,在DBUtils中封装DML操作的方法

    /**
     * 执行数据库的DML操作
     * @return
     */
    public static Integer update(String sql,Object ... paramter) throws Exception{
   
   
        conn = getConnection();
        PreparedStatement ps = conn.prepareStatement(sql);
        if(paramter != null && paramter.length > 0){
   
   
            for (int i = 0; i < paramter.length; i++) {
   
   
                ps.setObject(i+1,paramter[i]);
            }
        }
        int i = ps.executeUpdate();
        close(conn,ps);
        return i;
    }

然后在DML操作的时候我们就可以简化为如下步骤

    /**
     * 通过JDBC实现添加用户信息的操作
     */
    public void addUser(){
   
   
        String sql = "INSERT INTO T_USER(user_name,real_name,password,age,d_id)values(?,?,?,?,?)";
        try {
   
   
            DBUtils.update(sql,"wangwu","王五","111",22,1001);
        } catch (Exception e) {
   
   
            e.printStackTrace();
        }
    }

显然这种方式会比最初的使用要简化很多,但是在查询处理的时候还是没有解决ResultSet结果集的处理问题,所以还需要继续优化

JDBC优化3.0

针对ResultSet的优化需要从反射和元数据两方面入手,具体如下

    /**
     * 查询方法的简易封装
     * @param sql
     * @param clazz
     * @param parameter
     * @param <T>
     * @return
     * @throws Exception
     */
    public static <T> List<T> query(String sql, Class clazz, Object ... parameter) throws  Exception{
   
   
        conn = getConnection();
        PreparedStatement ps = conn.prepareStatement(sql);
        if(parameter != null && parameter.length > 0){
   
   
            for (int i = 0; i < parameter.length; i++) {
   
   
                ps.setObject(i+1,parameter[i]);
            }
        }
        ResultSet rs = ps.executeQuery();
        // 获取对应的表结构的元数据
        ResultSetMetaData metaData = ps.getMetaData();
        List<T> list = new ArrayList<>();
        while(rs.next()){
   
   
            // 根据 字段名称获取对应的值 然后将数据要封装到对应的对象中
            int columnCount = metaData.getColumnCount();
            Object o = clazz.newInstance();
            for (int i = 1; i < columnCount+1; i++) {
   
   
                // 根据每列的名称获取对应的值
                String columnName = metaData.getColumnName(i);
                Object columnValue = rs.getObject(columnName);
                setFieldValueForColumn(o,columnName,columnValue);
            }
            list.add((T) o);
        }
        return list;
    }

    /**
     * 根据字段名称设置 对象的属性
     * @param o
     * @param columnName
     */
    private static void setFieldValueForColumn(Object o, String columnName,Object columnValue) {
   
   
        Class<?> clazz = o.getClass();
        try {
   
   
            // 根据字段获取属性
            Field field = clazz.getDeclaredField(columnName);
            // 私有属性放开权限
            field.setAccessible(true);
            field.set(o,columnValue);
            field.setAccessible(false);
        }catch (Exception e){
   
   
            // 说明不存在 那就将 _ 转换为 驼峰命名法
            if(columnName.contains("_")){
   
   
                Pattern linePattern = Pattern.compile("_(\\w)");
                columnName = columnName.toLowerCase();
                Matcher matcher = linePattern.matcher(columnName);
                StringBuffer sb = new StringBuffer();
                while (matcher.find()) {
   
   
                    matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
                }
                matcher.appendTail(sb);
                // 再次调用复制操作
                setFieldValueForColumn(o,sb.toString(),columnValue);
            }
        }
    }

封装了以上方法后我们的查询操作就可以简化为

    /**
     *
     * 通过JDBC查询用户信息
     */
    public void queryUser(){
   
   
        try {
   
   
            String sql = "SELECT id,user_name,real_name,password,age,d_id from t_user where id = ?";
            List<User> list = DBUtils.query(sql, User.class,2);
            System.out.println(list);
        } catch (Exception e) {
   
   
            e.printStackTrace();
        }
    }

这样一来在操作数据库中数据的时候就只需要关注于核心的SQL操作了。当然以上的设计还比较粗糙,这时Apache 下的 DbUtils是一个很好的选择

Apache DBUtils

初始配置

DButils中提供了一个QueryRunner类,它对数据库的增删改查的方法进行了封装,获取QueryRunner的方式

    private static final String PROPERTY_PATH = "druid.properties";

    private static DruidDataSource dataSource;
    private static QueryRunner queryRunner;

    public static void init() {
   
   
        Properties properties = new Properties();
        InputStream in = DBUtils.class.getClassLoader().getResourceAsStream(PROPERTY_PATH);
        try {
   
   
            properties.load(in);
        } catch (IOException e) {
   
   
            e.printStackTrace();
        }
        dataSource = new DruidDataSource();
        dataSource.configFromPropety(properties);
        // 使用数据源初始化 QueryRunner
        queryRunner = new QueryRunner(dataSource);
    }

创建QueryRunner对象的时候我们需要传递一个DataSource对象,这时我们可以选择Druid或者Hikai等常用的连接池工具,这里用的是Druid。

druid.username=root
druid.password=123456
druid.url=jdbc:mysql://localhost:3306/db?characterEncoding=utf-8&serverTimezone=UTC
druid.minIdle=10
druid.maxActive=30

基本操作

QueryRunner中提供的方法解决了重复代码的问题,传入数据源解决了资源管理的问题。而对于ResultSet结果集的处理则是通过 ResultSetHandler 来处理。我们可以自己来实现该接口

    /**
     * 查询所有的用户信息
     * @throws Exception
     */
    public void queryUser() throws Exception{
   
   
        DruidUtils.init();
        QueryRunner queryRunner = DruidUtils.getQueryRunner();
        String sql = "select * from t_user";
        List<User> list = queryRunner.query(sql, new ResultSetHandler<List<User>>() {
   
   
            @Override
            public List<User> handle(ResultSet rs) throws SQLException {
   
   
                List<User> list = new ArrayList<>();
                while(rs.next()){
   
   
                    User user = new User();
                    user.setId(rs.getInt("id"));
                    user.setUserName(rs.getString("user_name"));
                    user.setRealName(rs.getString("real_name"));
                    user.setPassword(rs.getString("password"));
                    list.add(user);
                }
                return list;
            }
        });
        for (User user : list) {
   
   
            System.out.println(user);
        }
    }

或者用DBUtils中提供的默认的相关实现来解决

    /**
     * 通过ResultHandle的实现类处理查询
     */
    public void queryUserUseBeanListHandle() throws Exception{
   
   
        DruidUtils.init();
        QueryRunner queryRunner = DruidUtils.getQueryRunner();
        String sql = "select * from t_user";
        // 不会自动帮助我们实现驼峰命名的转换
        List<User> list = queryRunner.query(sql, new BeanListHandler<User>(User.class));
        for (User user : list) {
   
   
            System.out.println(user);
        }
    }

通过Apache 封装的DBUtils是能够很方便的帮助我们实现相对比较简单的数据库操作

SpringJDBC

在Spring框架平台下,也提供的有JDBC的封装操作,在Spring中提供了一个模板方法 JdbcTemplate,里面封装了各种各样的 execute,query和update方法。

JdbcTemplate这个类是JDBC的核心包的中心类,简化了JDBC的操作,可以避免常见的异常,它封装了JDBC的核心流程,应用只要提供SQL语句,提取结果集就可以了,它是线程安全的。

初始配置

在SpringJdbcTemplate的使用中,我们依然要配置对应的数据源,然后将JdbcTemplate对象注入到IoC容器中。

@Configuration
@ComponentScan
public class SpringConfig {
   
   

    @Bean
    public DataSource dataSource(){
   
   
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        dataSource.setUrl("jdbc:mysql://localhost:3306/db?characterEncoding=utf-8&serverTimezone=UTC");
        return  dataSource;
    }

    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource){
   
   
        JdbcTemplate template = new JdbcTemplate();
        template.setDataSource(dataSource);
        return template;
    }
}

CRUD操作

在操作数据库中数据的时候,只需要从容器中获取JdbcTemplate实例即可

@Repository
public class UserDao {
   
   

    @Autowired
    private JdbcTemplate template;

    public void addUser(){
   
   
        int count = template.update("insert into t_user(user_name,real_name)values(?,?)","bobo","波波老师");
        System.out.println("count = " + count);
    }

    public void query2(){
   
   
        String sql = "select * from t_user";
        List<User> list = template.query(sql, new BeanPropertyRowMapper<>(User.class));
        for (User user : list) {
   
   
            System.out.println(user);
        }
    }

}

Hibernate

Apache DBUtils和SpringJdbcTemplate虽然简化了数据库的操作,但是本身提供的功能还是比较简单的(缺少缓存,事务管理等),所以在实际开发中往往并没有直接使用上述技术,而是用到了Hibernate和MyBatis等这些专业的ORM持久层框架。

ORM介绍

ORM( Object Relational Mapping) ,也就是对象与关系的映射,对象是程序里面的对象,关系是它与数据库里面的数据的关系,也就是说,ORM框架帮助我们解决的问题是程序对象和关系型数据库的相互映射的问题

Hibernate的使用

Hibernate是一个很流行的ORM框架,2001年的时候就出了第一个版本。使用步骤如下

创建项目

创建一个Maven项目并添加相关的依赖即可,我们在此处直接通过 SpringDataJpa的依赖处理

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.11</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

配置文件

在使用Hibernate的使用,我们需要为实体类创建一些hbm的xml映射文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
        '-//Hibernate/Hibernate Mapping DTD 3.0//EN'
        'http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd'>
<hibernate-mapping>
    <class name="com.model.User" table="t_user">
        <id name="id" />
        <property name="userName" column="user_name"></property>
        <property name="realName" column="real_name"></property>
    </class>
</hibernate-mapping>

以及Hibernate的配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">
            com.mysql.cj.jdbc.Driver
        </property>
        <property name="hibernate.connection.url">
            jdbc:mysql://localhost:3306/db?characterEncoding=utf8&serverTimezone=UTC
        </property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">123456</property>
        <property name="hibernate.dialect">
            org.hibernate.dialect.MySQLDialect
        </property>

        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.format_sql">true</property>
        <property name="hibernate.hbm2ddl.auto">update</property>

        <mapping resource="User.hbm.xml"/>
    </session-factory>
</hibernate-configuration>

CRUD 操作

然后在程序中我们可以通过Hibernate提供的 Session对象来实现CRUD操作

public class HibernateTest {
   
   

    /**
     * Hibernate操作案例演示
     * @param args
     */
    public static void main(String[] args) {
   
   
        Configuration configuration = new Configuration();
        // 默认使用hibernate.cfg.xml
        configuration.configure();
        // 创建Session工厂
        SessionFactory factory = configuration.buildSessionFactory();
        // 创建Session
        Session session = factory.openSession();
        // 获取事务对象
        Transaction transaction = session.getTransaction();
        // 开启事务
        transaction.begin();
        // 把对象添加到数据库中
        User user = new User();
        user.setId(666);
        user.setUserName("hibernate");
        user.setRealName("持久层框架");
        session.save(user);
        transaction.commit();
        session.close();
    }
}

其他方式

在映射文件的位置,我们也可以通过注解的方式来替换掉映射文件

@Data
@Entity
@Table(name = "t_user")
public class User {
   
   

    @Id
    @Column(name = "id")
    private Integer id;

    @Column(name = "user_name")
    private String userName;

    @Column(name = "real_name")
    private String realName;

    @Column(name = "password")
    private String password;

    @Column(name = "age")
    private Integer age;

    @Column(name = "i_id")
    private Integer dId;
}

在Spring中给我们提供的JPA对持久层框架做了统一的封装,而且本质上就是基于HibernateJPA来实现的,所以我们在使用的时候也可以通过SpringDataJPA的API来操作

dao的接口只需要继承JpaRepository接口即可

public interface IUserDao extends JpaRepository<User,Integer> {
}

service层正常处理

import java.util.List;
@Service
public class UserServiceImpl implements IUserService {
   
   

    @Autowired
    private IUserDao dao;

    @Override
    public List<User> query() {
   
   
        return dao.findAll();
    }

    @Override
    public User save(User user) {
   
   
        return dao.save(user);
    }
}

Hibernate总结

Hibernate的出现大大简化了我们的数据库操作,同时也能够更好的应对更加复杂的业务场景,Hibernate具有如下的特点

  1. 根据数据库方言自定生成SQL,移植性好
  2. 自动管理连接资源
  3. 实现了对象和关系型数据的完全映射,操作对象就想操作数据库记录一样
  4. 提供了缓存机制

Hibernate在处理复杂业务的时候同样也存在一些问题

  1. 比如API中的get(),update()和save()方法,操作的实际上是所有的字段,没有办法指定部分字段,换句话说就是不够灵活
  2. 自定生成SQL的方式,如果要基于SQL去做一些优化的话,也是非常困难的。
  3. 不支持动态SQL,比如分表中的表名,条件,参数变化等,无法根据条件自动生成SQL

因此我们需要一个更为灵活的框架

MyBatis

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

“半自动”的ORM框架能够很好的解决上面所讲的Hibernate的几个问题,半自动化”是相对于Hibernate的全自动化来说的。它的封装程度没有Hibernate那么高,不会自动生成全部的SQL语句,主要解决的是SQL和对象的映射问题。

MyBatis的前身是ibatis,2001年开始开发,是“internet”和“abatis ['æbətɪs](障碍物)”两个单词的组合。04年捐赠给Apache。2010年更名为MyBatis。

在MyBatis里面,SQL和代码是分离的,所以会写SQL基本上就会用MyBatis,没有额外的学习成本。

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
7月前
|
人工智能 缓存 JavaScript
通义灵码深度体验:AI编程助手如何提升全栈开发效率
通义灵码是一款强大的AI编程助手,支持从代码补全到智能体自主开发的全流程辅助。在React+Node.js项目中,其实现了100%字段匹配的Mongoose Schema生成;通过`@灵码`指令,30秒内完成天气查询CLI工具开发,包含依赖管理与文档编写。其上下文记忆能力可自动关联模块逻辑,如为商品模型扩展库存校验。集成MCP服务时,不仅生成基础代码,还推荐最佳实践并添加缓存优化。测试显示,其响应速度快、复杂任务准确率高,适合中小型项目快速迭代,初期开发效率提升约40%。尽管存在文档同步延迟和TypeScript支持不足的问题,仍是一款优秀的AI编程伙伴。
428 7
|
安全 应用服务中间件 网络安全
简单比较 http https http2,我们要如何把http升级为https
【9月更文挑战第13天】本文对比了HTTP、HTTPS和HTTP/2的特点与适用场景。HTTP以明文传输,适合低安全要求的环境;HTTPS通过SSL/TLS加密,适用于电子商务等安全要求高的场景;HTTP/2采用二进制格式和多路复用,适合高性能Web应用。文章还详细介绍了将HTTP升级为HTTPS的步骤,包括申请和安装SSL证书、配置Web服务器、重定向HTTP流量到HTTPS以及测试HTTPS功能。升级到HTTPS可提高数据安全性和用户信任度。
606 13
|
存储 人工智能 运维
阿里云首款单相浸没液冷解决方案正式对外发布!
阿里云首款单相浸没液冷解决方案正式对外发布!
阿里云首款单相浸没液冷解决方案正式对外发布!
|
前端开发 数据管理 数据处理
Django的MTV
【8月更文挑战第13天】
324 4
|
SQL 大数据 分布式数据库
SQL与大数据的神秘力量:如何用高效SQL处理海量数据,让你的项目一鸣惊人?
【8月更文挑战第31天】在现代软件开发中,处理海量数据是关键挑战之一。本文探讨了SQL与大数据结合的方法,包括数据类型优化、索引优化、分区优化及分布式数据库应用,并通过示例代码展示了如何实施这些策略。通过遵循最佳实践,如了解查询模式、使用性能工具及定期维护索引,开发者可以更高效地利用SQL处理大规模数据集。随着SQL技术的发展,其在软件开发中的作用将愈发重要。
529 0
|
前端开发 JavaScript 开发者
详细解读CSS——盒子模型(含详解)
详细解读CSS——盒子模型(含详解)
379 0
|
存储 Unix Linux
硬件 RAID 与软件 RAID 之间的区别
独立磁盘冗余阵列 (RAID) 是一种将多个物理驱动器组合到一个单元中的虚拟磁盘技术。这是一种通过使用多个驱动器来提高存储介质的性能和可靠性的方法。驱动器配置为在磁盘之间划分数据以分配负载,或者复制数据以确保在磁盘发生故障后可以恢复数据。RAID 的执行可以使用特殊控制器(硬件 RAID)或操作系统驱动程序(软件 RAID)来完成。
654 0
|
数据采集 存储 传感器
浅谈数据采集的难点|大规模数据采集首选的电商API接口
在当今的制造业领域,数据采集是一个难点。很多企业的生产数据采集主要依靠传统的手工作业方式,采集过程中容易出现人为的记录错误且效率低下。 有些企业虽然引进了相关技术手段,并且应用了数据采集系统,但是由于系统本身的原因以及企业没有选择最适合自己的数据采集系统,因此也无法实现信息采集的实时性、精确性和延伸性管理,各单元出现了信息断层的现象。
|
移动开发 运维 前端开发
SpringBoot 整合 Thymeleaf & 如何使用后台模板快速搭建项目
SpringBoot 整合 Thymeleaf & 如何使用后台模板快速搭建项目
416 0
|
图形学
【Unity实战系列】如何把你的二次元老婆/老公导入Unity进行二创并且进行二次元渲染?(附模型网站分享)
【Unity实战系列】如何把你的二次元老婆/老公导入Unity进行二创并且进行二次元渲染?(附模型网站分享)
1195 1

热门文章

最新文章