这篇文章上次修改于 212 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

JDBC 应用程序使用 Driver 接口加载适当的驱动程序,使用 Connection 接口连接到数据库,使用 Statement 接口创建和执行 SQL 语句,如果语句返回结果,则使用 ResultSet 接口处理结果

jdbc流程

建立连接

1.

          //通过DriverManager获取数据库连接
         Connection cn = DriverManager.getConnection("jdbc:mariadb://localhost:3306/test?user=ajian&password=123456");

        System.out.println(cn.toString());
  1. Java 支持自动驱动程序发现,因此您不必显式加载驱动程序。 但是,在撰写本文时,并非所有数据库驱动程序都支持此功能。 为安全起见,请显式加载驱动程序

    Class.forName("JDBCDriverClass");
  2. image-20211018201759729
  3. image-20211018201958798

语句执行

使用Statement执行SQL语句。

executeUpdate()主要用于执行DML和DDL语句。执行DML语句返回受SQL语句影响的行数,执行DDL语句返回0。
executeQuery()只能执行查询语句,执行后返回代表查询结果的ResultSet对象。
DMLinster /update /delete受影响行数
DDLcreate /drop /alter成功0
        //通过Connection对象创建Statement对象
        Statement sd = cn.createStatement();
        
      var result =    sd.executeUpdate("insert  into   user(user_name, user_passwd,user_email,user_status) values (\"safaffa\",\"assss\",\"ajian@email.com\",0 )");
         System.out.println(result);//返回1  受影响行数
       

重复执行的sql

Statement 接口用于执行不包含任何参数的静态 SQL 语句。

PreparedStatement 接口(extend-ing Statement)用于执行预编译的 SQL 语句,可以带参数或不带参数。因为 SQL 语句是预编译的,所以它们对于重复执行非常有效。

 PreparedStatement preparedStatement = connection.prepareStatement ("insert into Student (firstName, mi, lastName) " + "values (?, ?, ?)");

//setX(int parameterIndex, X value);

preparedStatement.setString(1, "Jack");
preparedStatement.setString(2, "A");
preparedStatement.setString(3, "Ryan");

ResultSet rset = preparedStatement.executeQuery();
   var temp= preparedStatement.executeUpdate() ;
   

结果集

操作结果集 如果执行的SQL语句是查询语句,则执行结果将返回一个ResultSet对象,该对象里保存了SQL语句查询的结果。


        ResultSet res = sd.executeQuery("select * from user ");
        //ResultSet对象主要提供了如下两类方法。
        

        //➢ next()、previous()、first()、last()、beforeFirst()、afterLast()、absolute()等移动记录指针的方法。
        //➢ getXxx()方法获取记录指针指向行、特定列的值。该方法既可使用列索引作为参数,也可使用列名作为参数。使用列索引作为参数性能更好,使用列名作为参数可读性更好。
                while (res.next())
               {
                    System.out.println(res.getInt(1) + "  " + res.getString(2) + "  " + res.getString(3) + "  " + res.getString(4));
              }
        
        

回收 close

回收数据库资源,包括关闭`ResultSetStatementConnection等资源。

        res.close();
        sd.close();
        cn.close();

sql语句

创建create

create table Student (
ssn char(9),
firstName varchar(25),
mi char(1),
lastName varchar(25),
birthDate date,
street varchar(25),
phone char(11),
zipCode char(5),
deptId char(4),
primary key (ssn)
);

插入insert

insert into tableName [(column1, column2, ..., column)] values (value1, value2, ..., valuen);


insert into Course (courseId, subjectId, courseNumber, title, numOfCredits) values ('11113', 'CSCI', '3720', 'Database Systems', 3);

更新update

update tableName set column1 = newValue1 [, column2 = newValue2, ...] [where condition];

update Course set numOfCredits = 4 where title = 'Database Systems';

删除delete

delete from tableName [where condition];
delete from Course where title = 'Database Systems';

delete from Course;

查询select

select column-list from table-list [where condition];

select firstName, mi, lastName from Student where deptId = 'CS' and zipCode = '31411';

您可以在模式 p 中使用通配符 %(百分比符号)和 _(下划线符号)。
% 匹配零个或多个字符,
_ 匹配 s 中的任何单个字符。
例如,

lastName like '_mi%'     匹配任何第二个和第三个字母是 m 和 i 的字符串

lastName not like '_mi%'   排除第二个和第三个字母是 m 和 i 的任何字符串。

between-and 运算符使用以下语法检查值 v 是否在其他两个值 v1 和 v2 之间

v between v1 and v2           //    v >= v1 and v <= v2 
v not between  v1 and v2      // v < v1 or v > v2 .

is null 运算符使用以下语法检查值 v 是否为空:

v is null or v is not null

SQL 提供了 distinct 关键字,可用于消除结果中的重复元组

select distinct subjectId as "Subject ID" from Course; 

SQL 提供了 order by 子句来使用以下语法对输出进行排序

select column-list from table-list [ where condition] [ order by 某列];
//默认情况下,顺序是升序。 要按降序排序 请附加 desc 关键字
select lastName, firstName, deptId from Student where deptId = 'CS' order by lastName desc ;

销毁drop

select column-list from table-list [where condition];

select firstName, mi, lastName from Student where deptId = 'CS' and zipCode = '31411';

修改用户有远程权限

1.让数据库有远程监听选项

2. 更改 账号权限

select User, host from mysql.user;

root账户中的host项是localhost表示该账号只能进行本地登录,我们需要修改权限,输入命令:

GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'password' WITH GRANT OPTION;

修改权限。%表示针对所有IP,password表示将用这个密码登录root用户,如果想只让某个IP段的主机连接,可以修改为

GRANT ALL PRIVILEGES ON *.* TO 'root'@'192.168.100.%' IDENTIFIED BY 'my-new-password' WITH GRANT OPTION;

注意:此时远程连接的密码可能与你在本地登录时的密码不同了,主要看你在IDENTIFIED BY后面给了什么密码

FLUSH PRIVILEGES;
mysql restart

2.(另)创建有远程权限的用户

3.得到(重置) 远程密码

use mysql;
select User,authentication_string,Host from user;

host默认都是localhost访问权限

 alter user 'root'@'%' identified by '123';
 FLUSH PRIVILEGES;

重置远程密码

mysql:MySQL数据库修改用户权限(远程访问权限、操作权限)

其他

自动提交

关闭自动提交 设置回滚点

setAutoCommit(false) 方法禁用自动提交

因此所有 SQL 语句都被分组到一个事务中,该事务通过调用

commit() 或 rollback() 方法终止。 rollback() 方法撤消事务所做的所有更改。

批处理

//从 连接 创建 批处理接口
Statement statement = connection.createStatement();
// 添加任务
statement.addBatch("create table T (C1 integer, C2 varchar(15))");
statement.addBatch( "insert into T values (100, 'Smith')" );
statement.addBatch( "insert into T values (200, 'Jones')" );
// 批量执行 分别返回
int count[] = statement.executeBatch();

要确定驱动程序是否支持批量更新,请在 DatabaseMetaData 实例上调用 supportsBatchUpdates()。

您不能在批处理中执行 SELECT 语句

存储过程

CallableStatement 接口旨在执行 SQL 存储过程。 过程可能有 IN、OUT 或 IN OUT 参数。

IN 参数在调用时接收传递给过程的值。 OUT 参数在过程完成后返回一个值,但在调用过程时它不包含任何值。

IN-OUT 参数包含在调用过程时传递给过程的值,并在完成后返回一个值。

例如,Oracle PL/SQL 中的以下过程具有 IN 参数 p1 、 OUT 参数 p2 和 IN OUT 参数 p3

create or replace procedure sampleProcedure
(p1 in varchar, p2 out number, p3 in out integer) 
begin
/* do something */
end sampleProcedure;


//注册

create or replace function studentFound 
(first varchar2, last varchar2) 
return number is numberOfSelectedRows number := 0;

begin

select count(*) into numberOfSelectedRows
from Student
where Student.firstName = first and
Student.lastName = last;

return numberOfSelectedRows;

end studentFound;
//使用



CallableStatement callableStatement =connection.prepareCall(
"{call sampleProcedure(?, ?, ?)}");
CallableStatement callableStatement =connection.prepareCall(
"{? = call studentFound(?, ?)}");
//调用 两种方式   ,注册 存储过程句柄函数


callableStatement.setString(2, firstName);
callableStatement.setString(3, lastName);
//设置 传出类型
callableStatement.registerOutParameter(1, Types.INTEGER);
callableStatement.execute();  //或 executeUpdate ()

callableStatement.getInt(1);



存储过程这一篇就够了

元数据

通过DatabaseMetaData接口 从 连接句柄 获取数据库URL、用户名、JDBC驱动名称等数据库元数据,

DatabaseMetaData dbMetaData = connection.getMetaData();

System.out.println("database URL: " + dbMetaData.getURL());

System.out.println("database username:"+dbMetaData.getUserName());

System.out.println("database product name: "+dbMetaData.getDatabaseProductName());

System.out.println("database product version: "+dbMetaData.getDatabaseProductVersion());

System.out.println("JDBC driver name: " +
 dbMetaData.getDriverName());

System.out.println("JDBC driver version: " +
 dbMetaData.getDriverVersion());

System.out.println("JDBC driver major version: " +
 dbMetaData.getDriverMajorVersion());

System.out.println("JDBC driver minor version: " +
 dbMetaData.getDriverMinorVersion());

System.out.println("Max number of connections: " +
 dbMetaData.getMaxConnections());

System.out.println("MaxTableNameLength: " +
 dbMetaData.getMaxTableNameLength());

System.out.println("MaxColumnsInTable: " +
 dbMetaData.getMaxColumnsInTable());
----------------------------------------------

//使用 结果集 从 连接元数据 获取 getTables 
ResultSet rsTables = dbMetaData.getTables(null, null, null,new String[] {"TABLE"});
while (rsTables.next()) 
    System.out.print(rsTables.getString("TABLE_NAME") + " ");

通过ResultSetMetaData接口 从结果集 获取表列数、列名等结果集元数据。

ResultSet resultSet = statement.executeQuery
 ("select * from Enrollment");

ResultSetMetaData rsMetaData =
    resultSet.getMetaData();

for (int i = 1; i <= rsMetaData.getColumnCount(); i++)
    System.out.printf("%-12s\t",rsMetaData.getColumnName(i));
 
 while (resultSet.next()) {
     for (int i = 1; i <=rsMetaData.getColumnCount(); i++)
            System.out.printf("%-12s\t",resultSet.getObject(i));
 }

----------------------------------------------------------------------------------------
private static void displayResultSet(ResultSet resultSet) throws SQLException {
     ResultSetMetaData rsMetaData = resultSet.getMetaData();
     resultSet.beforeFirst();
     while (resultSet.next()) {
         for (int i = 1; i <= rsMetaData.getColumnCount(); i++)
             System.out.printf("%-12s\t", resultSet.getObject(i));
          System.out.println(); }
     }
}

要查找结果集中的列数,首先使用

ResultSet 上的 getMetaData ()方法创建 ResultSetMetaData 实例。

使用 getColumnCount ()返回列计数,

使用 getColumnName (int)返回列名

可更新结果集

访问数据库的一个更强大的方法是使用可滚动和可更新的结果,这使您能够向前和向后滚动行,并使用第一个、最后一个、下一个、上一个、绝对或相对方法将光标移动到所需的位置。此外,还可以在结果集中插入、删除或更新行,并将更改自动反映到数据库中。

// 连接 创建 滚动 句柄  //  句柄 执行的 结果集 都是 可滚动
Statement statement = connection.createStatement(int resultSetType, int resultSetConcurrency);

PreparedStatement statement = connection.prepareStatement(String sql, int resultSetType, int resultSetConcurrency);

//resultSetType     
TYPE_FORWARD_ONLY :按顺序向前访问结果集。
TYPE_SCROLL_INSENSITIVE:结果集是可滚动的,但对数据库中的更改不敏感。
TYPE_SCROLL_SENSITIVE:结果集是可滚动的,并且对其他人所做的更改敏感。 如果您希望结果集可滚动和更新,请使用此类型。

// resultSetConcurrency 
CONCUR_READ_ONLY:结果集不能用于更新数据库。 
CONCUR_UPDATABLE:结果集可用于更新数据库。 例如,如果您希望结果集可滚动和可更新,您可以

    
    Statement statement = connection.createStatement
(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
------------------------------------------------------------------
    // 连接 创建 滚动 句柄  //  句柄 执行的 结果集 都是 可滚动
ResultSet resultSet = statement.executeQuery(query);


// first() 、 next() 、 previous() 和  last()  absolute(int row) 方法将光标移动到
//  第一行、   下一行、   上一行和       最后一行   指定行

    //getXxx(int columnIndex) 或 getXxx(String columnName) 方法用于检索当前行指定字段的值
    // insertRow() 、 deleteRow() 和 updateRow() 方法也可用于插入、删除和更新当前行。 
 
    // Update the second row
    resultSet.absolute(2); // Move cursor to the second row
    resultSet.updateString("state", "New S"); // Update the column
     resultSet.updateString("capital", "New C"); // Update the column
     resultSet.updateRow(); // Update the row in the data source
    //在应用 insertRow 或 updateRow 之前,需要使用 updateXxx (int columnIndex,Xxx 值)或 update (String columnName,Xxx 值)方法向当前行的字段写入一个新值



    
    //CancelRowUpdates ()方法取消对行的更新。Close ()方法关闭结果集并释放其资源。如果最后一列读取的值为 sqlnull,则 wasNull ()方法返回 true。

可更新的 ResultSet 对象有一个与之关联的特殊行,用作构建要插入的行的暂存区。 此特殊行称为插入行。

要插入一行,首先调用 moveToInsertRow() 方法将光标移动到插入行(第 36 行),然后使用 updateXxx 方法更新列(第 37-38 行),最后使用 insertRow() 插入行 方法(第 39 行) 调用 moveToCurrentRow() 将光标移动到当前插入的行(第 40 行)

// Insert after the last row
resultSet.last();
resultSet.moveToInsertRow(); // Move cursor to the insert row
resultSet.updateString("state", "Florida");
resultSet.updateString("capital", "Tallahassee");
resultSet.insertRow(); // Insert the row
resultSet.moveToCurrentRow(); // Move the cursor to the current row 

可以在 DatabaseMetaData 接口中使用 supportsResultSetType (int type)和 supportsResultSetConcurrency (int type,int concurrency)来查找 JDBC 驱动程序支持哪种结果类型和模式

Image

SQL3 引入了一种称为 BLOB(Binary Large OBject)的新数据类型,用于存储二进制数据,可用于存储图像

另一种新的 SQL3 类型是 CLOB(Character Large OBject),用于以字符格式存储大文本

​ 您可以使用 getBlob 、 setBinaryStream 、 getClob 、 setBlob 和 setClob 来访问接口 ResultSet 和 PreparedStatement 中的 SQL BLOB 和 CLOB 值。


create table Country(name varchar(30), flag blob, description varchar(255));
PreparedStatement pstmt = connection.prepareStatement("insert into Country values(?, ?, ?)" );

File file = new File(imageFilename);
InputStream inputImage = new FileInputStream(file);
//图像通常存储在文件中。 您可以首先获取图像文件的 InputStream 实例,然后使用 setBinaryStream 方法将输入流与表中的单元格关联,
pstmt.setBinaryStream(2, inputImage, (int)(file.length())); 
-
//恢复图像
Blob blob = rs.getBlob( 1 );
ImageIcon imageIcon = new ImageIcon( blob.getBytes( 1 , (int)blob.length()));