Java-JDBC连接数据库

目录

一、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、代码示例:

//注册驱动:把驱动类加载到内存中
//注意:5.1版本驱动包中驱动类名:com.mysql.jdbc.Driver
//8.0版本驱动类名:com.mysql.cj.jdbc.Driver
Class.forName("com.mysql.cj.jdbc.Driver");

//与数据库建立连接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/atguigu","root","1234");

//实现增删改查数据
//.....

//关闭连接:如果不再使用连接需要断开连接以释放资源(底层是TCP/IP协议和IO流操作)
conn.close();
//程序能正常编译执行表示连接成功,如果抛异常表示连接失败。

B、步骤说明:

  1. 注册驱动

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

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

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

//方式一:
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testJDBC?user=root&password=1234");

//方式二:
Properties info = new Properties();
info.setProperty("user", "root");
info.setProperty("password", "1234");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testJDBC",info);

//方式三:(推荐方式)
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testJDBC","root","1234");

2 实现增删改查操作

2.1 添加数据

/*
用JDBC实现添加一条记录到atguigu数据库的t_department表中。

mysql> desc t_department;
+-------------+--------------+------+-----+---------+----------------+
| Field       | Type         | Null | Key | Default | Extra          |
+-------------+--------------+------+-----+---------+----------------+
| did         | int          | NO   | PRI | NULL    | auto_increment |
| dname       | varchar(20)  | NO   | UNI | NULL    |                |
| description | varchar(200) | YES  |     | NULL    |                |
+-------------+--------------+------+-----+---------+----------------+
3 rows in set (0.01 sec)


mysql> select * from t_department;
+-----+--------+------------------+
| did | dname  | description      |
+-----+--------+------------------+
|   1 | 研发部 | 负责研发工作     |
|   2 | 人事部 | 负责人事管理工作 |
|   3 | 市场部 | 负责市场推广工作 |
|   4 | 财务部 | 负责财务管理工作 |
|   5 | 后勤部 | 负责后勤保障工作 |
|   6 | 测试部 | 负责测试工作     |
+-----+--------+------------------+
6 rows in set (0.00 sec)

步骤:
1、注册驱动
2、获取数据库连接
3、获取Statement对象,用来执行sql
4、执行sql,即执行Statement对象的方法:int executeUpdate(),(增删改操作时)
5、释放资源
 */
public class TestInsert {
    public static void main(String[] args)throws Exception {
        //1.注册驱动,把驱动类加载到内存中
        Class.forName("com.mysql.cj.jdbc.Driver");
        //2.创建数据库连接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/atguigu","root","1234");
        //3.获取StateMent
        String sql = "insert into t_department values(null,'数据部门','数据部门简介')";
        PreparedStatement pst = conn.prepareStatement(sql);
        //4.执行SQL,插入数据,返回sql影响的记录数
        int len = pst.executeUpdate();
        System.out.println(len>0 ? "添加成功" : "添加失败");
        //5.释放资源
        pst.close();
        conn.close();
        /*
mysql> select * from t_department;
+-----+--------------+------------------+
| did | dname        | description      |
+-----+--------------+------------------+
|   1 | 研发部       | 负责研发工作     |
|   2 | 人事部       | 负责人事管理工作 |
|   3 | 市场部       | 负责市场推广工作 |
|   4 | 财务部       | 负责财务管理工作 |
|   5 | 后勤部       | 负责后勤保障工作 |
|   6 | 测试部       | 负责测试工作     |
|   7 | 数据部门 	  | 数据部门简介    |
+-----+--------------+------------------+
7 rows in set (0.00 sec)
         */
    }
}

2.2 修改数据

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

2.3 删除数据

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

2.4 查询数据

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

二、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语句拼接

//提取用户名和密码变量,模拟登录操作
String name = "tom";
String password = "123456";
String sql =
    "select * from users where name='" + name + "' and password='" + password + "'";
System.out.println("sql="+sql);

Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);

(2)SQL注入  

String name = "tom";
String password="' or '1'='1";// 如果登录时键盘录入密码为' or '1'='1时,结果登录成功
String sql =
    "select * from users where name='" + name + "' and password='" + password + "'";
System.out.println("sql="+sql);

Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);

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

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

1.2、解决问题

(1)避免sql拼接

String sql = "insert into users(name,password,email,birthday) values(?,?,?,?)";
PreparedStatement pst = conn.prepareStatement(sql);//这里要传带?的sql,然后mysql端就会对这个sql进行预编译

//设置?的具体值
/*pst.setString(1, name);
		pst.setString(2, password);
		pst.setString(3, email);
		pst.setDouble(4, birthday);*/

pst.setObject(1, name);
pst.setObject(2, password);
pst.setObject(3, email);
pst.setObject(4, birthday);

int len = pst.executeUpdate();//此处不能传sql
System.out.println(len);

(2)不会有sql注入问题

//即使输入' or '1'= '1也没问题
String sql = "select * from users where name=? and password=?";
String name = "tom";
String password = "123123";
PreparedStatement pstmt = conn.prepareStatement(sql);

pstmt.setString(1,name );
pstmt.setString(2,password );
ResultSet rs = pstmt.executeQuery();

(3)处理blob类型的数据  

//pic字段为blob类型
String sql = "insert into users(name,pic) value(?,?)";
PreparedStatement pstmt = conn.prepareStatement(sql);

pstmt.setString(1,"tom" );
pstmt.setBlob(2,new FileInputStream("D:/1.jpeg"));

int i = pstmt.executeUpdate();
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事务处理

 采用转账案例

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

3 获取自增长键值

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

4 批处理操作

/*
 * 批处理:
 * 	批量处理sql
 * 
 * 例如:
 * (1)订单明细表的多条记录的添加
 * (2)批量添加模拟数据
 * ...
 * 
 * 不用批处理,和用批处理有什么不同?
 * 批处理的效率很多
 * 
 * 如何进行批处理操作?
 * (1)在url中要加一个参数
 *     rewriteBatchedStatements=true,默认值false会发送多次sql请求
 *     那么我们的url就变成了  jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
 *     这里的?,表示?后面是客户端给服务器端传的参数,多个参数直接使用&分割
 * (2)调用方法不同
 * pst.addBatch();
 * int[] all = pst.executeBatch();
 * 
 * 注意:如果批量添加时,insert使用values,不要使用value,否则相当于发送多次sql执行
 */
public class TestBatch {
	
	public static void main(String[] args) throws Exception{
		long start = System.currentTimeMillis();
		//例如:在部门表t_department中添加1000条模拟数据
		//1、注册驱动
		Class.forName("com.mysql.cj.jdbc.Driver");
		
		//2、获取连接
		Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true", "root", "123456");
		
		//3、执行sql
		String sql = "insert into t_department values(null,?,?)";
		PreparedStatement pst = conn.prepareStatement(sql);
		
		//设置?的值
		for (int i = 1; i <=1000; i++) {
			pst.setObject(1, "模拟部门"+i);
			pst.setObject(2, "模拟部门的简介"+i);
			
			pst.addBatch();//添加到批处理一组操作中,攒一块处理
/*			if(i % 500 == 0){//有时候也攒一部分,执行一部分
				//2.执行
				pst.executeBatch();
				//3.清空
				pst.clearBatch();
			}*/
		}
        //执行批处理操作
		pst.executeBatch();
		
		//4、关闭
		pst.close();
		conn.close();
		
		long end = System.currentTimeMillis();
		System.out.println("耗时:" + (end - start));//耗时:821
	}
}
  • 5
    点赞
  • 67
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值