6

数据库:JDBC的简单使用

 2 years ago
source link: https://blackdn.github.io/2022/07/29/SQL-JDBC-2022/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

“萤火点点,流光难寻;星海横流,岁月成碑。”

数据库:JDBC的简单使用

这篇文章本来是放到上一篇数据库:SQL从入门到入门里的
但是那样太长了,看着很不舒服
所以干脆分出来再水一篇了=。=
看了眼目录,这应该是我有史以来最简单的文章了=。=

JDBC使用

JDBC即Java Database Connectivity,是Java为关系数据库定义的访问接口
对于MySQL来说,其JDBC的驱动就是一个jar包,它本身是纯Java编写的,因此用户可以引用java.sql包下面的相关接口访问MySQL

SQL定义的数据类型和Java本身的有一点区别,常见数据类型对照表如下:

Java类型 SQL类型
boolean BIT, BOOL
short SMALLINT
int INTEGER
long BIGINT
float REAL
double FLOAT, DOUBLE
String CHAR, VARCHAR
BigDecimal DECIMAL
java.sql.Date DATE
java.sql.Time TIME

JDBC连接数据库并查询

讲道理连接数据库都要指定什么库名,用户名,密码,端口啥的,太麻烦了就跳过这些配置了=。=
总之,我们会通过java.sql包Connection类来获取一个数据库连接,而Connection对象通常由DriverManager类来获取,其参数就是数据库的配置信息

Connection conn = DriverManager.getConnection(<Uri>, <Username>, <Password>);

然后,ConnectioncreateStatement()方法可以创建一个Statement对象,该对象提供的executeQuery()方法允许我们通过SQL语句执行查询,最后用ResultSet来保存执行得到结果。
不过由于Statement对象不能阻止SQL注入,因此现在多用PreparedStatement,其使用?作为占位符,并且把数据连同SQL本身传给数据库,而非字符串,因此可以保证不被注入。
更多关于SQL注入的内容可见:SQL Injection 从入门到不精通

PreparedStatement statement = connection.prepareStatement("SELECT `id`, `name` FROM `users` WHERE `id`=?");
statement.setLong(1, id);
ResultSet resultSet = statement.executeQuery();

其中,statement.setLong(1, id)表示PreparedStatement中第一个占位符?的内容是变量id的内容(因为其数据类型是long,在数据库中是BIGINT
假设long id = 1,其替换占位符,则SQL语句就变为"SELECT name FROM users WHERE id=1"

SQL执行完后,可以用ResultSetnext()方法来获取查询到的结果。当然结果可能不止一条,因此有多条数据的话就可以用循环来获取,如果没有数据了,next()方法会返回false
此外,StatmentResultSet都是需要关闭的资源,因此嵌套使用try (resource)确保及时关闭
所以最后整个操作会变成这样:

//use try-statement to close in time
try (Connection connection = DatabaseConnectionProvider.createConnection(configuration)) {
    try (PreparedStatement statement = connection.prepareStatement("SELECT `id`, `name` FROM `users` WHERE `id`=?")) {
        statement.setLong(1, id); //config statement
        ResultSet resultSet = statement.executeQuery();
        while (resultSet.next()) {
            long fitId = resultSet.getLong("id");
            String fitName = resultSet.getString("name");
        }
    }
}

上面的例子中,在获取数据的时候是根据字段名来的,也可以根据SQL语句中的字段顺序来获取,比如上面是SELECT id, name,则id为1,name为2
因此可以分别写成getLong(1)getString(2)

JDBC插入数据

和查询类似,我们还是用PreparedStatement执行一条SQL语句,不过最后执行的就不是executeQuery()

//use try-statement to close in time
try (Connection connection = DatabaseConnectionProvider.createConnection(configuration)) {
    try (PreparedStatement statement = connection.prepareStatement("INSERT INTO `users` (`name`) VALUES (?)", Statement.RETURN_GENERATED_KEYS)) {
        statement.setString(1, name);
        statement.execute();
        //get result
        ResultSet resultSet = statement.getGeneratedKeys();
        resultSet.next();
        resultSet.getLong(1);
    }
}

PrepareStatement中,除了SQL语句,我们还加了一个Statement.RETURN_GENERATED_KEYS),表示获取插入记录的自增主键(这里是id),毕竟在数据库不可视的情况下我们是不知道现在自增到哪了。

这里用的是execute(),其既可以执行查询语句,返回true;又可以执行插入更新删除语句,返回false
事实上,还可以用executeUpdate()方法,该方法返回一个int,表示受影响的行数。插入一条数据则返回1,两条数据则返回2。

成功执行后,因为我们还想要获取自增主键的值,所以要用getGeneratedKeys()来获取结果ResultSet。因为我只插入了一条数据,所以我能肯定只返回一条(就是我插入的这条),而getLong(1)就是返回的自增主键的值。
如果一次插入多条记录,那么这个ResultSet对象就会有多行返回值(就要用循环和next()来获取);如果插入时有多列自增,那么ResultSet对象的每一行都会对应多个自增值(就要getLong(1)getLong(2)等)

JDBC更新数据

有了插入数据的例子,更新就很简答了

//use try-statement to close in time
try (Connection connection = DatabaseConnectionProvider.createConnection(configuration)) {
    try (PreparedStatement statement = connection.prepareStatement("UPDATE `users` SET `name`=? WHERE `id`=?")) {
        statement.setBoolean(1, name);
        statement.setLong(2, id);
        if (statement.executeUpdate() == 1) {
            System.out.println("update successfully");
        }
    }
}

同样使用PreparedStatement执行SQL语句,值得注意的是这里出现了多个占位符?,因此在为这些占位符设置参数的时候,从1开始从左到右表示对应的占位符。
因为我第一个占位符是name,第二个占位符是id,所以下面分别用1和2来为其设置具体的值。

JDBC删除

删除也没啥,改一下SQL语句就行了,用execute()executeUpdate()都可以,能够根据具体情况选择。

try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
    try (PreparedStatement ps = conn.prepareStatement("DELETE FROM `users` WHERE `id`=?")) {
        ps.setObject(1, id); 
        if (statement.executeUpdate() == 1) {
            System.out.println("delete successfully");
        }
    }
}

JDBC建表

因为这一段是后来加的,所以放在后面,不过其实很简单,也就是SQL语句加上statement.execute()

try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
    try (PreparedStatement ps = conn.prepareStatement("CREATE TABLE IF NOT EXISTS `users` (\n" +
                "`id`           BIGINT          PRIMARY KEY AUTO_INCREMENT,\n" +
                "`name`         VARCHAR(128)    NOT NULL,\n" +
                ")")) {
        ps.execute();
    }
}

因为我们在SQL语句中加上了IF NOT EXISTS,所以不用担心重复创建表,如果已经有了重名的表,这个SQL语句就不会执行的。

JDBC其他操作

判断表是否存在

很多时候我们想在操作前先判断表是否存在,最原始的方法就是catch抛出的异常,再根据异常进行操作。
但是显然这样很麻烦,如果有多个异常,还得去获取异常中的信息。
为此,我们可以使用DatabaseMetaData类的getTables方法,比如我们查看是否存在users表:

DatabaseMetaData metaData = connection.getMetaData();
ResultSet resultSet = metaData.getTables(null, null, "users", new String[]{"TABLE"});
boolean flag = resultSet.next();    //existed: true

getTables()的四个参数分别如下。

参数 作用
String catalog 规定用来寻找表名的目录,通常为null
String schemaPattern 数据库名,对于oracle来说就是用户名,可为null
String tableNamePattern 用于匹配表名,可以使用通配符来匹配多个表。比如”%”表示任意表名,”T_“表示两个字的表名,第一个字母为T
String[] types 表的类型(TABLE或VIEW),用于缩小检索范围

最后返回一个ResultSet,因为上面这个例子只匹配一个表,所以直接用resultSet.next()获得结果,如果存在则为true,不存在则为false
如果利用通配符匹配多个表,那么结果需要循环调用resultSet.next()

JDBC事务

事务在之前SQL的内容有所提及,具体可见:
而在JDBC中要使用事务,就要用代码的思维来决定一下顺序
首先我们要用setAutoCommit(false)来关闭自动提交,否则JDBC会执行一行提交一行;然后我们执行多条SQL语句,最后提交事务,重新打开自动提交。
当然还要抓取异常,如果出现了异常就要回滚,从而保证事务的原子性。
于是就有了以下框架:

try {
    connection.setAutoCommit(false);	 // 关闭自动提交
    //insert(); update(); delete();... (执行多条SQL语句)
    connection.commit();    // 提交事务
} catch (SQLException e) {
    connection.rollback();    // 回滚事务
} finally {
    connection.setAutoCommit(true);
    connection.close();
}


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK