Java使用传统JDBC操作数据库

什么是JDBC?

        JDBC是Java DataBase Connectivity的缩写,它是Java程序访问数据库的标准接口。 使用Java程序访问数据库时,Java代码并不是直接通过直接建立TCP连接去访问数据库,而是通过JDBC接口来建立连接,而JDBC接口则通过JDBC驱动来实现真正对数据库的访问。在JDBC驱动实现类中,通过建立TCP连接来建立真正的网络通讯连接。具体的JDBC驱动是由数据库厂商提供的,他们都是实现Java提供的JDBC标准,来实现操作本公司旗下的数据库,例如Mysql数据库许需要 mysql-connector-java-版本号 的驱动包(jar包),有了驱动包之后,我们就能实现Java操作数据库了。

JDBC的相关接口(类)

加载驱动类:DriverManager驱动管理类使用该驱动类,进行连接的获取与创建JDBC驱动包中的Driver类的对象。
        不推荐:硬编码,应用程序与数据库驱动耦合
        com.mysql.cj.jdbc.Driver driver = new com.mysql.cj.jdbc.Driver();
        推荐 : 反射的方式创建Driver类的对象,高版本的JDBC驱动包,可以自动加载驱动类,该步骤可以省略
        Class.forName("com.mysql.cj.jdbc.Driver");

Connection 数据连接接口:获取并创建Connection接口的实现类对象(比如MySQL的ConnectionImpl实现类)

        参数1:数据库连接字符串(JDBC连接字符串):jdbc:数据库类型:数据库地址(服务器地址、端口号、数据库名称)?连接参数
         jdbc:mysql://localhost:3306/数据库名称?charset=utf8mb4&useSSL=false&useTimezone=true&serverTimezone=GMT%2B8
        参数2:数据库用户名
        参数3:数据库密码

 Statement(PreparedStatement)数据库操作接口:执行SQL语句

ResultSet 数据库结果集接口:执行select语句后,获取查询结果

JDBC查询(Query)操作

使用Statement:

步骤:

1.建立连接、设置数据库连接参数;

2.更新SQL语句;

3.创建Statement数据库操作对象,执行executeQuery(),执行后获取查询ResultSet结果集对象;

4.释放资源。

注意:由于Connection、Statement、ResultSet都是需要关闭的资源,所以这里使用try with resource,实现自动关闭。

package com.fulian.demo01;

import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

// 使用Statement执行查询
public class Test02 {
	public static void main(String[] args) {
		// 数据库连接参数
		final String JDBC_URL = "jdbc:mysql://localhost:3306/数据库名称?charset=utf8mb4&useSSL=false&useTimezone=true&serverTimezone=GMT%2B8";
		final String DB_USER_NAME = "用户名";
		final String DB_USER_PASS = "密码";

		// 查询所有用户
		try (Connection con = DriverManager.getConnection(JDBC_URL, DB_USER_NAME, DB_USER_PASS);) {
			System.out.println("数据库连接成功!" + con);

			// 2.SQL
			int val1 = 1, val2 = 6;
			String sql = "SELECT * FROM user_info WHERE user_id >= " + val1 + " AND user_id <= " + val2;
			System.out.println("sql = " + sql);

			// 2.1 创建Statement数据库操作对象
			try (Statement st = con.createStatement();) {
				System.out.println(st);

				// 2.2 执行SQL(查询),执行后获取查询ResultSet结果集对象
				try (ResultSet rs = st.executeQuery(sql);) {
					System.out.println(rs);

					// 2.3
					// 获取某一行中若干字段值
					while (rs.next()) {
						// 如果rs.next()方法的返回值为true,代表存在下一行数据
						// rs则移动游标(指向标),只想下一行
						// rs.getXXX()方法,则开始读取游标只读取游标指向的行
						int userId = rs.getInt(1); // 获取当前行中的第1个字段值(user_id用户编号字段)
						String phoneNumber = rs.getString(2); // 获取当前行中的第2个字段值(login_phone_number登陆手机号码字段)
						String realName = rs.getString(3); // 获取当前行中的第3个字段值(user_real_name用户真实姓名字段)
						Date lastLogin = rs.getDate(4); // 获取当前行中的第4个字段值(last_login_date_time字段)

						System.out.println("用户编号:" + userId);
						System.out.println("手机号码:" + phoneNumber);
						System.out.println("真实姓名:" + realName);
						System.out.println("最后登录:" + lastLogin);
						System.out.println();
					}
				}
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

使用PreparedStatement

优点:1.完全避免SQL注入问题,保持数据库的安全性;

           2.PreparedStatement创建时,传入带有?占位符的SQL语句,保证每次传入的sql语句都是相同的,只是占位符的数据不同,执行时,基于MySQL预编译机制,提高数据库的执行效率。

步骤:

1.建立连接、设置数据库连接参数;

2.更新SQL语句;

3.创建PreparedStatement数据库操作对象,执行executeQuery(),执行后获取查询ResultSet结果集对象;

4.释放资源。

package com.fulian.demo01;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class Test04 {
	public static void main(String[] args) {
		// 数据库连接参数
		final String JDBC_URL = "jdbc:mysql://localhost:3306/数据库名称?charset=utf8mb4&useSSL=false&useTimezone=true&serverTimezone=GMT%2B8";
		final String DB_USER_NAME = "用户名";
		final String DB_USER_PASS = "密码";

		// 用户登录
		try (Connection con = DriverManager.getConnection(JDBC_URL, DB_USER_NAME, DB_USER_PASS);) {
			System.out.println("数据库连接成功!" + con);

			// 2.SQL
			String phoneNumber = "12345678900";
			String loginPassword = "1794";
			String sql = "SELECT * FROM user_info WHERE login_phone_number = ? AND login_password = ?";
			System.out.println("sql = " + sql);

			// 2.1 创建PreparedStatement数据库操作对象
			// PreparedStatement创建时,传入带有?占位符的SQL语句,并执行
			// PreparedStatement执行时,基于MySQL预编译机制,提高数据库的执行效率
			try (PreparedStatement pst = con.prepareStatement(sql);) {
				// 执行前,处理?占位符
				pst.setString(1, phoneNumber);
				pst.setString(2, loginPassword);
				
				// 2.2 执行SQL(查询)
				try (ResultSet rs = pst.executeQuery();) {
					// 输出登录状态 
					if (rs.next()) {
						System.out.println("登录成功!");
					}else {
						System.out.println("登录失败!");
					}
				}
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}

JDBC修改(Update)操作

        在jdbc中,所有的增加、删除、修改都是update操作,步骤如下:    

        1.建立连接、设置数据库连接参数;

        2.更新SQL语句;

        3.创建PreparedStatement数据库操作对象,执行executeUpdate()方法,执行后获取修改行数;

        4.释放资源。

以增加数据为例,代码如下:

package com.fulian.demo01;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class Test08 {
	public static void main(String[] args) {
		// 数据库连接参数
		final String JDBC_URL = "jdbc:mysql://localhost:3306/数据库名称?charset=utf8mb4&useSSL=false&useTimezone=true&serverTimezone=GMT%2B8";
		final String DB_USER_NAME = "用户名";
		final String DB_USER_PASS = "密码";

		// 增加
		// 1.创建数据库连接
		try (Connection con = DriverManager.getConnection(JDBC_URL, DB_USER_NAME, DB_USER_PASS);) {
			// 2.SQL
			String sql = "INSERT INTO material_info(material_name,material_stock,material_unit)VALUES(?,?,?)";
			try(PreparedStatement pst = con.prepareStatement(sql);){
				pst.setString(1, "消毒棉片片");
				pst.setInt(2, 98);
				pst.setString(3, "包");
				
				int rows = pst.executeUpdate();
				System.out.println("影响行数:" + rows);
			}
			
		
			
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

那么,如果想要增加一条数据并返回新增加数据的主键呢

在创建PreparedStatement的时候,指定一个RETURN_GENERATED_KEYS标志位,表示JDBC驱动必须返回插入的自增主键。示例代码如下:

        try(PreparedStatement pst = con.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);){
				pst.setString(1, "消毒玻璃水");
				pst.setInt(2, 532);
				pst.setString(3, "瓶");
				
				// 先执行添加操作
				int rows = pst.executeUpdate();
				System.out.println("影响行数:" + rows);
				
				// 在获取自动生成的主键值
				ResultSet rs = pst.getGeneratedKeys();
				if(rs.next()) {
					int newRecord = rs.getInt(1);
					System.out.println("新纪录的编号值:" + newRecord);
				}
			}

JDBC的事务与批处理

JDBC事务

        数据库事务(Transaction)是由若干个SQL语句构成的一个操作序列。数据库系统保证在一个事务中的所有SQL要么全部执行成功,要么全部不执行,即数据库事务具有ACID特性:  Atomicity:原子性 、Consistency:一致性、Isolation:隔离性、Durability:持久性。

        在JDBC中,有两种事务类型,一种是隐式事务,执行完sql语句自动提交事务,另一种是显式事务,需要在jdbc中手动关闭自动提交,每次执行完sql语句,必须得手动提交或回滚。

package com.fulian.demo02;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class Test02 {
	public static void main(String[] args) {
		// 转出账户
		String sourceAccount = "张三丰";
		int sourceMoney = 5000;
		
		// 转入帐户
		String targetAccount = "李四狗";
		int targetMoney = 5000;
		
		// 数据库连接参数
		final String JDBC_URL = "jdbc:mysql://localhost:3306/数据库名称?charset=utf8mb4&useSSL=false&useTimezone=true&serverTimezone=GMT%2B8";
		final String DB_USER_NAME = "用户名";
		final String DB_USER_PASS = "密码";

		// 数据库连接
		try (Connection con = DriverManager.getConnection(JDBC_URL, DB_USER_NAME, DB_USER_PASS);) {
			
			// 关闭自动提交事务
			con.setAutoCommit(false);
			
			// 转出
			String sql1 = "UPDATE bank SET currentMoney = currentMoney - ? WHERE customerName = ?";
			try (PreparedStatement pst1 = con.prepareStatement(sql1);){
				pst1.setInt(1, sourceMoney);
				pst1.setString(2, sourceAccount);
				
				int row1 = pst1.executeUpdate();
				System.out.println("转出操作影响行数:" + row1);
				
				// 转入
				String sql2 = "UPDATE bank SET currentMoney = currentMoney + ? WHERE customerName = ?";
				try (PreparedStatement pst2 = con.prepareStatement(sql2);){
					pst2.setInt(1, targetMoney);
					pst2.setString(2, targetAccount);
					
					int row2 = pst2.executeUpdate();
					System.out.println("转入操作影响行数:" + row2);
				} 
			} catch (Exception e) {
				e.printStackTrace();
				// 手动回滚事务
				con.rollback();
			}			
			
			// 手动提交事务
			con.commit();
			
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

JDBC批处理

        使用JDBC操作数据库的时候,经常会执行一些批量操作。SQL数据库对SQL语句相同,但只有参数不同的若干语句可以作为batch批处理执行,即批量执行,每次执行一条一句之后,addBath(),添加到内存中,等待全部加载完成后,执行executeBatch(),统一进行操作,以插入若干条数据为例,代码如下:

package com.fulian.demo02;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

public class Test03 {
	public static void main(String[] args) {
		List couponList = Arrays.asList(
				new Coupon(CouponType.CASH, 500, new Date(System.currentTimeMillis()  + 7*24*60*60*1000)),
				new Coupon(CouponType.CASH, 40, new Date(System.currentTimeMillis()  + 5*24*60*60*1000)),
				new Coupon(CouponType.DISSCOUNT, 0.66, new Date(System.currentTimeMillis()  + 8*24*60*60*1000)),
				new Coupon(CouponType.CASH, 500, new Date(System.currentTimeMillis()  + 14*24*60*60*1000)),
				new Coupon(CouponType.SCORE, 5000, new Date(System.currentTimeMillis()  + 2*24*60*60*1000)),
				new Coupon(CouponType.SCORE, 9999, new Date(System.currentTimeMillis()  + 1*24*60*60*1000)),
				new Coupon(CouponType.CASH, 100, new Date(System.currentTimeMillis()  + 5*24*60*60*1000)),
				new Coupon(CouponType.CASH, 200, new Date(System.currentTimeMillis()  + 6*24*60*60*1000)),
				new Coupon(CouponType.DISSCOUNT, 0.1, new Date(System.currentTimeMillis()  + 14*24*60*60*1000)),
				new Coupon(CouponType.DISSCOUNT, 0.95, new Date(System.currentTimeMillis()  + 14*24*60*60*1000)),
				new Coupon(CouponType.SCORE, 23450, new Date(System.currentTimeMillis()  + 30*24*60*60*1000)),
				new Coupon(CouponType.CASH, 500, new Date(System.currentTimeMillis()  + 7*24*60*60*1000))
				);
		
		// 数据库连接参数
		final String JDBC_URL = "jdbc:mysql://localhost:3306/数据库名称?charset=utf8mb4&useSSL=false&useTimezone=true&serverTimezone=GMT%2B8";
		final String DB_USER_NAME = "用户名";
		final String DB_USER_PASS = "密码";

		// 数据库连接
		try (Connection con = DriverManager.getConnection(JDBC_URL, DB_USER_NAME, DB_USER_PASS);) {
			String sql = "INSERT INTO coupon_info(type,amount,expires)VALUES(?,?,?)";
			
			// 采用批处理
			try(PreparedStatement pst = con.prepareStatement(sql)){
				for(Coupon coupon : couponList) {
					pst.setNString(1, coupon.getType().name()); // 优惠卷类型
					pst.setDouble(2, coupon.getAmount()); // 面额
					pst.setDate(3, new java.sql.Date(coupon.getExpries().getTime())); // 过期时间
					pst.addBatch(); // 添加至批处理
				}
				
				// 整体执行批处理
				int[] rows = pst.executeBatch();
				System.out.println("影响行数:" + Arrays.toString(rows));
			}
			
			
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

JDBC连接池

        从上面的例子中,我们可以看到,每次执行JDBC操作时,都要设置数据库连接参数,调用DriverManager.getConnection()方法,因此我们可以封装一个DBUtils1工具类,代码如下:

package com.fulian.demo03;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

// 数据库工具类
public class DBUtils1 {

	// 数据库连接参数
	private static final String JDBC_URL = "jdbc:mysql://localhost:3306/my_db?charset=utf8mb4&useSSL=false&useTimezone=true&serverTimezone=GMT%2B8";
	private static final String DB_USER_NAME = "root";
	private static final String DB_USER_PASS = "menghao0624";

	// 获取数据库连接
	public static Connection getConnection() {
		try {
			Connection con = DriverManager.getConnection(JDBC_URL,DB_USER_NAME,DB_USER_PASS);
			return con;
		} catch (SQLException e) {
			e.printStackTrace();
			return null;
		}
	}
}

        但是,在执行JDBC的增删改查的操作时,每一次操作都来必须创建并打开Connection数据库连接,执行SQL语句,最后关闭Connection连接。在这个过程中,Connection连接被频繁创建和关闭,这个操作非常消耗内存与CPU等系统资源。并且,关闭Connection需要手动完成,一旦忘记会造成数据库服务器连接耗尽。在一些高并发访问数据库的场景下,手动的创建和关闭数据库连接,有可能会造成系统性能瓶颈。

        因此,我们就可以使用数据库的连接池,数据库连接池负责分配、管理和释放数据库连接,它的核心思想就是连接复用。通过建立一个数据库连接池,并在连接池池中维护若干个连接对象。当用户想要连接数据库,就要先从连接池中获取连接对象,然后操作数据库。

        目前主流的数据库连接池有:HikariCP、 C3P0、BoneCP、Druid,这里我们使用C3P0连接池技术,需要添加:

 然后,我们就可以使用C3P0再次封装一个DBUtils2工具类(基于java代码实现)和DBUtils3工具类(基于配置文件实现):

DBUtils2:

package com.fulian.demo04;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import com.mchange.v2.c3p0.ComboPooledDataSource;

// 基于数据库连接池技术的工具类
// C3P0
public class DBUtils2 {
	// 数据库连接参数
	private static final String JDBC_URL = "jdbc:mysql://localhost:3306/数据库名称?charset=utf8mb4&useSSL=false&useTimezone=true&serverTimezone=GMT%2B8";
	private static final String DB_USER_NAME = "用户名";
	private static final String DB_USER_PASS = "密码";

	//DataSource接口实现类(C3P0提供)
	// 创建连接池对象
	private static ComboPooledDataSource dataSource = new ComboPooledDataSource();
	
	// 配置连接池(设置连接池参数)
	static {
		// 数据库连接参数
		dataSource.setJdbcUrl(JDBC_URL);
		dataSource.setUser(DB_USER_NAME);
		dataSource.setPassword(DB_USER_PASS);
		
		// 连接池参数
		dataSource.setInitialPoolSize(10); // 初始化连接数
        dataSource.setMaxPoolSize(100); // 最大连接数
        dataSource.setMinPoolSize(20); // 最小连接数
        dataSource.setMaxIdleTime(30); // 最大空闲时间(单位:秒)
        dataSource.setCheckoutTimeout(3000); // 等待时间(单位:毫秒)
        dataSource.setIdleConnectionTestPeriod(30); // 检查空闲连接的时间间隔(单位:秒)
	}
	
	// 获取数据库连接
	public static Connection getConnection() {
		// 从连接池中返回一个“空闲”连接
		try {
			Connection con = dataSource.getConnection();
			return con;
		} catch (SQLException e) {
			e.printStackTrace();
			return null;
		}
	}
}

DBUtils3:默认在src目录下读取名称为c3p0-config.xml的配置文件

package com.fulian.demo04;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import com.mchange.v2.c3p0.ComboPooledDataSource;

// 基于数据库连接池技术的工具类
// C3P0
public class DBUtils3 {

	//DataSource接口实现类(C3P0提供)
	// 创建连接池对象
	private static ComboPooledDataSource dataSource = new ComboPooledDataSource();
	
	// 获取数据库连接
	public static Connection getConnection() {
		// 从连接池中返回一个“空闲”连接
		try {
			Connection con = dataSource.getConnection();
			return con;
		} catch (SQLException e) {
			e.printStackTrace();
			return null;
		}
	}
}
配置文件


	
	
		
		com.mysql.cj.jdbc.Driver
		jdbc:mysql://localhost:3306/数据库名称?charset=utf8mb4&useSSL=false&useTimezone=true&serverTimezone=GMT%2B8
		用户名
		密码
		
		
		30000
		
		
		30
		
		
		10
		
		
		30
		
		
		100
		
		
		10
	
	

你可能感兴趣的:(java,开发语言,mysql,数据库)