JDBC的使用及案例

news2024/11/24 11:37:48

1. JDBC基本操作

1.1. JDBC概述

  • JDBC(Java Data Base Connectivity)Java连接数据库
  • 是一种用于执行SQL语句的Java API,为多种关系数据库提供统一访问
  • 它由一组用Java语言编写的类和接口组成在这里插入图片描述
  • 有了JDBC,程序员只需用JDBC API写一个程序,就可以访问所有数据库
    在这里插入图片描述
  • SUN公司时规范制定者,制定了JDBC规范
    • DriverManager类:管理不同的JDBC驱动
    • Connection接口
    • Statement接口和PreparedStatement接口
    • ResultSet接口
  • 数据库厂商,如微软、甲骨文等提供JDBC接口的驱动jar包
  • 程序员学习JDBC规范应用jar包里的类
    在这里插入图片描述
  • JDBC访问数据库的步骤
    1. 加载一个Driver驱动
    2. 创建数据库连接Connection
    3. 创建SQL命令发送器Statement
    4. 通过Statement发送SQL命令并得到结果
    5. 处理结果(SELECT语句)
    6. 关闭数据库连接:ResultSet、Statement、Connection

1.2. 使用JDBC完成添加操作

public class TestInsert {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 0. 将相应数据库的jar包放入项目
        // 1. 加载驱动
        String driver = "com.mysql.cj.jdbc.Driver";
        String url = "jdbc:mysql://127.0.0.1:3306/table?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
        String user = "root";
        String password = "root";
        Class.forName(driver);
        // 2. 建立(和数据库连接)
        Connection conn = DriverManager.getConnection(url, user, password);
        // 3. 创建一个SQL命令发送器
        Statement stmt = conn.createStatement();
        // 4. 使用SQL命令发送器(手枪)发送SQL命令(子弹)并得到结果
        String sql = "insert into dept values(101, '教学部', '北京')";
        // insert update delete返回值表明添加了几条数据
        int n = stmt.executeUpdate(sql);
        // 5. 处理结果
        if(n > 0) {
            System.out.println("添加部门成功");
        }else {
            System.out.println("添加部门失败");
        }
        // 6. 关闭各种数据库资源
        stmt.close();
        conn.close();
    }
}

总结:

  • 错误: Exception in thread “main” java.lang.ClassNotFoundException: com.mysql.jdbc2.Driver,原因:没有添加jar或com.mysql.jdbc2.Driver路径错误
  • 理解:Class.forName(“com.mysql.jdbc.Driver”);的作用:第一次使用com.mysql.jdbc.Driver,就会执行静态代码块,注册驱动,注册之后 jar–META-INF–services–java.sql.Driver–com.mysql.jdbc.Driver,会自动找到该内容并加载
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }
    static {
        try {
            DriverManager.registerDriver(new Driver());
        }catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

2. JDBC基本操作

2.1. 使用JDBC完成更新删除操作

使用JDBC完成更新、删除操作的操作步骤和完成添加操作的步骤完全相同,唯一不同的就是SQL语句不通

String sql = "update dept set dname='咨询部' where deptno = 90";
String sql = "delete from dept where deptno > 70";

2.2. 使用JDBC完成查询操作

public class TestSelect {
    public static void main(String[] args) {
        // 将相应数据库的jar包放入项目
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        int n = 0;
        try {
            // 1. 加载驱动
            String driver = "com.mysql.cj.jdbc.Driver";
            String url = "jdbc:mysql://127.0.0.1:3306/table?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
            String user = "root";
            String password = "root";
            Class.forName(driver);
            // 2. 建立和数据库连接
            conn = DriverManager.getConnection(url, user, password);
            // 3. 创建一个SQL命令发送器
            stmt = conn.createStatement();
            // 4. 使用SQL命令发送器发送SQL命令(子弹)并得到结果
            String sql = "select * from dept where deptno > 20";
            rs = stmt.executeQuery(sql);
            // 5. 处理结果
            System.out.println("编号\t名称\t地址");
            while (rs.next()) {
                // 获取各列的数据
                int deptno = rs.getInt(1);
                String dname = rs.getString(2);
                String location = rs.getString(3);
                System.out.println(deptno + "\t" + dname + "\t" + location);
            }
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            // 6. 关闭各种数据库资源
            try {
                if(stmt != null) {
                    stmt.close();
                }
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
            try {
                if(conn != null) {
                    conn.close();
                }
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
  • ResultSet里的数据一行一行排列,每行有多个字段,且有一个记录指针,指针所指的数据行叫做当前数据行,我们只能来操作当前的数据行。想要取得某一条记录,就要使用ResultSet的next()方法,如果想要得到ResultSet中的所有记录,就要使用while循环
  • ResultSet对象自动维护指向当前数据行的游标。每调用一次next()方法,游标向下移动一行
  • 初始状态下记录指针指向第一条数据的前面,通过next()方法指向第一条记录。循环完毕后指向最后一条记录的后面在这里插入图片描述
  • 作为一种好的编程风格,应在不需要Statement对象和Connection对象时显式地关闭他们。
  • 用户不必关闭ResultSet。当他的Statement关闭、重新执行或用于从多结果序列中获取下一个结果时,该ResultSet将被自动关闭
// 封装后台查询数据并在前台显示
public class TestSelect2 {
    // 前台页面
    public static void main(String[] args) {
        // 点击查询按钮
        List<Dept> deptList = selectAll();
        // 显示查询结果
        System.out.println("编号\t名称\t地址");
        for (Dept dept : deptList) {
            System.out.println(dept.getDeptno() + "\t" + dept.getDname() + "\t" + dept.getLocation());
        }
    }
    // 后台服务器
    public static List<Dept> selectAll() {
        // 将相应数据库的jar包放入项目
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        List<Dept> deptList = new ArrayList<Dept>();
        try {
            // 1. 加载驱动
            String driver = "com.mysql.cj.jdbc.Driver";
            String url = "jdbc:mysql://127.0.0.1:3306/table?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
            String user = "root";
            String password = "root";
            Class.forName(driver);
            // 2. 建立数据库连接
            conn = DriverManager.getConnection(url, user, password);
            // 3. 创建一个SQL命令发送器
            stmt = conn.createStatement();
            // 4. 使用SQL命令发送器来发送SQL命令(子弹)并得到结果
            String sql = "select * from dept";
            rs = stmt.executeQuery(sql);
            // 5. 处理结果
            while (rs.next()) {
                // 获取当前行各个列的数据
                int deptno = rs.getInt("deptno");
                String dname = rs.getString("dname");
                // 可以使用列的编号,更建议使用列的名称,不区分大小写
                String loc = rs.getString("loc");
                // 将当前行各个列的数据封装到一个Dept对象中
                Dept dept = new Dept(deptno, dname, loc);
                // 将Dept对象加入到集合中
                deptList.add(dept);
            }
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {

        }
        return deptList;
    }
}

3. JDBC高级操作

3.1. PreparedStatement

模拟淘宝登陆功能。用户在前台输入用户名和密码,后台判断信息是否正确,并给出前台反馈信息,前台输出反馈信息。

  1. 创建数据库表t_user
CREATE TABLE t_user(
	userid VARCHAR(10) PRIMARY KEY, -- 字符串做主键无法实现自增
	realname VARCHAR(3) NOT NULL,
	password VARCHAR(6) NOT NULL,
	money DOUBLE(10, 2)
);
SELECT * FROM t_user;
INSERT INTO t_user VALUES('wyb', '王一博', 'wyb', 100000), ('xz', '肖战', 'xz', 200000);
  1. 创建实体类User
  2. 开发前后台代码
  3. 进行测试
public class TestLogin {
    // 前台
    public static void main(String[] args) {
        // 1. 从键盘输入用户名和密码
        Scanner input = new Scanner(System.in);
        System.out.println("请输入用户名");
        String userId = input.next();
        System.out.println("请输入密码");
        String password = input.next();
        // 2. 调用后台完成登陆
        User user = login(userId, password);
        // 3. 输出结果
        if(user != null) {
            System.out.println("登陆成功,当前用户名:" + user.getRealname());
        }else {
            System.out.println("登陆失败,请重新登陆");
        }
    }
    public static User login(String userId, String pwd) {
        // 将相应数据库的jar包放入项目
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        User user = null; // 默认登陆失败
        try {
            // 1. 加载驱动
            String driver = "com.mysql.cj.jdbc.Driver";
            String url = "jdbc:mysql://127.0.0.1:3306/table?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
            String username = "root";
            String password = "root";
            Class.forName(driver);
            // 2. 建立和数据库连接
            conn = DriverManager.getConnection(url, username, password);
            // 3. 创建一个SQL命令发送器
            stmt = conn.createStatement();
            // 4. 使用SQL命令发送器来发送SQL命令并得到结果
            String sql = "select * from t_user where userid = '" + userId + "' and password = '" + pwd + "'";
            System.out.println(sql);
            rs = stmt.executeQuery(sql);
            // 5. 处理结果
            if(rs.next()) { // 登陆成功,查询到了数据
                // 获取当前行各个列的数据
                String realName = rs.getString("realname");
                double money = rs.getDouble("money");
                // 将当前行各个列的数据封装到一个User对象中
                user = new User(userId, realName, null, money);
            }
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {

        }
        return user;
    }
}

问题:有SQL注入风险,原因就是SQL语句是字符串拼接的。使用PreparedStatement来解决
在这里插入图片描述

  • PreparedStatement和Statement的关系和区别
    • 关系:public interface PreparedStatement extends Statement
    • 区别
      • PreparedStatement安全性高,可以避免SQL注入
      • PreparedStatement简单不繁琐,不用进行字符串拼接
      • 用在执行多个相同数据库DML操作时,PreparedStatement性能高
	public static User login(String userId, String pwd) {
        // 将相应数据库的jar包放入项目
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        User user = null; // 默认登陆失败
        try {
            // 1. 加载驱动
            String driver = "com.mysql.cj.jdbc.Driver";
            String url = "jdbc:mysql://127.0.0.1:3306/table?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
            String username = "root";
            String password = "root";
            Class.forName(driver);
            // 2. 建立和数据库连接
            conn = DriverManager.getConnection(url, username, password);
            // 3. 使用SQL命令发送器来发送SQL命令并得到结果
            String sql = "select * from t_user where userid = ? and password = ?"; // ?:占位符
            // 4. 创建一个SQL命令发送器
            pstmt = conn.prepareStatement(sql);
            // 5. 使用SQL命令发送器来发送SQL命令并得到结果
            pstmt.setString(1, userId);
            pstmt.setString(2, pwd);
            rs = pstmt.executeQuery();
            // 6. 处理结果
            if(rs.next()) {
                String realName = rs.getString("realname");
                double money = rs.getDouble("money");
                user = new User(userId, realName, null, money);
            }

        } catch (SQLException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }finally {

        }
        return user;
    }

3.2. JDBC中使用事务

  • 在JDBC中,事务操作缺省是自动提交的
    • 一条对数据库的DML(insert、update、delete)代表一项事务操作
    • 操作成功后,系统将自动调用commit()提交,否则自动调用rollback()回滚
  • 在JDBC中,事务操作方法都位于接口java.sql.Connection中
    • 可以通过调用setAutoCommit(false)来禁止自动提交
    • 之后就可以把多个数据库操作的表达式作为一个事务,在操作完成之后调用commit()来进行整体提交
    • 倘若其中一个表达式操作失败,都不会执行到commit(),并且将产生响应的异常;此时就可以在异常捕获时调用rollback()进行回滚,恢复至初始数据状态
  • 事务开始的边界是组成当前事务的所有statement中的第一个被执行的时候
  • 事务结束的边界是commit或rollback方法被调用
public class TestTransaction {
    public static void main(String[] args) {
        Connection conn = null;
        Statement stmt = null;
        try {
            // 1. 加载驱动
            String driver = "com.mysql.cj.jdbc.Driver";
            String url = "jdbc:mysql://127.0.0.1:3306/table?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
            String user = "root";
            String password = "root";
            Class.forName(driver);
            conn = DriverManager.getConnection(url, user, password);
            stmt = conn.createStatement();
            // 事务不再自动结束,需要手动的提交或回滚
            conn.setAutoCommit(false);
            stmt.executeUpdate("update t_user set money = money - 2000 where userid = 'wyb'");
            stmt.executeUpdate("update t_user set money = money1 + 2000 where userid = 'xz'");
            // 手动提交事务,能执行该语句,表明前面多个DML操作都可以成功,只是数据只写入缓存,还没有真正写入数据库
            conn.commit();
        } catch (SQLException e) {
            // 手动的回滚事务,回到所有DML操作执行之前的状态
            try {
                conn.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {

        }
    }
}

3.3. JDBC API总结

  • Connection接口:代表数据库连接
    在这里插入图片描述
  • DriverManager类:管理一组JDBC驱动程序的基本服务,应用程序不再需要使用Class.forName()显式的加载JDBC驱动程序,在调用getConnection方法时,DriverManager会试着从初始化时加载的那些驱动程序以及使用与当前applet或应用程序相同的类加载器显示加载的那些驱动程序中查找合适的驱动程序
    在这里插入图片描述
  • Statement接口:用于将SQL语句发送到数据库中,或理解为执行sql语句
    • Statement:用于执行不带参数的简单SQL语句
    • PreparedStatement(从Statement继承):用于执行带或不带参数的预编译SQL语句
    • CallableStatement(从Statement继承):用于执行数据库存储过程的调用
      在这里插入图片描述
  • ResultSet接口:初始状态下记录指针指向第一条记录的前面,通过next()方法指向第一条记录。循环完毕后指向最后一条记录的后面。
    在这里插入图片描述
    在这里插入图片描述

4. 案例:员工管理系统

  • 项目功能:查询所有、按照编号查询、添加员工、更新员工、删除员工
  • 项目技能:
    1. 使用JDBC访问数据库
    2. 分层开发:前台:调用后台并输出结果,后台:使用JDBC访问数据库并返回结果
    3. 提供工具类DBUtil,复用代码
    4. 使用Properties类读取属性文件
    5. 使用log4j记录日志
    6. 扩展:提取查询方法(使用反射)

4.1. 搭建项目框架

  1. 创建项目empmgr
  2. 添加jar包
  3. 创建包
    • com.wyb.dao:后台:存放访问数据库的接口,EmployeeDao
    • com.wyb.dao.imp:后台:存放访问数据库的实现类,EmployeeDaoImpl
    • com.wyb.entity:后台:存放实体类Employee
    • com.wyb.test:前台
    • com.wyb.util:工具类,提取数据库常用操作便于重复调用,避免代码重复
  4. 创建实体类Employee
  5. 创建后台的接口EmployeeDao和实现类EmployeeDaoImpl
public Lisk<Employee> findAll();
public Employee findById(int empno);
public int save(Employee emp);
public int update(Employee emp);
public void delete(int empno);

在这里插入图片描述

4.2. Employee类和EmployeeDao接口

package com.wyb.dao;
import com.wyb.entity.Employee;
import java.util.List;

public interface EmployeeDao {
    /*
    * 查询所有员工
    * */
    public List<Employee> findAll();
    /*
    * 查询指定编号的员工
    * */
    public Employee findById(int empno);
    /*
    * 添加员工
    * */
    public int save(Employee emp);
    /*
    * 修改员工信息
    * */
    public int update(Employee emp);
    /*
    * 删除指定编号的员工
    * */
    public int delete(int empno);
}
package com.wyb.entity;
import java.util.Date;

public class Employee {
    private int empno;
    private String ename;
    private String job;
    private int mgr;
    private Date hireDate;
    private double sal;
    private double comm;
    private int deptno;
    public Employee(int empno, String ename, String job, int mgr, Date hireDate, double sal, double comm, int deptno) {
        this.empno = empno;
        this.ename = ename;
        this.job = job;
        this.mgr = mgr;
        this.hireDate = hireDate;
        this.sal = sal;
        this.comm = comm;
        this.deptno = deptno;
    }
    public Employee(String ename, String job, int mgr, Date hireDate, double sal, double comm, int deptno) {
        this.ename = ename;
        this.job = job;
        this.mgr = mgr;
        this.hireDate = hireDate;
        this.sal = sal;
        this.comm = comm;
        this.deptno = deptno;
    }
    public Employee(int empno, String job, double sal, int deptno) {
        this.empno = empno;
        this.job = job;
        this.sal = sal;
        this.deptno = deptno;
    }
    public int getEmpno() {
        return empno;
    }
    public void setEmpno(int empno) {
        this.empno = empno;
    }
    public String getEname() {
        return ename;
    }
    public void setEname(String ename) {
        this.ename = ename;
    }
    public String getJob() {
        return job;
    }
    public void setJob(String job) {
        this.job = job;
    }
    public int getMgr() {
        return mgr;
    }
    public void setMgr(int mgr) {
        this.mgr = mgr;
    }
    public Date getHireDate() {
        return hireDate;
    }
    public void setHireDate(Date hireDate) {
        this.hireDate = hireDate;
    }
    public double getSal() {
        return sal;
    }
    public void setSal(double sal) {
        this.sal = sal;
    }
    public double getComm() {
        return comm;
    }
    public void setComm(double comm) {
        this.comm = comm;
    }
    public int getDeptno() {
        return deptno;
    }
    public void setDeptno(int deptno) {
        this.deptno = deptno;
    }
    @Override
    public String toString() {
        return "Employee{" +
                "empno=" + empno +
                ", ename='" + ename + '\'' +
                ", job='" + job + '\'' +
                ", mgr=" + mgr +
                ", hireDate=" + hireDate +
                ", sal=" + sal +
                ", comm=" + comm +
                ", deptno=" + deptno +
                '}';
    }
}

4.3. 查询所有员工

后台代码

public class EmployeeDaoImpl implements EmployeeDao {
    @Override
    public List<Employee> findAll() {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        List<Employee> list = new ArrayList<Employee>();
        String driver = "com.mysql.cj.jdbc.Driver";
        String url = "jdbc:mysql://127.0.0.1:3306/table";
        String user = "root";
        String password = "root";
        try {
            Class.forName(driver);
            conn = DriverManager.getConnection(url, user, password);
            stmt = conn.createStatement();
            String sql = "select * from emp";
            rs = stmt.executeQuery(sql);
            while (rs.next()) {
                int empno = rs.getInt(1);
                String ename = rs.getString(2);
                String job = rs.getString(3);
                int mgr = rs.getInt(4);
                Date hiredate = rs.getDate("hiredate");
                double sal = rs.getDouble("sal");
                double comm = rs.getDouble("comm");
                int deptno = rs.getInt("deptno");
                Employee emp = new Employee(empno, ename, job,mgr, hiredate, sal, comm, deptno);
                list.add(emp);
            }

        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if(rs != null) {
                    rs.close();
                }
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
            try {
                if(stmt != null) {
                    stmt.close();
                }
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
            try {
                if(conn != null) {
                    conn.close();
                }
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        return list;
    }
}

前台代码

package com.wyb.test;
import com.wyb.dao.EmployeeDao;
import com.wyb.dao.impl.EmployeeDaoImpl;
import com.wyb.entity.Employee;
import java.util.List;
import java.util.Scanner;

public class Test {
    public static void main(String[] args) {
        findAll();
    }
    private static void findAll() {
        // 1. 调用后台获取所有员工信息
        EmployeeDao employeeDao = new EmployeeDaoImpl();
        List<Employee> list = employeeDao.findAll();
        System.out.println("编号\t\t姓名\t\t岗位\t\t\t上级\t\t入职时间\t\t薪水\t\t补助\t\t部门编号");
        for (Employee emp : list) {
            System.out.println(emp.getEmpno() + "\t" + emp.getEname() +  "\t" + emp.getJob() + "\t" + emp.getMgr() + "\t" + emp.getHireDate() + "\t" + emp.getSal() + "\t" + emp.getComm() + "\t" + emp.getDeptno());
        }
    }
}

4.4. 查询指定编号的员工

后台代码

	@Override
    public Employee findById(int empno) {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        Employee emp = null;
        List<Employee> list = new ArrayList<Employee>();
        String driver = "com.mysql.cj.jdbc.Driver";
        String url = "jdbc:mysql://127.0.0.1:3306/table";
        String user = "root";
        String password = "root";
        try {
            Class.forName(driver);
            conn = DriverManager.getConnection(url, user, password);
            stmt = conn.createStatement();
            String sql = "select * from emp where empno = " + empno;
            rs = stmt.executeQuery(sql);
            while (rs.next()) {
                String ename = rs.getString(2);
                String job = rs.getString(3);
                int mgr = rs.getInt(4);
                Date hiredate = rs.getDate("hiredate");
                double sal = rs.getDouble("sal");
                double comm = rs.getDouble("comm");
                int deptno = rs.getInt("deptno");
                emp = new Employee(ename, job,mgr, hiredate, sal, comm, deptno);
                list.add(emp);
            }

        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if(rs != null) {
                    rs.close();
                }
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
            try {
                if(stmt != null) {
                    stmt.close();
                }
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
            try {
                if(conn != null) {
                    conn.close();
                }
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        return emp;
    }

前台代码

	private static void findById() {
        Scanner input = new Scanner(System.in);
        System.out.println("请输入员工编号:");
        int empno = input.nextInt();
        // 调用后台获取指定编号的员工信息
        EmployeeDaoImpl employeeDao = new EmployeeDaoImpl();
        Employee emp = employeeDao.findById(empno);
        if(emp == null) {
            System.out.println("员工不存在");
        }else {
            System.out.println(emp);
        }
    }

4.5. 提取DBUtil工具类

在查询所有员工信息、查询指定编号员工信息的DAO层代码中,建立数据库连接、关闭数据库资源的代码重复了,可以提前到一个工具类的方法中供调用,避免代码重复,也利用修改维护。

package com.wyb.util;
import java.sql.*;

public class DBUtil {
    /*
    * 创建并返回数据库连接
    * */
    public static Connection getConnection() {
        Connection conn = null;
        String driver = "com.mysql.cj.jdbc.Driver";
        String url = "jdbc:mysql://127.0.0.1:3306/table";
        String user = "root";
        String password = "root";
        try {
            Class.forName(driver);
            conn = DriverManager.getConnection(url, user, password);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        return conn;
    }
    /*
    * 关闭资源
    * */
    public static void closeAll(ResultSet rs, Statement stmt, Connection conn) {
        try {
            if(rs != null) {
                rs.close();
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        try {
            if(stmt != null) {
                stmt.close();
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        try {
            if(conn != null) {
                conn.close();
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

4.6. 添加员工

	@Override
    public int save(Employee emp) {
        Connection conn = null;
        PreparedStatement pstmt = null;
        int n = 0;
        try {
            conn = DBUtil.getConnection();
            String sql = "insert into emp values(null, ?, ?, ?, ?, ?, ?, ?)";
            pstmt = conn.prepareStatement(sql);
            // 使用Statement发送SQL命令并得到结果
            pstmt.setString(1, emp.getEname());
            pstmt.setString(2, emp.getJob());
            pstmt.setInt(3, emp.getMgr());
            pstmt.setDate(4, new java.sql.Date(emp.getHireDate().getTime()));
            pstmt.setDouble(5, emp.getSal());
            pstmt.setDouble(6, emp.getComm());
            pstmt.setInt(7, emp.getDeptno());
            n = pstmt.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            // 6. 关闭资源
            DBUtil.closeAll(null, pstmt, conn);
        }
        // 返回数据
        return n;
    }
	private static void save() {
        // 从键盘输入要添加的员工信息
        Scanner input = new Scanner(System.in);
        System.out.println("请输入员工姓名");
        String ename = input.next();
        System.out.println("请输入岗位");
        String job = input.next();
        System.out.println("请输入员工上级编号");
        int mgr = input.nextInt();
        System.out.println("请输入员工入职时间(yyyy-MM-dd)");
        String sdate = input.next();
        DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Date hireDate = null;
        try {
            hireDate = sdf.parse(sdate);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        System.out.println("请输入员工薪水");
        double sal = input.nextDouble();
        System.out.println("请输入员工津贴");
        double comm = input.nextDouble();
        System.out.println("请输入员工部门编号");
        int deptno = input.nextInt();
        // 调用后台完成添加操作并返回结果
        Employee emp = new Employee(ename, job, mgr, hireDate, sal, comm, deptno);
        EmployeeDaoImpl employeeDao = new EmployeeDaoImpl();
        int n = employeeDao.save(emp);
        if(n > 0) {
            System.out.println("添加成功");
        }else {
            System.out.println("添加失败");
        }
    }

问题:java.util.Date和java.sql.Date关系
联系:public class Date extend java.util.Date
区别

  • java.util.Date yyyy-MM-dd hh:mm:ss java.sql.Date yyyy-MM-dd
  • java.util.Date有无参数构造方法,new Date()获取当前的时间; java.sql.Date没有无参数构造方法,但有Date.valueOf(“1997-08-05”);
  • java.util.Date str --> Date DateFormat; java.sql.Date str --> Date Date.valueOf(“1997-08-05”)

问题: util.Date转换为 sql.Date:new java.sql.Date(emp.getHireDate().getTime())
问题:JDBC缺点
需要手动进行关系和对象的转换
添加操作是需要将对象数据转换为关系表的数据;查询操作是需要将关系数据转换为对象的数据
转换必须要做,JDBC是程序员做的,增加了时间;可以交给框架来做,比如MyBats、Hibernate

4.7. 完善DBUtil类

修改员工、删除员工功能的代码和添加员工非常相似,只是SQL语句和传递的具体参数的不同。
可以为insert、update、delete这三个DML操作提取一个方法呢

	/*
    * 针对DML操作:insert、update、delete
    * */
    public static int executeUpdate(String sql, Object[] params) {
        Connection conn = null;
        PreparedStatement pstmt = null;
        int n = 0;
        try {
            conn = getConnection();
            pstmt = conn.prepareStatement(sql);
            for (int i = 0; i < params.length; i++) {
                pstmt.setObject(i + 1, params[i]);
            }
            n = pstmt.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            closeAll(null, pstmt, conn);
        }
        return n;
    }

EmployeeDaoImpl的三个DML方法调用DButil的executeUpdate()方法后,代码大大的简化了

	@Override
    public int save(Employee emp) {
        String sql = "insert into emp values(null, ?, ?, ?, ?, ?, ?, ?)";
        Object[] params = {emp.getEname(), emp.getJob(), emp.getMgr(), emp.getHireDate(), emp.getSal(), emp.getComm(), emp.getDeptno()}
        return DBUtil.executeUpdate(sql, params);
    }
    @Override
    public int update(Employee emp) {
        String sql = "update emp set job = ?, sal = ? where empno = ?";
        Object[] params = {emp.getJob(), emp.getSal(), emp.getEmpno()};
        return DBUtil.executeUpdate(sql, params);
    }
    @Override
    public int delete(int empno) {
        return DBUtil.executeUpdate("delete from emp where empno = ?", new Object[]{empno});
    }

4.8. 添加主菜单

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Scanner;

public class Test2 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        do {
            System.out.println("");
            System.out.println("欢迎使用员工管理系统");
            System.out.println("\t1.查询所有员工");
            System.out.println("\t2.查询指定编号员工");
            System.out.println("\t3.添加员工信息");
            System.out.println("\t4.修改员工信息");
            System.out.println("\t5.删除员工信息");
            System.out.println("\t6.退出");
            System.out.println("请选择菜单");
            int choice = input.nextInt();
            switch (choice) {
                case 1: findAll();break;
                case 2: findById();break;
                case 3: save();break;
                case 4: update();break;
                case 5: delete();break;
                case 6:
                    System.out.println("谢谢使用");
                    return;
                default:
                    System.out.println("输入错误");
            }
            System.out.println("按任意键继续");
            input.nextLine();
            input.nextLine();
        }while (true);
    }
    public static void delete() {
        Scanner input = new Scanner(System.in);
        System.out.println("请输入员工编号");
        int empno = input.nextInt();
        // 调用后台完成删除操作并返回结果
        EmployeeDao employeeDao = new EmployeeDaoImpl();
        int n = employeeDao.delete(empno);
        if(n > 0) {
            System.out.println("删除成功");
        }else {
            System.out.println("删除失败");
        }
    }
    public static void update() {
        Scanner input = new Scanner(System.in);
        System.out.println("请输入员工岗位");
        String job = input.next();
        System.out.println("请输入员工薪水");
        double  sal =  input.nextDouble();
        System.out.println("请输入员工部门编号");
        int  deptno =  input.nextInt();
        System.out.println("请输入员工编号");
        int  empno =  input.nextInt();
        Employee emp = new Employee(empno, job, sal, deptno);
        EmployeeDaoImpl employeeDao = new EmployeeDaoImpl();
        int n = employeeDao.update(emp);
        if(n > 0) {
            System.out.println("修改成功");
        }else {
            System.out.println("修改失败");
        }
    }
    public static void save(){
        //从键盘输入要添加的员工信息
        Scanner input = new Scanner(System.in);
        System.out.println("请输入员工姓名");
        String  ename =  input.next();
        System.out.println("请输入员工岗位");
        String  job =  input.next();
        System.out.println("请输入员工上级编号");
        int  mgr =  input.nextInt();
        System.out.println("请输入员工入职时间(yyyy-MM-dd)");
        String sdate = input.next(); //"1999-12-23"

        DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Date hireDate = null;
        try {
            hireDate = sdf.parse(sdate);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        System.out.println("请输入员工薪水");
        double  sal =  input.nextDouble();
        System.out.println("请输入员工津贴");
        double  comm =  input.nextDouble();
        System.out.println("请输入员工部门编号");
        int  deptno =  input.nextInt();
        //调用后台完成添加操作并返回结果
        Employee emp = new Employee(ename, job, mgr, hireDate,
                sal, comm, deptno);
        EmployeeDao employeeDao = new EmployeeDaoImpl();
        int n = employeeDao.save(emp);
        //输出结果
        if(n>0){
            System.out.println("添加成功");
        }else{
            System.out.println("添加失败");
        }
    }


    public static void findById(){
        //从键盘输入员工编号
        Scanner input = new Scanner(System.in);
        System.out.println("请输入员工编号");
        int empno =input.nextInt();
        //调用后台获取指定编号员工
        EmployeeDao employeeDao = new EmployeeDaoImpl();
        Employee emp = employeeDao.findById(empno);

        //在前台输出员工列表
        if(emp == null){
            System.out.println("查无此人");
        }else{
            System.out.println("编号\t姓名\t岗位\t上级编号\t入职时间\t薪水\t补助\t所属部门编号");
            System.out.println(emp.getEmpno()+"\t"+emp.getEname()+"\t"+emp.getJob()+
                    "\t"+emp.getMgr()+"\t"+emp.getHireDate()+
                    "\t"+emp.getSal()+"\t"+emp.getComm()+"\t"+emp.getDeptno());
        }

    }

    /**
     * 查询所有员工的前台
     */
    public static void findAll(){
        //调用后台获取员工列表
        EmployeeDao employeeDao = new EmployeeDaoImpl();
        List<Employee> empList = employeeDao.findAll();

        //在前台输出员工列表
        System.out.println("编号\t姓名\t岗位\t上级编号\t入职时间\t薪水\t补助\t所属部门编号");
        for(Employee emp:empList){
            System.out.println(emp.getEmpno()+"\t"+emp.getEname()+"\t"+emp.getJob()+
                    "\t"+emp.getMgr()+"\t"+emp.getHireDate()+
                    "\t"+emp.getSal()+"\t"+emp.getComm()+"\t"+emp.getDeptno());
        }
    }
}

4.9. 使用属性文件存储数据库连接参数

属性文件是一个文本文件,可以直接打开修改,不涉及编译问题

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/table
user=root
pwd=root

读取属性文件:用类Properties来完成。并且不仅可以读属性文件,也可以写属性文件

认识Properties类

  • Properties也是一个集合类,并且使用Map类型的集合类,存储key-valuepublic class Properties extend Hashtable<Object, Object>
  • 特点:key、value都是String类型
  • 作用:读+写属性文件,更多的是读
  • 存储键值对:prop.setProperty("cn", "china");
  • 根据key找到对应的value:String pwd = prop.getPropperty("pwd")
  • 读属性文件
InputStream is = Test.class.getResourceAsStream("/jdbc.properties");
prop.load(is);
  • 写属性文件:prop.store(out, comments);
	/*
    * 读取属性文件
    * */
    private DBUtil() {
    }
    private static String driver;
    private static String url;
    private static String user;
    private static String password;
    static {
        // 读取属性文件,根据key获取四个连接参数value
        // 1. 创建Properties对象
        Properties prop = new Properties();
        // 2. 使用Properties对象读取属性文件并存储键值对
        InputStream is = DBUtil.class.getResourceAsStream("/jdbc.propertoes");
        try {
            prop.load(is);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        // 3. 根据key找到value
        driver = prop.getProperty("driver");
        url = prop.getProperty("url");
        user = prop.getProperty("user");
        password = prop.getProperty("pwd");
    }

4.10. 使用log4j记录日志

  1. log4j日志的级别
    • FATAL:出现非常严重的错误事件,可能导致应用程序异常终止
    • ERROR:虽有错误,但仍允许应用程序继续进行
    • WARN:运行环境潜藏着危害
    • INFO:报告信息,这些信息在粗粒度级别上突出显示应用程序的进程
    • DEBUG:细粒度信息事件,用于应用程序调试
  2. 使用log4j记录日志
    • 加入jar包:log4j-1.2.8.jar
    • 加入属性文件:src下log4j.properties
log4j.rootLogger=error,logfile

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout

log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=/Users/mingqi/Desktop/audio/java/practive/jdbc_empmgr/log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %F %p %m%n
  • Appender 日志目的地 : ConsoleAppender FileAppender
  • Layout 日志格式化器 :SimpleLayout PatternLayout
  1. 代码中记录日志
//创建一个日志记录器
private static final Logger logger = Logger.getLogger(DBUtil.class.getName());
//在合适的地方添加日志
logger.info("正确的读取了属性文件:"+prop);
logger.debug("正确的关闭了结果集");
logger.error("DML操作错误:"+e);

5. 连接池

5.1. 连接池原理

建立数据库连接的方式有两种

  • 传统连接方式:
    • 首先调用Class.forName()方法加载数据库驱动
    • 然后调用DriverManager.getConnection()方法建立连接
  • 连接池技术:
    • 连接池是应用程序启动时就预先建立多个数据库连接对象,然后将连接对象保存到连接池中
    • 当客户请求时,从池中取出一个连接对象为客户服务
    • 当完成请求时,客户程序调用close()方法,将连接对象放回池中
    • 多于连接池中连接数的请求,排队等待
    • 应用程序还可根据连接池中连接的使用率,动态增加或减少池中的连接数
      在这里插入图片描述
      在这里插入图片描述
  • 传统数据库连接方式的缺点
    • 一个连接对象对应一个物理连接
    • 每次操作都打开一个物理连接
    • 使用完都关闭连接,造成系统性能低下
  • 连接池技术的优点
    • 客户程序得到的连接对象是连接池中物理连接的一个句柄
    • 调用连接对象的close()方法,物理连接并没有关闭,数据源的实现知识删除了应用程序中的连接对象和池中的连接对象之间的联系
  • 数据库连接的建立及关闭是耗费系统资源的操作,为了能重复利用数据库连接对象,缩短请求的响应时间、提高服务器的性能,应采用连接池技术

5.2. 连接池的实现

package src.com.db1;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;

public class ConnectionPool {
    private static LinkedList<Connection> list = new LinkedList<Connection>();
    /*
    * 只执行一次,第一次加载类时执行,默认10个连接
    * */
    static {
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        for (int i = 0; i < 10; i++) {
            Connection conn = newConnection();
            list.addLast(conn);
        }
    }
    public static Connection newConnection() {
        Connection conn = null;
        try {
            conn = DriverManager.getConnection("jdbc:mysql//localhost:3306/table", "root", "root");
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        return conn;
    }
    public static Connection getConnection() {
        // 如果连接池中有连接,就返回
        if(list.size() > 0) {
            return list.removeFirst();
        }else {
            // 如果没有连接,就创建一个新的物理连接
            return newConnection();
        }
    }
    public static void returnConnection(Connection conn) {
        // 数量小于10,不关闭连接,而是放入连接池
        if(list.size() < 10) {
            list.addLast(conn);
        }else {
            // 关闭物理连接
            try {
                conn.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 10; i++) {
            // 获取连接池中连接
            ConnectionPool.getConnection();
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }
}

可以使用List来存储多个连接,因为连接池的主要操作是连接的获取和归还,就是添加和删除两个操作,使用LinkList的效率更高。

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

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

相关文章

将vue项目打包为安卓软件

前言 在我的前一个文章&#xff0c;有讲如何实现一个笔记系统 点击跳转到:纯vue实现笔记系统 那么我如果想要分享给我的朋友该怎么办呢? 那么我将带大家去实现打包安卓软件 安卓实际打包软件 也为了更信服&#xff0c;这里提供一个我的打包之后的软件给大家&#xff0c;感兴…

Python自动化办公2.0 课程更新

之前的课程&#xff0c;包含了Python pandassklearn 数据分析&#xff0c;和Stremlit 可视化仪表盘的开发 和一系列自动化项目案例的开发&#xff0c;包括我们封装了ztl-uia 模块&#xff0c;可以同时自动化操控windows 软件和浏览器, 封装的模块&#xff0c;针对为付费学员使…

证书学习(三).p12证书颁发的5个步骤、如何在线生成证书、证书工具网站推荐

目录 一、证书颁发的 5 个步骤二、在线生成证书2.1 在线生成 CSR 文件2.2 在线 CSR 签发证书三、其他在线工具3.1 在线解析证书3.2 在线证书格式转换(证书转 PKCS#12/DER/JSK 格式)3.3 在线解析 .p12 文件、下载 .cer 文件3.4 直接通过参数设置申请证书【最便捷】四、补充:其…

【职业选择】AI工程师、机器学习工程师和深度学习工程师的职责与工作内容有什么区别?

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发…

LVGL 控件之进度条(lv_bar)

目录 一、进度条1、概述2、方向3、进度条的当前值和范围值4、进度条模式5、进度条事件6、相关 API 二、例程 一、进度条 1、概述 进度条对象&#xff08;lv_bar&#xff09;有一个背景和一个指示器。指示器的宽度根据进度条的当前值自动设置。 如果设置进度条的宽度小于其高…

[C++] C++11详解 (五)function包装器、bind绑定

标题&#xff1a;[C] C11详解 (五)function包装器、bind 水墨不写bug 目录 一、function包装器 二、bind绑定 正文开始&#xff1a; 一、function包装器 function包装器&#xff0c;function实现在<functional>头文件中。C中的function本质上是一个类模板。 function…

由浅入深学习 C 语言:Hello World【提高篇】

目录 引言 1. Hello World 程序代码 2. C 语言角度分析 Hello World 程序 2.1. 程序功能分析 2.2 指针 2.3 常量指针 2.4 指针常量 3. 反汇编角度分析 Hello World 程序 3.1 栈 3.2 函数用栈传递参数 3.3 函数调用栈 3.4 函数栈帧 3.5 相关寄存器 3.6 相关汇编指令…

离散傅里叶变换(Discrete Fourier Transform, DFT)介绍,地震波分析

介绍 离散傅里叶变换&#xff08;Discrete Fourier Transform, DFT&#xff09;是一种非常重要的信号处理工具&#xff0c;它将离散时间信号从时间域转换到频率域。DFT在信号处理、图像处理、通信系统以及许多其他工程和科学领域中得到了广泛应用。为了理解DFT&#xff0c;我们…

时序预测 | 基于DLinear+PatchTST多变量时间序列预测模型(pytorch)

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 DLinearPatchTST多变量时间序列 dlinear,patchtst python代码&#xff0c;pytorch架构 适合功率预测&#xff0c;风电光伏预测&#xff0c;负荷预测&#xff0c;流量预测&#xff0c;浓度预测&#xff0c;机械领域预…

3.美食推荐系统(Java项目springboot和vue)

目录 0.系统的受众说明 1 绪论 1.1研究背景 1.2研究现状 1.3研究内容 2 系统关键技术 2.1 Springboot框架 2.2 JAVA技术 2.3 MYSQL数据库 2.4 B/S结构 3 系统分析 3.1 可行性分析 3.1.1 技术可行性 3.1.2经济可行性 3.1.3操作可行性 3.2 系统性能分析 3.3 系统功能分析 3.4系统…

【3D目标检测】MMdetection3d——nuScenes数据集训练BEVFusion

引言 MMdetection3d&#xff1a;【3D目标检测】环境搭建&#xff08;OpenPCDet、MMdetection3d&#xff09; MMdetection3d源码地址&#xff1a;https://github.com/open-mmlab/mmdetection3d/tree/main?tabreadme-ov-file IS-Fusion源码地址&#xff1a;https://github.co…

139. MySQL同步ES的四种方案

文章目录 1. 前言2. 数据同步方案2.1 同步双写2.2 异步双写2.3 基于 SQL 抽取2.4 基于 Binlog 实时同步 3. 数据迁移工具选型3.1 Canel3.2 阿里云 DTS3.3 Databus3.4 其它 4. 后记 本文介绍数据同步的 4 种方案&#xff0c;并给出常用数据迁移工具&#xff0c;目录如下&#xf…

【软件测试专栏】认识软件测试、测试与开发的区别

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;软件测试专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 认识软件测试、测试与开发的区别 关键词&#xff1a;软件测试、测…

最短路算法详解(Dijkstra 算法,Bellman-Ford 算法,Floyd-Warshall 算法)

文章目录 一、Dijkstra 算法二、Bellman-Ford 算法三、Floyd-Warshall 算法 由于文章篇幅有限&#xff0c;下面都只给出算法对应的部分代码&#xff0c;需要全部代码调试参考的请点击&#xff1a; 图的源码 最短路径问题&#xff1a;从在带权图的某一顶点出发&#xff0c;找出…

【PyCharm激活码】2024年最新pycharm专业版激活码+安装教程!

一、PyCharm激活 激活码&#xff1a; KQ8KMJ77TY-eyJsaWNlbnNlSWQiOiJLUThLTUo3N1RZIiwibGljZW5zZWVOYW1lIjoiVW5pdmVyc2l0YXMgTmVnZXJpIE1hbGFuZyIsImxpY2Vuc2VlVHlwZSI6IkNMQVNTUk9PTSIsImFzc2lnbmVlTmFtZSI6IkpldOWFqOWutuahtiDorqTlh4blupflkI0iLCJhc3NpZ25lZUVtYWlsIjoi…

ArcEngine二次开发实用函数18:使用shp矢量对栅格文件进行掩模和GP授权获取

目录 1. 权限设置 2. 添加如下引用 3. 核心代码: 首先要确定要使用的gp工具需要什么权限,这个可以在工具的帮助中查看;获取权限之后,引用名称空间,编写处理代码: 下面给出具体的实例代码: 1. 权限设置 ESRI.ArcGIS.RuntimeManager.Bind(ESRI.ArcGIS.ProductCode.Eng…

介绍一下最近很火的一款游戏黑神话悟空,以及国产游戏面临的挑战

《黑神话&#xff1a;悟空》是一款由杭州游科互动科技有限公司开发的单机动作角色扮演游戏&#xff0c;以中国古典名著《西游记》为背景。游戏在2024年8月20日上线&#xff0c;支持PC&#xff08;Steam、Epic、Wegame&#xff09;和PlayStation 5平台&#xff0c;未来还将登陆X…

OpenCV绘图函数(13)绘制多边形函数函数polylines()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 画几条多边形曲线 函数原型 void cv::polylines (InputOutputArray img,InputArrayOfArrays pts,bool isClosed,const Scalar & color…

浅谈 Android 15 新 API:确保 TextView 完整展示、不被切断~

本文为稀土掘金技术社区首发签约文章&#xff0c;30天内禁止转载&#xff0c;30天后未获授权禁止转载&#xff0c;侵权必究&#xff01; 前言 很多语言和文字拥有特殊的、复杂的写法、画法&#xff0c;一个字符可能延伸到前一个字符的区域&#xff0c;甚至后一个字符的区域。 …

力扣375.猜数字大小 II

力扣375.猜数字大小 II dp dp[i][j]是说依次以从i到j的数字作为分割点(猜的数)&#xff0c;必定赢的游戏所用钱的最小值。 枚举每一列&#xff0c;从下往上算出dp[i][j]&#xff0c;最终答案为dp[1][n] class Solution {public:int getMoneyAmount(int n) {if(n 1)retu…