当前位置:   article > 正文

Java-JDBC连接数据库_javajdbc连接数据库

javajdbc连接数据库

目录

一、JDBC开发步骤

1.Java程序连接数据库

1.1引入MySQL驱动包

1.2Java连接MySQL步骤

2 实现增删改查操作

2.1 添加数据

2.2 修改数据

2.3 删除数据

2.4 查询数据

二、JDBC处理相关问题

1 解决SQL注入问题

1.1、问题演示

1.2、解决问题

2 JDBC事务处理

3 获取自增长键值

4 批处理操作


一、JDBC开发步骤

1.Java程序连接数据库

1.1引入MySQL驱动包

使用Java连接MySQL之前需要先引入MySQL驱动jar包。

创建Java项目,并引入MySQL驱动jar包到项目中,如下图示例:

 

1.2Java连接MySQL步骤

A、代码示例:

  1. //注册驱动:把驱动类加载到内存中
  2. //注意:5.1版本驱动包中驱动类名:com.mysql.jdbc.Driver
  3. //8.0版本驱动类名:com.mysql.cj.jdbc.Driver
  4. Class.forName("com.mysql.cj.jdbc.Driver");
  5. //与数据库建立连接
  6. Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/atguigu","root","1234");
  7. //实现增删改查数据
  8. //.....
  9. //关闭连接:如果不再使用连接需要断开连接以释放资源(底层是TCP/IP协议和IO流操作)
  10. conn.close();
  11. //程序能正常编译执行表示连接成功,如果抛异常表示连接失败。

B、步骤说明:

  1. 注册驱动

    此步骤的目的是把驱动类加载到内存中,可以通过以下方式实现:

    1. //方式一:不推荐,因会导致注册驱动被执行两次(看源码),并且代码强依赖数据库驱动jar
    2. DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
    3. //方式二:创建了两个Driver对象(见源码),并且强依赖数据库驱动
    4. new com.mysql.cj.jdbc.Driver();
    5. //方式三:推荐,反射方式,接收字符串参数,降低了对驱动类的依赖
    6. Class.forName("com.mysql.cj.jdbc.Driver");

    实际在JDK6之后DriverManager就已经可以实现自动注册驱动,如果手动注册了驱动,不再自动注册。但是仍然建议显示通过反射方式注册驱动。

    需要驱动包中此位置文件META-INF/services/java.sql.Driver 中包含内容:com.mysql.cj.jdbc.Driver

  2. 与数据库建立连接

    加载驱动程序后,可以使用DriverManager的重载方法getConnection创建Connection对象,每个Connection对象表示Java程序与数据库之间的一个物理连接。

    Connection conn = getConnection(String url,String user,String password);

其中不同数据库URL配置不同:

RDBMSJDBC驱动程序名称URL格式
MySQLcom.mysql.cj.jdbc.Driverjdbc:mysql://hostname / databaseName
ORACLEoracle.jdbc.driver.OracleDriverjdbc:oracle:thin:@ hostname:port Number:databaseName
DB2com.ibm.db2.jdbc.net.DB2Driverjdbc:db2:hostname:port Number / databaseName

URL格式说明:

协议:子协议://主机名:端口/数据库名?参数名1=参数值1&参数名2=参数值2 其中,如果主机是本机或端口是默认3306端口时,可以缺省。如:jdbc:mysql:///databaseName

示例:jdbc:mysql://localhost:3306/testdb?useUnicode=true&characterEncoding=utf8(如果JDBC程序与服务器端的字符集不一致,会导致乱码,那么可以通过参数指定服务器端的字符集)

使用某些版本驱动包,建立连接时可能还需要设置服务器时区参数:

jdbc:mysql://localhost:3306/dbname?serverTimezone=Asia/Shanghai

三个重载方法创建连接示例:

  1. //方式一:
  2. Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testJDBC?user=root&password=1234");
  3. //方式二:
  4. Properties info = new Properties();
  5. info.setProperty("user", "root");
  6. info.setProperty("password", "1234");
  7. Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testJDBC",info);
  8. //方式三:(推荐方式)
  9. Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testJDBC","root","1234");

2 实现增删改查操作

2.1 添加数据

  1. /*
  2. 用JDBC实现添加一条记录到atguigu数据库的t_department表中。
  3. mysql> desc t_department;
  4. +-------------+--------------+------+-----+---------+----------------+
  5. | Field | Type | Null | Key | Default | Extra |
  6. +-------------+--------------+------+-----+---------+----------------+
  7. | did | int | NO | PRI | NULL | auto_increment |
  8. | dname | varchar(20) | NO | UNI | NULL | |
  9. | description | varchar(200) | YES | | NULL | |
  10. +-------------+--------------+------+-----+---------+----------------+
  11. 3 rows in set (0.01 sec)
  12. mysql> select * from t_department;
  13. +-----+--------+------------------+
  14. | did | dname | description |
  15. +-----+--------+------------------+
  16. | 1 | 研发部 | 负责研发工作 |
  17. | 2 | 人事部 | 负责人事管理工作 |
  18. | 3 | 市场部 | 负责市场推广工作 |
  19. | 4 | 财务部 | 负责财务管理工作 |
  20. | 5 | 后勤部 | 负责后勤保障工作 |
  21. | 6 | 测试部 | 负责测试工作 |
  22. +-----+--------+------------------+
  23. 6 rows in set (0.00 sec)
  24. 步骤:
  25. 1、注册驱动
  26. 2、获取数据库连接
  27. 3、获取Statement对象,用来执行sql
  28. 4、执行sql,即执行Statement对象的方法:int executeUpdate(),(增删改操作时)
  29. 5、释放资源
  30. */
  31. public class TestInsert {
  32. public static void main(String[] args)throws Exception {
  33. //1.注册驱动,把驱动类加载到内存中
  34. Class.forName("com.mysql.cj.jdbc.Driver");
  35. //2.创建数据库连接
  36. Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/atguigu","root","1234");
  37. //3.获取StateMent
  38. String sql = "insert into t_department values(null,'数据部门','数据部门简介')";
  39. PreparedStatement pst = conn.prepareStatement(sql);
  40. //4.执行SQL,插入数据,返回sql影响的记录数
  41. int len = pst.executeUpdate();
  42. System.out.println(len>0 ? "添加成功" : "添加失败");
  43. //5.释放资源
  44. pst.close();
  45. conn.close();
  46. /*
  47. mysql> select * from t_department;
  48. +-----+--------------+------------------+
  49. | did | dname | description |
  50. +-----+--------------+------------------+
  51. | 1 | 研发部 | 负责研发工作 |
  52. | 2 | 人事部 | 负责人事管理工作 |
  53. | 3 | 市场部 | 负责市场推广工作 |
  54. | 4 | 财务部 | 负责财务管理工作 |
  55. | 5 | 后勤部 | 负责后勤保障工作 |
  56. | 6 | 测试部 | 负责测试工作 |
  57. | 7 | 数据部门 | 数据部门简介 |
  58. +-----+--------------+------------------+
  59. 7 rows in set (0.00 sec)
  60. */
  61. }
  62. }

2.2 修改数据

  1. public class TestUpdate {
  2. public static void main(String[] args)throws Exception {
  3. //1.把驱动类加载到内存中
  4. Class.forName("com.mysql.cj.jdbc.Driver");
  5. //2.获取数据库连接对象
  6. String url = "jdbc:mysql://localhost:3306/atguigu";
  7. Connection conn = DriverManager.getConnection(url, "root", "1234");
  8. //3.获取Statement对象
  9. String sql = "update t_department set description = 'xx' where did = 7";
  10. PreparedStatement pst = conn.prepareStatement(sql);
  11. //4.执行SQL,修改数据, 返回sql影响的记录数
  12. int len = pst.executeUpdate();
  13. System.out.println(len > 0 ? "修改成功" : "修改失败");
  14. //5.释放资源
  15. pst.close();
  16. conn.close();
  17. }
  18. }
  19. /*
  20. mysql> select * from t_department;
  21. +-----+--------------+------------------+
  22. | did | dname | description |
  23. +-----+--------------+------------------+
  24. | 1 | 研发部 | 负责研发工作 |
  25. | 2 | 人事部 | 负责人事管理工作 |
  26. | 3 | 市场部 | 负责市场推广工作 |
  27. | 4 | 财务部 | 负责财务管理工作 |
  28. | 5 | 后勤部 | 负责后勤保障工作 |
  29. | 6 | 测试部 | 负责测试工作 |
  30. | 7 | 测试数据部门 | xx |
  31. +-----+--------------+------------------+
  32. 7 rows in set (0.00 sec)
  33. */

2.3 删除数据

  1. public class TestDelete {
  2. public static void main(String[] args)throws Exception {
  3. //1.把驱动类加载到内存中
  4. Class.forName("com.mysql.cj.jdbc.Driver");
  5. //2.获取数据库连接对象
  6. String url = "jdbc:mysql://localhost:3306/atguigu";
  7. Connection conn = DriverManager.getConnection(url, "root", "1234");
  8. //3.获取Statement对象
  9. String sql = "delete from t_department where did = 7";
  10. PreparedStatement pst = conn.prepareStatement(sql);
  11. //4.执行SQL,删除数据, 返回sql影响的记录数
  12. int len = pst.executeUpdate();
  13. System.out.println(len > 0 ? "删除成功" : "删除失败");
  14. //5.释放资源
  15. pst.close();
  16. conn.close();
  17. }
  18. }
  19. /*
  20. mysql> select * from t_department;
  21. +-----+--------+------------------+
  22. | did | dname | description |
  23. +-----+--------+------------------+
  24. | 1 | 研发部 | 负责研发工作 |
  25. | 2 | 人事部 | 负责人事管理工作 |
  26. | 3 | 市场部 | 负责市场推广工作 |
  27. | 4 | 财务部 | 负责财务管理工作 |
  28. | 5 | 后勤部 | 负责后勤保障工作 |
  29. | 6 | 测试部 | 负责测试工作 |
  30. +-----+--------+------------------+
  31. 6 rows in set (0.00 sec)
  32. */

2.4 查询数据

  1. /*
  2. 步骤:
  3. 1、注册驱动
  4. 2、获取数据库连接
  5. 3、获取Statement对象,用来执行sql
  6. 4、执行sql,即执行Statement对象的方法:
  7. (1)int executeUpdate():执行insert,update,delete等更新数据库数据的sql
  8. (2)ResultSet executeQuery():执行select查询的sql,返回一个结果集
  9. (3)boolean execute():可以用来执行DDL语句
  10. 5、遍历结果集ResultSet:
  11. boolean next():判断是否还有下一行
  12. getObject(字段名或序号),getString(字段名或序号),getInt(字段名或序号)等
  13. 6、释放资源
  14. */
  15. public class TestSelect {
  16. public static void main(String[] args)throws Exception {
  17. //1.注册驱动,把驱动类加载到内存中
  18. Class.forName("com.mysql.cj.jdbc.Driver");
  19. //2.获取数据库连接对象
  20. String url = "jdbc:mysql://localhost:3306/atguigu";
  21. Connection conn = DriverManager.getConnection(url, "root", "1234");
  22. //3.获取Statement对象
  23. String sql = "select * from t_department";
  24. PreparedStatement pst = connn.prepareStatement(sql);
  25. //4.执行查询SQL,返回查询结果
  26. ResultSet resultSet = pst.executeQuery();
  27. //5.遍历结果集
  28. while(rs.next()){ //while循环一次,迭代一行,遍历一行
  29. int did = rs.getInt("did");//get一次得到一个单元格的数据
  30. String dname = rs.getString("dname");
  31. String decription = rs.getString("description");
  32. System.out.println(did +"\t" + dname +"\t" + decription);
  33. }
  34. //释放资源
  35. rs.close();
  36. pst.close();
  37. conn.close();
  38. }
  39. }

二、JDBC处理相关问题

1 解决SQL注入问题

PrepareStatement接口是Statement的子接口,提供了更优秀的功能

返回值方法名含义
ResultSetexecuteQuery()执行预处理sql语句的查询操作
intexecuteUpdate()执行预处理sql语句的增删改操作
voidsetInt(int parameterIndex, int x)设置sql参数
voidsetFloat(int parameterIndex, float x)设置sql参数
voidsetString(int parameterIndex, String x)设置sql参数
voidsetDate(int parameterIndex, java.sql.Date x)设置sql参数
voidsetObject(int parameterIndex, Object x)设置sql参数
.........

1.1、问题演示

(1)SQL语句拼接

  1. //提取用户名和密码变量,模拟登录操作
  2. String name = "tom";
  3. String password = "123456";
  4. String sql =
  5. "select * from users where name='" + name + "' and password='" + password + "'";
  6. System.out.println("sql="+sql);
  7. Statement stmt = conn.createStatement();
  8. ResultSet rs = stmt.executeQuery(sql);

(2)SQL注入  

  1. String name = "tom";
  2. String password="' or '1'='1";// 如果登录时键盘录入密码为' or '1'='1时,结果登录成功
  3. String sql =
  4. "select * from users where name='" + name + "' and password='" + password + "'";
  5. System.out.println("sql="+sql);
  6. Statement stmt = conn.createStatement();
  7. ResultSet rs = stmt.executeQuery(sql);

(3)处理blob等类型的数据

  • 如果数据库表中字段定义是blob类型时,需要写入的是二进制数据,即需要通过字节流写入二进制数据,这时无法把数据直接拼接成sql字符串。

1.2、解决问题

(1)避免sql拼接

  1. String sql = "insert into users(name,password,email,birthday) values(?,?,?,?)";
  2. PreparedStatement pst = conn.prepareStatement(sql);//这里要传带?的sql,然后mysql端就会对这个sql进行预编译
  3. //设置?的具体值
  4. /*pst.setString(1, name);
  5. pst.setString(2, password);
  6. pst.setString(3, email);
  7. pst.setDouble(4, birthday);*/
  8. pst.setObject(1, name);
  9. pst.setObject(2, password);
  10. pst.setObject(3, email);
  11. pst.setObject(4, birthday);
  12. int len = pst.executeUpdate();//此处不能传sql
  13. System.out.println(len);

(2)不会有sql注入问题

  1. //即使输入' or '1'= '1也没问题
  2. String sql = "select * from users where name=? and password=?";
  3. String name = "tom";
  4. String password = "123123";
  5. PreparedStatement pstmt = conn.prepareStatement(sql);
  6. pstmt.setString(1,name );
  7. pstmt.setString(2,password );
  8. ResultSet rs = pstmt.executeQuery();

(3)处理blob类型的数据  

  1. //pic字段为blob类型
  2. String sql = "insert into users(name,pic) value(?,?)";
  3. PreparedStatement pstmt = conn.prepareStatement(sql);
  4. pstmt.setString(1,"tom" );
  5. pstmt.setBlob(2,new FileInputStream("D:/1.jpeg"));
  6. int i = pstmt.executeUpdate();
  7. System.out.println(i > 0 ? "成功" : "失败");
  • 注意两个问题:

    ①my.ini关于上传的字节流文件有大小限制,可以在my.ini中修改变量max_allowed_packet值

    max_allowed_packet=16M

    ②每一种blob有各自大小限制:

    tinyblob:255字节、blob:65k、mediumblob:16M、longblob:4G

2 JDBC事务处理

 采用转账案例

  1. /*
  2. * mysql默认每一个连接是自动提交事务的。
  3. * 那么当我们在JDBC这段,如果有多条语句想要组成一个事务一起执行的话,那么在JDBC这边怎么设置手动提交事务呢?
  4. * (1)在执行之前,设置手动提交事务
  5. * Connection的对象.setAutoCommit(false)
  6. * (2)成功:
  7. * Connection的对象.commit();
  8. * 失败:
  9. * Connection的对象.rollback();
  10. *
  11. * 补充说明:
  12. * 为了大家养成要的习惯,在关闭Connection的对象之前,把连接对象设置回自动提交
  13. * (3)Connection的对象.setAutoCommit(true)
  14. *
  15. * 因为我们现在的连接是建立新的连接,那么如果没有还原为自动提交,没有影响。
  16. * 但是我们后面实际开发中,每次获取的连接,不一定是新的连接,而是从连接池中获取的旧的连接,而且你关闭也不是真关闭,
  17. * 而是还给连接池,供别人接着用。以防别人拿到后,以为是自动提交的,而没有commit,最终数据没有成功。
  18. */
  19. public class TestTransaction {
  20. public static void main(String[] args) throws Exception{
  21. /*
  22. * 一般涉及到事务处理的话,那么业务逻辑都会比较复杂。
  23. * 例如:购物车结算时:
  24. * (1)在订单表中添加一条记录
  25. * (2)在订单明细表中添加多条订单明细的记录(表示该订单买了什么东西)
  26. * (3)修改商品表的销量和库存量
  27. * ...
  28. * 那么我们今天为了大家关注事务的操作,而不会因为复杂的业务逻辑的影响导致我们的理解,那么我们这里故意
  29. * 用两条修改语句来模拟组成一个简单的事务。
  30. * update t_department set description = 'xx' where did = 2;
  31. * update t_department set description = 'yy' where did = 3;
  32. *
  33. * 我希望这两条语句要么一起成功,要么一起回滚
  34. * 为了制造失败,我故意把第二条语句写错
  35. * update t_department set description = 'yy' (少了where) did = 3;
  36. */
  37. //1、注册驱动
  38. Class.forName("com.mysql.cj.jdbc.Driver");
  39. //2、获取连接
  40. Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "1234");
  41. //设置手动提交事务
  42. conn.setAutoCommit(false);
  43. //3、执行sql
  44. String sql1 = "update t_department set description = 'xx' where did = 2";
  45. String sql2 = "update t_department set description = 'yy' did = 3";//这句错的
  46. //使用prepareStatement的sql也可以不带问号?
  47. PreparedStatement pst = null;
  48. try {
  49. pst = conn.prepareStatement(sql1);
  50. int len = pst.executeUpdate();
  51. System.out.println("第一条:" + (len>0?"成功":"失败"));
  52. pst = conn.prepareStatement(sql2);
  53. len = pst.executeUpdate();
  54. System.out.println("第二条:" + (len>0?"成功":"失败"));
  55. //都成功了,就提交事务
  56. System.out.println("提交");
  57. conn.commit();
  58. } catch (Exception e) {
  59. System.out.println("回滚");
  60. //失败要回滚
  61. conn.rollback();
  62. }
  63. //4、关闭
  64. pst.close();
  65. conn.setAutoCommit(true);//还原为自动提交
  66. conn.close();
  67. }
  68. }

3 获取自增长键值

  1. /*
  2. * 我们通过JDBC往数据库的表格中添加一条记录,其中有一个字段是自增的,那么在JDBC这边怎么在添加之后直接获取到这个自增的值
  3. * PreparedStatement是Statement的子接口。
  4. * Statement接口中有一些常量值:
  5. * (1)Statement.RETURN_GENERATED_KEYS
  6. *
  7. * 要先添加后获取到自增的key值:
  8. * (1)PreparedStatement pst = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
  9. * (2)添加sql执行完成后,通过PreparedStatement的对象调用getGeneratedKeys()方法来获取自增长键值,遍历结果集
  10. * ResultSet rs = pst.getGeneratedKeys();
  11. */
  12. public class TestAutoIncrement {
  13. public static void main(String[] args) throws Exception{
  14. //1、注册驱动
  15. Class.forName("com.mysql.cj.jdbc.Driver");
  16. //2、获取连接
  17. Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "123456");
  18. //3、执行sql
  19. String sql = "insert into t_department values(null,?,?)";
  20. /*
  21. * 这里在创建PreparedStatement对象时,传入第二个参数的作用,就是告知服务器端
  22. * 当执行完sql后,把自增的key值返回来。
  23. */
  24. PreparedStatement pst = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
  25. //设置?的值
  26. pst.setObject(1, "测试部");
  27. pst.setObject(2, "测试项目数据");
  28. //执行sql
  29. int len = pst.executeUpdate();//返回影响的记录数
  30. if(len>0){
  31. //从pst中获取到服务器端返回的键值
  32. ResultSet rs = pst.getGeneratedKeys();
  33. //因为这里的key值可能多个,因为insert语句可以同时添加多行,所以用ResultSet封装
  34. //这里因为只添加一条,所以用if判断
  35. if(rs.next()){
  36. Object key = rs.getObject(1);
  37. System.out.println("自增的key值did =" + key);
  38. }
  39. }
  40. //4、关闭
  41. pst.close();
  42. conn.close();
  43. }
  44. }

4 批处理操作

  1. /*
  2. * 批处理:
  3. * 批量处理sql
  4. *
  5. * 例如:
  6. * (1)订单明细表的多条记录的添加
  7. * (2)批量添加模拟数据
  8. * ...
  9. *
  10. * 不用批处理,和用批处理有什么不同?
  11. * 批处理的效率很多
  12. *
  13. * 如何进行批处理操作?
  14. * (1)在url中要加一个参数
  15. * rewriteBatchedStatements=true,默认值false会发送多次sql请求
  16. * 那么我们的url就变成了 jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
  17. * 这里的?,表示?后面是客户端给服务器端传的参数,多个参数直接使用&分割
  18. * (2)调用方法不同
  19. * pst.addBatch();
  20. * int[] all = pst.executeBatch();
  21. *
  22. * 注意:如果批量添加时,insert使用values,不要使用value,否则相当于发送多次sql执行
  23. */
  24. public class TestBatch {
  25. public static void main(String[] args) throws Exception{
  26. long start = System.currentTimeMillis();
  27. //例如:在部门表t_department中添加1000条模拟数据
  28. //1、注册驱动
  29. Class.forName("com.mysql.cj.jdbc.Driver");
  30. //2、获取连接
  31. Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true", "root", "123456");
  32. //3、执行sql
  33. String sql = "insert into t_department values(null,?,?)";
  34. PreparedStatement pst = conn.prepareStatement(sql);
  35. //设置?的值
  36. for (int i = 1; i <=1000; i++) {
  37. pst.setObject(1, "模拟部门"+i);
  38. pst.setObject(2, "模拟部门的简介"+i);
  39. pst.addBatch();//添加到批处理一组操作中,攒一块处理
  40. /* if(i % 500 == 0){//有时候也攒一部分,执行一部分
  41. //2.执行
  42. pst.executeBatch();
  43. //3.清空
  44. pst.clearBatch();
  45. }*/
  46. }
  47. //执行批处理操作
  48. pst.executeBatch();
  49. //4、关闭
  50. pst.close();
  51. conn.close();
  52. long end = System.currentTimeMillis();
  53. System.out.println("耗时:" + (end - start));//耗时:821
  54. }
  55. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/秋刀鱼在做梦/article/detail/847944
推荐阅读
相关标签
  

闽ICP备14008679号