JDBC连接数据库

JDBC

1、简介

JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多 种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,据此可 以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序

Java 具有坚固、安全、易于使用、易于理解和可从网络上自动下载等特性,是编写数据库应用程序的杰 出语言。所需要的只是 Java应用程序与各种不同数据库之间进行对话的方法。

JDBC可以在各种平台上使用Java,如Windows,Mac OS和各种版本的UNIX。

JDBC库包括通常与数据库使用相关的下面提到的每个任务的API。

  • 连接数据库。

  • 创建SQL或MySQL语句。

  • 在数据库中执行SQL或MySQL查询。

  • 看和修改生成的记录。

2、 JDBC体系结构

请添加图片描述

3、 JDBC核心组件

  • DriverManager: 此类管理数据库驱动程序列表。使用通信子协议将来自java应用程序的连接请求 与适当的数据库驱动程序匹配。
  • Driver:此接口处理与数据库服务器的通信,我们很少会直接与Driver对象进行交互。而是使用 DriverManager对象来管理这种类型的对象。
  • Connection:该界面具有用于联系数据库的所有方法。连接对象表示通信上下文,即,与数据库 的所有通信仅通过连接对象。
  • Statement:使用从此接口创建的对象将SQL语句提交到数据库。除了执行存储过程之外,一些派 生接口还接受参数。
  • ResultSet:在使用Statement对象执行SQL查询后,这些对象保存从数据库检索的数据。它作为一 个迭代器,允许我们移动其数据。
  • SQLException:此类处理数据库应用程序中发生的任何错误

4、 CRUD语法介绍

Create, Read, Update, and Delete 通常称为CRUD操作。 与数据库DML、DQL一致

5、 使用步骤

构建JDBC应用程序涉及以下六个步骤:

  1. 导入包:需要包含包含数据库编程所需的JDBC类的包。大多数情况下,使用import java.sql.*就足够 了。
  2. 注册JDBC驱动程序:要求您初始化驱动程序,以便您可以打开与数据库的通信通道。
  3. 打开连接:需要使用 DriverManager.getConnection() 方法创建一个Connection对象,该对象表 示与数据库的物理连接。
  4. 执行查询:需要使用类型为Statement的对象来构建和提交SQL语句到数据库。
  5. 从结果集中提取数据:需要使用相应的 ResultSet.getXXX() 方法从结果集中检索数据。
  6. 释放资源:需要明确地关闭所有数据库资源,而不依赖于JVM的垃圾收集。

5.1、 JDBC连接步骤

建立JDBC连接所涉及的编程相当简单。这是简单的四个步骤

  1. 导入JDBC包:将Java语言的import语句添加到Java代码中导入所需的类。 -

  2. 注册JDBC驱动程序:此步骤将使JVM将所需的驱动程序实现加载到内存中,以便它可以满足您的JDBC 请求。

    ——注册驱动程序最常见的方法是使用Java的 Class.forName() 方法,将驱动程序的类文件动态加载到内存 中,并将其自动注册

    try {
        //MySQL8 com.mysql.cj.jdbc.Driver
        //MySQL com.mysql.jdbc.Driver
        //ORACLE oracle.jdbc.driver.OracleDriver
        //DB2 COM.ibm.db2.jdbc.net.DB2Driver
    	Class.forName("com.mysql.cj.jdbc.Driver");
    }catch(ClassNotFoundException ex) {
        System.out.println("Error: unable to load driver class!");
        System.exit(1);
    }
    

    ——第二种方法是使用静态DriverManager.registerDriver()方法

    try {
        Driver myDriver = new com.mysql.cj.jdbc.Driver();
        DriverManager.registerDriver( myDriver );
    }catch(ClassNotFoundException ex) {
        System.out.println("Error: unable to load driver class!");
        System.exit(1);
    }
    
  3. 数据库URL配置:这是为了创建一个格式正确的地址,指向要连接到的数据库。

    加载驱动程序后,可以使用 DriverManager.getConnection() 方法建立连接。为了方便参考,列出三个重载的 DriverManager.getConnection() 方法

    • ​ getConnection(String url)

    • ​ getConnection(String url,Properties prop)

    • ​ getConnection(String url,String user,String password)

  4. 创建连接对象:最后,调用DriverManager对象的getConnection()方法来建立实际的数据库连接。

    String URL = "jdbc:mysql://localhost:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=utf-8";
    String USER = "username";
    String PASS = "password"
    Connection conn = DriverManager.getConnection(URL, USER, PASS);
    

    关闭数据库连接

    为确保连接关闭,您可以在代码中提供一个“finally”块。一个finally块总是执行,不管是否发生异常。 要关闭上面打开的连接,应该调用close()方法;

5.2 Statement

创建语句对象 在使用Statement对象执行SQL语句之前,需要使用Connection对象的createStatement()方法创建 一个,如下例所示:

Statement stmt = null;
try {
	stmt = conn.createStatement( );
	. . .
}catch (SQLException e) {
	. . .
	}
finally {
	. . .
}

创建Statement对象后,您可以使用它来执行一个SQL语句,其中有三个执行方法之一。

boolean execute(String SQL):如果可以检索到ResultSet对象,则返回一个布尔值true; 否则返 回false。使用此方法执行SQL DDL语句或需要使用真正的动态SQL时。

int executeUpdate(String SQL):返回受SQL语句执行影响的行数。使用此方法执行预期会影响 多个行的SQL语句。使用增删改语句时使用

ResultSet executeQuery(String SQL):返回一个ResultSet对象。使用select语句时使用

关闭Statement对象

直接调用close()方法。

5.3 SQL注入

就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执 行恶意的SQL命令。具体来说,它是利用现有应用程序,将(恶意的)SQL命令注入到后台数据库引擎 执行的能力,它可以通过在Web表单中输入(恶意)SQL语句得到一个存在安全漏洞的网站上的数据 库,而不是按照设计者意图去执行SQL语句。比如先前的很多影视网站泄露VIP会员密码大多就是通过 WEB表单递交查询字符暴出的,这类表单特别容易受到SQL注入式攻击。

String username ="admin";
//这样的密码会
String password=" 'abc' or 1=1 ";
String sql="select * from users where username= '"+username+"' and password="+password;

5.4 PreparedStatement(预状态通道)

该PreparedStatement的接口扩展了Statement接口,它为您提供了一个通用的Statement对象有两 个优点附加功能。 此语句使您可以动态地提供参数。

PreparedStatement pstmt = null;
try {
    String SQL = "Update Employees SET age = ? WHERE id = ?";
    pstmt = conn.prepareStatement(SQL);
    pstmt.setInt()
	. . .
}catch (SQLException e) {
	. . .
}
finally {
	. . .
}

JDBC中的所有参数都由?符号,这被称为参数标记。

在执行SQL语句之前,必须为每个参数提供值。 所述的 setXXX() 方法将值绑定到所述参数,其中XXX代表要绑定到输入参数的值的Java数据类型。如果 忘记提供值,将收到一个SQLException。

每个参数标记由其顺序位置引用。第一个标记表示位置1,下一个位置2等等。该方法与Java数组索引不 同,从0开始。

关闭PreparedStatement对象

直接调用close()方法。

对比statement和PreparedStatement区别

(1)statement属于状态通道,PreparedStatement属于预状态通道

(2)预状态通道会先编译sql语句,再去执行,比statement执行效率高

(3)预状态通道支持占位符?,给占位符赋值的时候,位置从1开始

(4)预状态通道可以防止sql注入,原因:预状态通道在处理值的时候以字符串的方式处理

5.5 ResultSet

SELECT语句是从数据库中选择行并在结果集中查看行的标准方法。该java.sql.ResultSet中的接口表示 结果集数据库查询。 如果没有指定任何ResultSet类型,您将自动获得一个TYPE_FORWARD_ONLY。

6、Java操作两表关系

四种:双向一对一、一对多、多对一、多对多

多表关系处理数据

  1. 数据库通过外键建立两表关系

  2. 实体类通过属性的方式建立两表关系

    实体类要求:类名=表名,列名=属性名

6.1 一对多(老师教多名学生)

(1)创建数据表

CREATE TABLE `student` (
`stuid` int(11) NOT NULL AUTO_INCREMENT,
`stuname` varchar(255) DEFAULT NULL,
`teacherid` int(11) DEFAULT NULL,
PRIMARY KEY (`stuid`)
);
INSERT INTO `student` VALUES ('1', 'aaa', '3');
INSERT INTO `student` VALUES ('2', 'bb', '1');
INSERT INTO `student` VALUES ('3', 'cc', '3');
INSERT INTO `student` VALUES ('4', 'dd', '1');
INSERT INTO `student` VALUES ('5', 'ee', '1');
INSERT INTO `student` VALUES ('6', 'ff', '2');
DROP TABLE IF EXISTS `teacher`;
CREATE TABLE `teacher` (
`tid` int(11) NOT NULL AUTO_INCREMENT,
`tname` varchar(255) DEFAULT NULL,
PRIMARY KEY (`tid`)
);
INSERT INTO `teacher` VALUES ('1', '张三老师');
INSERT INTO `teacher` VALUES ('2', '李四老师');
INSERT INTO `teacher` VALUES ('3', '王五');

(2)创建实体类

public class Teacher {
private int tid;
private String tname;
private List<Student> list=new ArrayList<Student>();
    public List<Student> getList() {
    	return list;
    }
    public void setList(List<Student> list) {
        this.list = list;
    }
    public int getTid() {
    	return tid;
    }
    public void setTid(int tid) {
    	this.tid = tid;
    }
    public String getTname() {
    	return tname;
    }
    public void setTname(String tname) {
    	this.tname = tname;
    }
}
public class Student {
private int stuid;
private String stuname;
//外键列一般不生成属性
// private int teacherid;
    public int getStuid() {
    	return stuid;
    }
    public void setStuid(int stuid) {
    	this.stuid = stuid;
    }
    public String getStuname() {
    	return stuname;
    }
    public void setStuname(String stuname) {
    	this.stuname = stuname;
    }
}

(3)定义dao接口

public interface TeacherDao {
//定义操作方法
//1.定义一个根据老师id查询老师信息(学生的信息)
public Teacher getTeacherId(int tid);
}

(4)定义实现类

public class TeacherDaoImpl implements TeacherDao {
    @Override
    public Teacher getTeacherId(int tid) {
        Connection connection = null;
        PreparedStatement pps = null;
        ResultSet resultSet = null;
        Student student = null;
        try {
            //1、加载驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2、连接数据库
            String url = "jdbc:mysql://localhost:3306/mydb?characterEncoding=utf-8";
            String user = "root";
            String password = "root123";
            connection = DriverManager.getConnection(url, user, password);
            //3、创建sql状态预留通道
            String sql = "select * from student1 s,teacher t where s.teacherid=t.tid and t.tid=?";
            pps = connection.prepareStatement(sql);
            //给占位符赋值
            pps.setInt(1, tid);
            resultSet = pps.executeQuery();
            Teacher teacher = new Teacher();
            ArrayList<Student> students = new ArrayList<>();
            while (resultSet.next()) {
                teacher.setTid(resultSet.getInt("tid"));
                teacher.setTname(resultSet.getString("tname"));
                int stuid = resultSet.getInt("stuid");
                String stuname = resultSet.getString("stuname");
                Student student1 = new Student(stuid, stuname);
                students.add(student1);
            }
            teacher.setList(students);
            return teacher;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                //释放资源
                if (resultSet != null) {
                    resultSet.close();
                }
                if (pps != null) {
                    pps.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

(4)定义测试类

public class Demo2 {
    public static void main(String[] args) {
		TeacherDao dao = new TeacherDaoImpl();
        Teacher teacher = dao.getTeacherId(1);
        System.out.println("老师姓名:"+teacher.getTname());
        ArrayList<Student> studentList = teacher.getList();
        for(Student student :studentList)
        {
            System.out.println("\t studentname=" + student.getStuname());
        }
    }
}

6.2 多对一 (学生有多个老师)

(1)实体类

public class Student {
    private int stuid;
    private String stuname;
    private Teacher teacher;

    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

    public Student(int stuid, String stuname) {
        this.stuid = stuid;
        this.stuname = stuname;
    }

    public Student() {
    }

    public int getStuid() {
        return stuid;
    }

    public void setStuid(int stuid) {
        this.stuid = stuid;
    }

    public String getStuname() {
        return stuname;
    }

    public void setStuname(String stuname) {
        this.stuname = stuname;
    }
}

public class Teacher {
    private int tid;
    private String tname;

    public ArrayList<Student> getList() {
        return list;
    }

    public void setList(ArrayList<Student> list) {
        this.list = list;
    }

    private ArrayList<Student> list=new ArrayList<>();

    public Teacher() {
    }

    public Teacher(int tid, String tname) {
        this.tid = tid;
        this.tname = tname;
    }

    public int getTid() {
        return tid;
    }

    public void setTid(int tid) {
        this.tid = tid;
    }

    public String getTname() {
        return tname;
    }

    public void setTname(String tname) {
        this.tname = tname;
    }
}

(2)接口

//查询所有的学生(包含老师的信息)
public List<Student> getAll();

(3)实现类

 	@Override
    public List<Student> getAll() {
        Connection connection = null;
        PreparedStatement pps = null;
        ResultSet resultSet = null;
        Student student = null;
        try {
            //1、加载驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2、连接数据库
            String url = "jdbc:mysql://localhost:3306/mydb?characterEncoding=utf-8";
            String user = "root";
            String password = "root123";
            connection = DriverManager.getConnection(url, user, password);
            //3、创建sql预状态通道
            String sql = "select * from student1 s,teacher t where s.teacherid=t.tid";
            pps = connection.prepareStatement(sql);
            resultSet = pps.executeQuery(sql);
            List<Student> students = new ArrayList<>();
            while (resultSet.next()) {
                int stuid = resultSet.getInt("stuid");
                String stuname = resultSet.getString("stuname");
                Student student1 = new Student(stuid, stuname);
                Teacher teacher = new Teacher();
                teacher.setTid(resultSet.getInt("tid"));
                teacher.setTname(resultSet.getString("tname"));
                student1.setTeacher(teacher);
                students.add(student1);
            }
            return students;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                if (resultSet != null) {
                    resultSet.close();
                }
                if (pps != null) {
                    pps.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

(4) 测试类:

		TeacherDao dao = new TeacherDaoImpl();
        List<Student> studentList = dao.getAll();
        for (Student student : studentList) {
            System.out.println("学生姓名:"+student.getStuname()+",老师姓名:"+student.getTeacher().getTname());
        }

6.3 一对一(妻子丈夫)

数据表:

CREATE TABLE `husband` (
`husid` int(11) NOT NULL AUTO_INCREMENT,
`husname` varchar(255) DEFAULT NULL,
PRIMARY KEY (`husid`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
INSERT INTO `husband` VALUES ('1', '邓超');
DROP TABLE IF EXISTS `wife`;
CREATE TABLE `wife` (
`wifeid` int(11) NOT NULL AUTO_INCREMENT,
`wifename` varchar(255) DEFAULT NULL,
`hid` int(11) DEFAULT NULL,
PRIMARY KEY (`wifeid`),
UNIQUE KEY `uq_wife_hid` (`hid`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
INSERT INTO `wife` VALUES ('1', '孙俪', '1');

(1)实体类

public class Husband {
    private int husid;
    private String husname;
    private Wife wife;

    public Wife getWife() {
        return wife;
    }

    public void setWife(Wife wife) {
        this.wife = wife;
    }

    public int getHusid() {
        return husid;
    }

    public void setHusid(int husid) {
        this.husid = husid;
    }

    public String getHusname() {
        return husname;
    }

    public void setHusname(String husname) {
        this.husname = husname;
    }

    public Husband() {
    }

    public Husband(int husid, String husname) {
        this.husid = husid;
        this.husname = husname;
    }
}

public class Wife {
    private int wifeid;

    private Husband husband;

    public Husband getHusband() {
        return husband;
    }

    public void setHusband(Husband husband) {
        this.husband = husband;
    }

    public int getWifeid() {
        return wifeid;
    }

    public void setWifeid(int wifeid) {
        this.wifeid = wifeid;
    }

    public String getWifename() {
        return wifename;
    }

    public void setWifename(String wifename) {
        this.wifename = wifename;
    }

    public Wife() {
    }

    public Wife(int wifeid, String wifename) {
        this.wifeid = wifeid;
        this.wifename = wifename;
    }

    private String wifename;

}

(2)接口

public interface WifeDao {
	//查询妻子信息(包含丈夫信息)
	public Wife getWife(int wid);
	//查询丈夫信息(包含妻子信息)
	public Husband getHus(int hid);
}

(3)实现类

public class WifeDaoImpl implements WifeDao {
    @Override
    public Wife getWife(int wifeid) {
        Connection connection=null;
        PreparedStatement pps=null;
        ResultSet resultSet =null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            String url="jdbc:mysql://localhost:3306/mydb?characterEncoding=utf-8";
            String user="root";
            String password="root123";
            connection = DriverManager.getConnection(url,user,password);
            String sql="select * from wife w,husband h where w.hid=h.husid and w.wifeid=?";
            pps = connection.prepareStatement(sql);
            pps.setInt(1,wifeid);
            resultSet = pps.executeQuery();
            Wife wife = new Wife();
            while (resultSet.next()){
                //取出信息
                wife.setWifeid(resultSet.getInt("wifeid"));
                wife.setWifename(resultSet.getString("wifename"));
                Husband husband = new Husband();
                husband.setHusid(resultSet.getInt("husid"));
                husband.setHusname(resultSet.getString("husname"));
                //建立与丈夫间的关系
                wife.setHusband(husband);
            }
            return wife;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                if (resultSet != null) {
                    resultSet.close();
                }
                if (pps != null) {
                    pps.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    @Override
    public Husband getHus(int husid) {
        Connection connection=null;
        PreparedStatement pps=null;
        ResultSet resultSet =null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            String url="jdbc:mysql://localhost:3306/mydb?characterEncoding=utf-8";
            String user="root";
            String password="root123";
            connection = DriverManager.getConnection(url,user,password);
            String sql="select * from wife w,husband h where w.hid=h.husid and h.husid=?";
            pps = connection.prepareStatement(sql);
            pps.setInt(1,husid);
            resultSet = pps.executeQuery();

            Husband husband = new Husband();
            while (resultSet.next()){
                //取出信息
                husband.setHusid(resultSet.getInt("husid"));
                husband.setHusname(resultSet.getString("husname"));
                Wife wife = new Wife();
                wife.setWifeid(resultSet.getInt("wifeid"));
                wife.setWifename(resultSet.getString("wifename"));
                //建立与妻子间的关系
                husband.setWife(wife);
            }
            return husband;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                if (resultSet != null) {
                    resultSet.close();
                }
                if (pps != null) {
                    pps.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

(4)测试类

public class Demo {
    public static void main(String[] args) {
        WifeDao wifeDao = new WifeDaoImpl();
        Wife wife = wifeDao.getWife(1);
        System.out.println(wife.getWifename()+"的丈夫是:"+wife.getHusband().getHusname());
        Husband hus = wifeDao.getHus(1);
        System.out.println(hus.getHusname()+"的老婆是:"+hus.getWife().getWifename());
    }
}

6.4 多对多(科目-学生)

数据表

CREATE TABLE `middle` (
`middleid` int(11) NOT NULL AUTO_INCREMENT,
`stuid` int(11) DEFAULT NULL,
`subid` int(11) DEFAULT NULL,
PRIMARY KEY (`middleid`)
) ;
-- ----------------------------
-- Records of middle
-- ----------------------------
INSERT INTO `middle` VALUES ('1', '1', '1');
INSERT INTO `middle` VALUES ('2', '1', '2');
INSERT INTO `middle` VALUES ('3', '1', '3');
INSERT INTO `middle` VALUES ('4', '1', '5');
INSERT INTO `middle` VALUES ('5', '2', '2');
INSERT INTO `middle` VALUES ('6', '3', '2');
INSERT INTO `middle` VALUES ('7', '4', '2');
INSERT INTO `middle` VALUES ('8', '5', '2');
INSERT INTO `middle` VALUES ('9', '6', '2');
-- ----------------------------
-- Table structure for `student`
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`stuid` int(11) NOT NULL AUTO_INCREMENT,
`stuname` varchar(255) DEFAULT NULL,
`teacherid` int(11) DEFAULT NULL,
PRIMARY KEY (`stuid`)
);
-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES ('1', '张三', '3');
INSERT INTO `student` VALUES ('2', '李四', '1');
INSERT INTO `student` VALUES ('3', '王五', '3');
INSERT INTO `student` VALUES ('4', '赵六', '1');
INSERT INTO `student` VALUES ('5', '花花', '1');
INSERT INTO `student` VALUES ('6', '潇潇', '2');
-- ----------------------------
-- Table structure for `subject`
-- ----------------------------
DROP TABLE IF EXISTS `subject`;
CREATE TABLE `subject` (
`subid` int(11) NOT NULL AUTO_INCREMENT,
`subname` varchar(255) DEFAULT NULL,
PRIMARY KEY (`subid`)
);
-- ----------------------------
-- Records of subject
-- ----------------------------
INSERT INTO `subject` VALUES ('1', 'java');
INSERT INTO `subject` VALUES ('2', 'ui');
INSERT INTO `subject` VALUES ('3', 'h5');
INSERT INTO `subject` VALUES ('4', 'c');
INSERT INTO `subject` VALUES ('5', 'c++');
INSERT INTO `subject` VALUES ('6', 'c#');

(1)实体类

public class Subject {
    private int subid;
    private String subname;
    private Student student;
    private List<Student> studentList;

    public int getSubid() {
        return subid;
    }

    public List<Student> getStudentList() {
        return studentList;
    }

    public void setStudentList(List<Student> studentList) {
        this.studentList = studentList;
    }



    public void setSubid(int subid) {
        this.subid = subid;
    }

    public String getSubname() {
        return subname;
    }

    public void setSubname(String subname) {
        this.subname = subname;
    }

    public Student getStudent() {
        return student;
    }

    public void setStudent(Student student) {
        this.student = student;
    }
}

public class Student {
    private int stuid;
    private String stuname;
    private Subject subject;
    private List<Subject> subjectList;

    public int getTeacherid() {
        return teacherid;
    }

    public void setTeacherid(int teacherid) {
        this.teacherid = teacherid;
    }

    private int teacherid;

    public int getStuid() {
        return stuid;
    }

    public void setStuid(int stuid) {
        this.stuid = stuid;
    }

    public String getStuname() {
        return stuname;
    }

    public void setStuname(String stuname) {
        this.stuname = stuname;
    }

    public Subject getSubject() {
        return subject;
    }

    public void setSubject(Subject subject) {
        this.subject = subject;
    }

    public List<Subject> getSubjectList() {
        return subjectList;
    }

    public void setSubjectList(List<Subject> subjectList) {
        this.subjectList = subjectList;
    }
}

(2)接口

public interface SubjectDao {
	//查询学生信息(包括科目信息)
    public Student findByStuId(int stuid);
    //查询科目信息(包括学生信息)
    public Subject findBySubId(int subid);
 
}

(3)实现类

public class SubjectDaoImpl implements SubjectDao {
    @Override
    public Student findByStuId(int stuid) {
        Connection connection = null;
        PreparedStatement pps = null;
        ResultSet resultSet = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            String url = "jdbc:mysql://localhost:3306/mydb?characterEncoding=utf-8";
            String user = "root";
            String password = "root123";
            connection = DriverManager.getConnection(url, user, password);
            String sql = "select * from middle m,student s,subject sub where m.subid=sub.subid and m.stuid=s.stuid and s.stuid=?";
            pps = connection.prepareStatement(sql);
            pps.setInt(1, stuid);
            resultSet = pps.executeQuery();
            Student student = new Student();
            ArrayList<Subject> subjects = new ArrayList<>();
            while (resultSet.next()) {
                student.setStuid(resultSet.getInt("stuid"));
                student.setStuname(resultSet.getString("stuname"));

                Subject subject = new Subject();
                subject.setSubid(resultSet.getInt("subid"));
                subject.setSubname(resultSet.getString("subname"));
                subjects.add(subject);
            }
            // 建立学生与科目关系
            student.setSubjectList(subjects);
            return student;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                if (resultSet != null) {
                    resultSet.close();
                }
                if (pps != null) {
                    pps.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    @Override
    public Subject findBySubId(int subid) {

        Connection connection = null;
        PreparedStatement pps = null;
        ResultSet resultSet = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            String url = "jdbc:mysql://localhost:3306/mydb?characterEncoding=utf-8";
            String user = "root";
            String password = "root123";
            connection = DriverManager.getConnection(url, user, password);
            String sql = "select * from middle m,student s,subject sub where m.subid=sub.subid and m.stuid=s.stuid and sub.subid=?";
            pps = connection.prepareStatement(sql);
            pps.setInt(1, subid);
            resultSet = pps.executeQuery();
            Subject subject = new Subject();
            ArrayList<Student> students = new ArrayList<>();
            while (resultSet.next()) {
                subject.setSubid(resultSet.getInt("subid"));
                subject.setSubname(resultSet.getString("subname"));

                Student student = new Student();
                student.setStuid(resultSet.getInt("stuid"));
                student.setStuname(resultSet.getString("stuname"));
                students.add(student);
            }
            // 建立学生与科目关系
            subject.setStudentList(students);
            return subject;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                if (resultSet != null) {
                    resultSet.close();
                }
                if (pps != null) {
                    pps.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

(4)测试类

public class Demo1 {
    public static void main(String[] args) {
        SubjectDao dao = new SubjectDaoImpl();
        Student stu = dao.findByStuId(1);
        System.out.println(stu.getStuname());
        List<Subject> subjects=stu.getSubjectList();
        for (Subject subject : subjects) {
            System.out.println("\t 课程名:"+subject.getSubname());
        }
        Subject sub = dao.findBySubId(2);
        System.out.println("报了"+sub.getSubname()+"的同学有:");
        List<Student> students=sub.getStudentList();
        for (Student student : students) {
            System.out.println("\t"+student.getStuname());
        }
    }
}

7、数据库事务

7.1 事务概述

数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,要么完全地执 行,要么完全地不执行。 事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更 新面向数据的资源。通过将一组相关操作组合为一个要么全部成功要么全部失败的单元,可以简化错误 恢复并使应用程序更加可靠。一个逻辑工作单元要成为事务,必须满足所谓的ACID(原子性、一致性、 隔离性和持久性)属性。事务是数据库运行中的逻辑工作单位,由DBMS中的事务管理子系统负责事务 的处理。

事务开始于

  • 连接到数据库上,并执行一条DML语句insert、update或delete
  • 前一个事务结束后,又输入了另一条DML语句

事务结束于

  • 执行commit或rollback语句。

  • 执行一条DDL语句,例如create table语句,在这种情况下,会自动执行commit语句。

  • 执行一条DDL语句,例如grant语句,在这种情况下,会自动执行commit。

  • 断开与数据库的连接

  • 执行了一条DML语句,该语句却失败了,在这种情况中,会为这个无效的DML语句执行rollback语句。

10.2 事务的四大特点 (ACID)

actomicity(原子性)

表示一个事务内的所有操作是一个整体,要么全部成功,要么全部失败

consistency(一致性)

表示一个事务内有一个操作失败时,所有的更改过的数据都必须回滚到修改前状态

isolation(隔离性)

事务查看数据时数据所处的状态,要么是另一并发事务修改它之前的状态,要么是另一事务修 改它之后的状态,事务不会查看中间状态的数据。

durability(持久性)

持久性事务完成之后,它对于系统的影响是永久性的。

10.3 JDBC中事务应用

如果JDBC连接处于自动提交模式,默认情况下,则每个SQL语句在完成后都会提交到数据库。

事务使您能够控制是否和何时更改应用于数据库。它将单个SQL语句或一组SQL语句视为一个逻辑单 元,如果任何语句失败,则整个事务将失败。

要启用手动事务支持,而不是JDBC驱动程序默认使用的自动提交模式,请使用Connection对象的 setAutoCommit() 方法。

如果将boolean false传递给setAutoCommit(),则关闭自动提交。我们可以传递一个布尔值true来重新打开它。

10.4 事务的提交和回滚

完成更改后,我们要提交更改,然后在连接对象上调用commit()方法

connection.commit();

否则,要使用连接名为conn的数据库回滚更新

connection.rollback();
try{
	//Assume a valid connection object conn
	conn.setAutoCommit(false);
    Statement stmt = conn.createStatement();
    String SQL = "INSERT INTO Employees values (106, 20, 'Rita', 'Tez')";
    stmt.executeUpdate(SQL);
	//Submit a malformed SQL statement that breaks
	String SQL = "INSERTED IN Employees VALUES (107, 22, 'Sita', 'Singh')";
	stmt.executeUpdate(SQL);
	// If there is no error.
	conn.commit();
}catch(SQLException se){
	// If there is any error.
	conn.rollback();
}

8、JDBC批处理

8.1Statement批处理

以下是使用语句对象的批处理的典型步骤序列

  1. 使用createStatement()方法创建Statement对象。
  2. 使用setAutoCommit()将auto-commit设置为false 。
  3. 使用addBatch()方法在创建的语句对象上添加您喜欢的SQL语句到批处理中。
  4. 在创建的语句对象上使用executeBatch()方法执行所有SQL语句。
  5. 最后,使用commit()方法提交所有更改。
Statement stmt = conn.createStatement();
conn.setAutoCommit(false);
//sql1
String SQL = "INSERT INTO Employees (id, first, last, age) VALUES(200,'Zia','Ali', 30)";
stmt.addBatch(SQL);
//sql2
String SQL = "INSERT INTO Employees (id, first, last, age) VALUES(201,'Raj',
'Kumar', 35)";
stmt.addBatch(SQL);
//sql3
String SQL = "UPDATE Employees SET age = 35 WHERE id = 100";
stmt.addBatch(SQL);
int[] count = stmt.executeBatch();
conn.commit();

8.2 PreparedStatement批处理

  1. 使用占位符创建SQL语句。

  2. 使用prepareStatement() 方法创建PrepareStatement对象。

  3. 用setAutoCommit()将auto-commit设置为false 。

  4. 使用addBatch()方法在创建的语句对象上添加您喜欢的SQL语句到批处理中。

  5. 在创建的语句对象上使用executeBatch()方法执行所有SQL语句。

  6. 最后,使用commit()方法提交所有更改。

String SQL = "INSERT INTO Employees (id, first, last, age) VALUES(?, ?, ?, ?)";
PreparedStatement pstmt = conn.prepareStatement(SQL);
conn.setAutoCommit(false);
// Set the variables
pstmt.setInt( 1, 400 );
pstmt.setString( 2, "Pappu" );
pstmt.setString( 3, "Singh" );
pstmt.setInt( 4, 33 );
// Add it to the batch
pstmt.addBatch();
// Set the variables
pstmt.setInt( 1, 401 );
pstmt.setString( 2, "Pawan" );
pstmt.setString( 3, "Singh" );
pstmt.setInt( 4, 31 );
// Add it to the batch
pstmt.addBatch();
//add more batches
//Create an int[] to hold returned values
int[] count = stmt.executeBatch();
//Explicitly commit statements to apply changes
conn.commit();

9、 反射处理结果集

接口

//查询所有的学生信息
public List<Student> findAll(Class cla);

实现类

public List<Student> findAll(Class cla) {
        Connection connection = null;
        PreparedStatement pps = null;
        ResultSet resultSet = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            String url = "jdbc:mysql://localhost:3306/mydb?characterEncoding=utf-8";
            String user = "root";
            String password = "root123";
            connection = DriverManager.getConnection(url, user, password);
            String sql = "select * from student";
            pps = connection.prepareStatement(sql);
            resultSet = pps.executeQuery();
            Student student = new Student();
            ArrayList students = new ArrayList();
            // 获得数据库中所有的列有哪些
            ResultSetMetaData metaData = resultSet.getMetaData();
            //得到列数
            int count=metaData.getColumnCount();
            // 根据列数获得一个定长的数组
            String[] columns = new String[count];
            // 将每列的名字辅助给数组
            for (int i=0;i<count;i++){
                //列的位置从1开始
                //System.out.println(metaData.getColumnName(i+1));
                columns[i]=metaData.getColumnName(i+1);
            }
            //获取实体类中所有的方法
            Method[] declaredMethods = cla.getDeclaredMethods();
            while (resultSet.next()) {
                try {
                    //获取无参构造函数对象
                    Object o = cla.getDeclaredConstructor().newInstance();
                    for (String column : columns) {
                        //将数据库中获得的列名与set拼接
                        String name="set"+column;
                        for (Method declaredMethod : declaredMethods) {
                            // 将拼接的字符串与实体类中的方法比较
                            if(name.equalsIgnoreCase(declaredMethod.getName())){
                                // 调用invoke方法
                                declaredMethod.invoke(o,resultSet.getObject(column));
                                break;
                            }
                        }
                    }
                    students.add(o);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return students;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                if (resultSet != null) {
                    resultSet.close();
                }
                if (pps != null) {
                    pps.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

10、工具类的定义

public class DBUtil {
    /**
     * //1、定义需要获得的工具类
     */

    private Connection connection = null;
    private PreparedStatement pps = null;
    private ResultSet resultSet = null;
    private static String url;
    private static String user;
    private static String driverClassName;
    private static String password;
    /**
     * 表示受影响的行数
     */
    private int k = 0;
    //2、加载驱动
    static {

        try {
            /**
             * 工具类读取属性方法一
             */
            /*InputStream inputStream = DBUtil.class.getClassLoader().getResourceAsStream("db.properties");
            Properties properties = new Properties();
            properties.load(inputStream);
            url = properties.getProperty("url");
            user = properties.getProperty("username");
            password = properties.getProperty("password");
            diverClassName = properties.getProperty("driverClassName");*/
            /**
             * 工具类读取属性方法二
             */
            ResourceBundle rb=ResourceBundle.getBundle("db");
            url = rb.getString("url");
            user = rb.getString("username");
            password = rb.getString("password");
            driverClassName = rb.getString("driverClassName");
            Class.forName(driverClassName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * //3、连接数据库
     *
     * @return
     */
    protected Connection getConnection() {
        try {
            //普通连接方式
            connection = DriverManager.getConnection(url, user, password);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }

    /**
     * //4、创建通道
     *
     * @param sql
     * @return
     */
    protected PreparedStatement getPps(String sql) {
        try {
            getConnection();
            pps = connection.prepareStatement(sql);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return pps;
    }

    /**
     * //5、给占位符赋值
     *
     * @param list 保存的是给占位符赋值的值
     */
    protected void setParam(List list) {
        if (list != null && list.size() > 0) {
            for (int i = 0; i < list.size(); i++) {
                try {
                    pps.setObject(i + 1, list.get(i));
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * //6、增删改方法调用
     *
     * @param sql
     * @return
     */
    protected int update(String sql, List list) {
        try {
            getPps(sql);
            setParam(list);
            k = pps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return k;
    }

    /**
     * 7、查询方法调用
     *
     * @param sql
     * @param list
     */
    protected ResultSet query(String sql, List list) {
        try {
            getPps(sql);
            setParam(list);
            resultSet = pps.executeQuery();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return resultSet;
    }

    /**
     * 8、释放资源
     */
    protected void close() {
        try {
            if (resultSet != null) {
                resultSet.close();
            }
            if (pps != null) {
                pps.close();
            }
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    Properties ppt = new Properties();
}

10.1 properties文件保存数据库信息

特点: key-value存储方式

url=jdbc:mysql://localhost:3306/mydb?characterEncoding=UTF-8
username=root
password=root123
driverClassName=com.mysql.jdbc.Driver

读取属性两种方式

//1
InputStream inputStream = DBUtil.class.getClassLoader().getResourceAsStream("db.properties");
            Properties properties = new Properties();
            properties.load(inputStream);
            url = properties.getProperty("url");
            user = properties.getProperty("username");
            password = properties.getProperty("password");
            diverClassName = properties.getProperty("driverClassName");
//2
static{
ResourceBundle rb=ResourceBundle.getBundle("db");
            url = rb.getString("url");
            user = rb.getString("username");
            password = rb.getString("password");
            driverClassName = rb.getString("driverClassName");
            Class.forName(driverClassName);
}

使用ResourceBundle访问本地资源

在设计时,我们往往需要访问一些适合本地修改的配置信息,如果作为静态变量,那么每次修改都 需要重新编译一个class,.config保存此类信息并不适合,这时我们需要ResourceBundle。

通过ResourceBundle,我们需要访问位于/WEB-INF/classes目录下的一个后缀名为properties的文本 类型文件,从里面读取我们需要的值。

11、连接池

11.1 自定义连接池

数据连接池原理

​ 连接池基本的思想是在系统初始化的时候,将数据库连接作为对象存储在内存中,当用户需要访问数 据库时,并非建立一个新的连接,而是从连接池中取出一个已建立的空闲连接对象。使用完毕后,用户 也并非将连接关闭,而是将连接放回连接池中,以供下一个请求访问使用。而连接的建立、断开都由连 接池自身来管理。同时,还可以通过设置连接池的参数来控制连接池中的初始连接数、连接的上下限数 以及每个连接的最大使用次数、最大空闲时间等等,也可以通过其自身的管理机制来监视数据库连接的 数量、使用情况等。

自定义连接池一般不使用

11.2 DBCP连接池

DBCP是一个依赖Jakarta commons-pool对象池机制的数据库连接池.DBCP可以直接的在应用程序中 使用,Tomcat的数据源使用的就是DBCP。

步骤:

导入相应jar包

  • mysql-jdbc.jar
  • commons-dbcp.jar
  • commons-pool.jar

硬编码使用DBCP(在代码中添加配置)

public void testHard() throws SQLException{
	// 硬编码 使用DBCP连接池子
	BasicDataSource source = new BasicDataSource();
	//设置连接的信息
    source.setDriverClassName("com.mysql.jdbc.Driver");
    source.setUrl("jdbc:mysql://localhost:3306/day2");
    source.setUsername("root");
    source.setPassword("111");
    Connection connection = source.getConnection();
    String sql = "select * from student";
    Statement createStatement = connection.createStatement();
    ResultSet executeQuery = createStatement.executeQuery(sql);
    while (executeQuery.next()) {
    	System.out.println(executeQuery.getString(2));
    }
	connection.close(); //回收
}

软编码使用DBCP(添加配置文件)

文件名称:db.properties

文件位置: src下

#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/day2
username=root
password=root123
#<!-- 初始化连接 -->
initialSize=10
#最大连接数量
maxActive=50
#<!-- 最大空闲连接 -->
maxIdle=20
#<!-- 最小空闲连接 -->
minIdle=5
#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=6000

工具类实现

static {
            ResourceBundle rb= ResourceBundle.getBundle("db");
            url = rb.getString("url");
            user = rb.getString("username");
            password = rb.getString("password");
            driverClassName = rb.getString("driverClassName");
        try {
            // DBCP连接池
            dataSource.setDriverClassName(driverClassName);
            dataSource.setUrl(url);
            dataSource.setUsername(user);
            dataSource.setPassword(password);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
protected Connection getConnection() {
        try {
            connection = dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }

11.3 C3P0连接池

c3p0与dbcp区别

  1. dbcp没有自动回收空闲连接的功能 c3p0有自动回收空闲连接功能
  2. dbcp需要手动设置配置文件 c3p0不需要手动设置

实现步骤:

导入jar包

c3p0-0.9.1.2.jar

mysql-connector-java-5.0.8.jar

.添加配置文件

文件位置: src

文件命名:c3p0-config.xml/c3p0-config.properties

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
  <default-config>
  	<property name="driverClass">com.mysql.jdbc.Driver</property>
  	<property name="jdbcUrl">jdbc:mysql://localhost:3306/mydb?characterEncoding=UTF-8</property>
  	<property name="user">root</property>
  	<property name="password">root123</property>
  	<!-- 等待连接的超时时间,默认为0,代表无限等待,单位是毫秒 -->
    <property name="checkoutTimeout">30000</property>
    <!-- 检查空闲连接  默认为0 代表不检查 -->
    <property name="idleConnectionTestPeriod">30</property>
    <!-- 初始化连接 -->
    <property name="initialPoolSize">10</property>
    <!-- 最大空闲时间,超过这个时间的连接将被丢弃,默认为0,代表永远不关闭 -->
    <property name="maxIdleTime">50</property>
    <!-- 最大连接数 -->
    <property name="maxPoolSize">100</property>
    <!-- 最小连接数 -->
    <property name="minPoolSize">10</property>
    <!-- preparedStatement的缓存大小 -->
    <property name="maxStatements">200</property>
    <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。默认值: 3 --> 
	<property name="AcquireIncrement">5</property>
  </default-config>
</c3p0-config>

工具类实现

static ComboPooledDataSource db=new ComboPooledDataSource("abc");
protected Connection getConnection() {
        try {
            //c3p0连接方式
            connection = db.getConnection();

        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }

11.4 Druid(德鲁伊)连接池

特点:

一. 亚秒级查询 druid提供了快速的聚合能力以及亚秒级的OLAP查询能力,多租户的设计,是面向用户分析应用的理 想方式。

二.实时数据注入 druid支持流数据的注入,并提供了数据的事件驱动,保证在实时和离线环境下事件的实效性和统一性

三.可扩展的PB级存储 druid集群可以很方便的扩容到PB的数据量,每秒百 万级别的数据注入。即便在加大数据规模的情况 下,也能保证时其效性

四.多环境部署 druid既可以运行在商业的硬件上,也可以运行在云上。它可以从多种数据系统中注入数据,包括 hadoop,spark,kafka,storm和samza等

五.丰富的社区 druid拥有丰富的社区,供大家学习

步骤:

导入jar包

druid-1.0.0.jar

工具类实现

private static DruidDataSource ds;
    //2、加载驱动
    static {
        ds=new DruidDataSource();
        Properties properties = new Properties();
        try {
            // 加载配置文件
            ResourceBundle rb=ResourceBundle.getBundle("db");
            driverClassName= rb.getString("driverClassName");
            user = rb.getString("username");
            password = rb.getString("password");
            url = rb.getString("url");
            ds.setDriverClassName(driverClassName);
            ds.setUsername(user);
            ds.setPassword(password);
            ds.setUrl(url);

        } catch (Exception e) {
            e.printStackTrace();
        }
	}
protected Connection getConnection() {
        try {
            connection=ds.getConnection();
            return connection;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

注:在Druid连接池的配置中,driverClassName可配可不配,如果不配置会根据url自动识别dbType(数据库类型),然后选择相应的driverClassName。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Java中,使用JDBC(Java数据库连接)来连接数据库。以下是一个简单的例子: 1. 导入JDBC驱动程序 在Java应用程序中连接数据库之前,需要先导入JDBC驱动程序。这可以通过将JDBC驱动程序的jar文件添加到类路径中来成。例如,如果您使用MySQL数据库,则需要下载并添加MySQL JDBC驱动程序的jar文件。 2. 加载JDBC驱动程序 在使用JDBC连接数据库之前,需要先加载JDBC驱动程序。可以通过Class.forName()方法来加载JDBC驱动程序。例如,如果您使用MySQL数据库,则需要加载com.mysql.jdbc.Driver类。 3. 建立数据库连接 可以使用DriverManager.getConnection()方法来建立数据库连接。此方法需要指定数据库的URL、用户名和密码。例如,以下是连接到MySQL数据库的示例: ``` import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class JdbcConnection { public static void main(String[] args) { Connection conn = null; try { // 加载JDBC驱动程序 Class.forName("com.mysql.jdbc.Driver"); // 建立数据库连接 String url = "jdbc:mysql://localhost:3306/mydb"; String user = "root"; String password = "123456"; conn = DriverManager.getConnection(url, user, password); System.out.println("数据库连接成功!"); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } finally { // 关闭数据库连接 if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } } ``` 在上面的示例中,我们首先使用Class.forName()方法加载MySQL JDBC驱动程序。然后,我们使用DriverManager.getConnection()方法建立到MySQL数据库的连接。最后,我们在finally块中关闭数据库连接。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值