我是靠谱客的博主 从容草莓,最近开发中收集的这篇文章主要介绍JDBC1. 获取数据库连接2. 操作和访问数据库3. 批量操作4. 数据库事务5. DAO6. 数据库连接池7. Apache-DBUtils CRUD,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

1. 获取数据库连接

lib 下添加数据库驱动。
右键【Build Path】⇒【Add to Build Path】
例:mysql-connector-java-8.0.23.jar

    @Test
	public void connectionTest01() throws SQLException {
		// 获取Driver 实现类对象
		Driver driver = new com.mysql.jdbc.Driver();
		String url = "jdbc:mysql://localhost:3306/book";
		Properties info = new Properties();
		info.setProperty("user", "root");
		info.setProperty("password", "tiger");
		Connection conn = driver.connect(url, info);
		System.out.println(conn);
	}
	// 程序中没有第三方api(new com.mysql.jdbc.Driver()),使程序具有更好的可移植性
	@Test
	public void connectionTest02() throws Exception {
		// 获取Driver 实现类对象,使用反射
		Class clazz = Class.forName("com.mysql.jdbc.Driver");
		Driver driver = (Driver)clazz.newInstance();

		String url = "jdbc:mysql://localhost:3306/book";
		Properties info = new Properties();
		info.setProperty("user", "root");
		info.setProperty("password", "tiger");
		Connection conn = driver.connect(url, info);
		System.out.println(conn);
	}
	@Test
	public void connectionTest03() throws Exception {
		// 获取Driver 实现类对象,使用反射
		Class clazz = Class.forName("com.mysql.jdbc.Driver");
		Driver driver = (Driver)clazz.newInstance();
		// 注册驱动
		DriverManager.registerDriver(driver);
		// 获取连接
		String url = "jdbc:mysql://localhost:3306/book";
		Connection conn = DriverManager.getConnection(url,"root","tiger");
		System.out.println(conn);
	}
	@Test
	public void connectionTest04() throws Exception {
		// 加载驱动,mysql 的Driver 实现类进行了驱动的注册
		Class.forName("com.mysql.jdbc.Driver");
		// 获取连接
		String url = "jdbc:mysql://localhost:3306/book";
		Connection conn = DriverManager.getConnection(url,"root","tiger");
		System.out.println(conn);
	}
	@Test
	public void connectionTest05() throws Exception {
		// 读取配置文件
		InputStream inStream = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
		// 从流中加载数据
		Properties properties = new Properties();
		properties.load(inStream);

		String username = properties.getProperty("username");
		String password = properties.getProperty("password");
		String url = properties.getProperty("url");
		String driverClassName = properties.getProperty("driverClassName");
		// 加载驱动
		Class.forName(driverClassName);
		// 获取连接
		Connection conn = DriverManager.getConnection(url,username,password);
		System.out.println(conn);
	}

jdbc.properties

username=root
password=tiger
url=jdbc:mysql://localhost:3306/book
driverClassName=com.mysql.jdbc.Driver

2. 操作和访问数据库

2.1 Statement SQL 注入问题

用户名:1’ or
密码: =1 or ‘1’ = '1

String sql = "SELECT USER,PASSWORD FROM USER WHERE USER = '"+user+"'" AND PASSWORD = '"+password+"'";
SELECT USER,PASSWORD FROM USER WHERE USER = '1' AND PASSWORD = '=1 or '1' = '1'

使用PrepareStatement() 可以避免该问题。

2.2 PrepareStatement

PrepareStatement 是Statement 的子接口,预编译SQL 语句。

2.2.1 添加

	// 添加
	@Test
	public void testInsert() {
		// 获取连接
		Connection conn = null;
		PreparedStatement ps = null;
		try {
			// 读取配置文件
			InputStream inStream = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
			// 从流中加载数据
			Properties properties = new Properties();
			properties.load(inStream);
			String username = properties.getProperty("username");
			String password = properties.getProperty("password");
			String url = properties.getProperty("url");
			String driverClassName = properties.getProperty("driverClassName");
			// 加载驱动
			Class.forName(driverClassName);
			// 获取连接
			conn = DriverManager.getConnection(url,username,password);
			// 预编译SQL语句
			String sql = "INSERT INTO t_user(name,pwd,email) VALUES(?,?,?)";
			ps = conn.prepareStatement(sql);
			ps.setString(1, "chengyu");
			ps.setString(2, "123456");
			ps.setString(3, "111qq.com");

			ps.execute();
		} catch (Exception e) {
			e.printStackTrace();
		} finally{
			try {
				if(ps != null)
				ps.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			try {
				if(conn != null)
					conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

2.2.2 更新

创建数据库连接、关闭的工具类

JDBCUtils.java:抽取数据库连接部分工具类,主要负责数据库的连接及关闭操作。

public class JDBCUtils {
	public static Connection getConnection() throws Exception {
		// 读取配置文件
		InputStream inStream = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
		// 从流中加载数据
		Properties properties = new Properties();
		properties.load(inStream);
		String username = properties.getProperty("username");
		String password = properties.getProperty("password");
		String url = properties.getProperty("url");
		String driverClassName = properties.getProperty("driverClassName");
		// 加载驱动
		Class.forName(driverClassName);
		// 获取连接
		Connection conn = DriverManager.getConnection(url,username,password);
		return conn;
	}

	public static void closeResource(Connection conn, Statement ps) {
		try {
			if(ps != null)
			ps.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		try {
			if(conn != null)
				conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}

更新操作:

	@Test
	public void testUpdate()  {
		Connection conn = null;
		PreparedStatement ps = null;
		try {
			conn = JDBCUtils.getConnection();
			String sql = "update t_user set name = ? where id = ?";
			ps = conn.prepareStatement(sql);
			ps.setString(1, "chengwei");
			ps.setString(2, "1006");
			ps.execute();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(conn, ps);
		}
	}

2.2.3 删除

抽取增删改共通方法:

	public void update(String sql,Object ...obj) {
		Connection conn = null;
		PreparedStatement ps = null;
		try {
			conn = JDBCUtils.getConnection();
			ps = conn.prepareStatement(sql);
			for(int i = 0; i < obj.length ; i++) {
				ps.setObject(i + 1, obj[i]);
			}
			ps.execute();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(conn, ps);
		}
	}
	@Test
	public void testDelete() {
		String sql = "delete from t_user where id = ?";
		update(sql,1007);
	}

2.2.4 查询

2.2.4.1 查询指定表所有列

public class UserForQuery {
	@Test
	public void testQuery1() {
		Connection conn = null;
		PreparedStatement ps= null;
		ResultSet rs = null;
		try {
			conn = JDBCUtils.getConnection();
			String sql = "select * from t_user where id = ? ";
			ps = conn.prepareStatement(sql);
			ps.setObject(1, 1001);
			// 返回结果集
			rs = ps.executeQuery();
			// 处理结果集
			while(rs.next()) {// 是否有下一条数据
				int id = rs.getInt(1);
				String name = rs.getString(2);
				String pwd = rs.getString(3);
				String email = rs.getString(4);

				User user = new User(id,name,pwd,email);
				System.out.println(user);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(conn, ps, rs);
		}
	}
}

JDBCUtils 类中追加ResultSet 的关闭操作:

try {
	if(rs != null)
		rs.close();
} catch (SQLException e) {
	e.printStackTrace();
}

2.2.4.2 查询指定表任意列

	@Test
    public void testQueryForUser() {
    	String sql = "select id,name from  t_user where id = ?";
    	User user = queryForUser( sql,1001 );
    	System.out.println(user);
    }
	// User 表的共同方法
	public User queryForUser(String sql,Object ...obj ) {
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			conn = JDBCUtils.getConnection();
			ps = conn.prepareStatement(sql);
			for(int i = 0; i < obj.length ; i++) {
				ps.setObject(i + 1, obj[i]);
			}
			rs = ps.executeQuery();
			// 获取结果集的元数据
			ResultSetMetaData rsmd = rs.getMetaData();
			// 获取结果集列数
			int columnCount = rsmd.getColumnCount();
			while(rs.next()) {
				User user = new User();
				for(int i = 0; i < columnCount; i++) {
						Object vaule = rs.getObject(i+1);
						String columnName = rsmd.getColumnName(i + 1);
						// 获取列名
						Field field = User.class.getDeclaredField(columnName);
						field.setAccessible(true);
						field.set(user, vaule);
				}
				return user;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}  finally {
			JDBCUtils.closeResource(conn, ps, rs);
		}
		return null;
	}

2.2.4.3 查询任意表(返回一条)

	@Test
	public void testGetInstance() {
		String sql = "SELECT * from t_user  where id = ?";
		User user = getInstance(User.class, sql, 1002);
		System.out.println(user);
	}
	
	public <T> T getInstance(Class<T> clazz,String sql,Object ...obj) {
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			conn = JDBCUtils.getConnection();
			ps = conn.prepareStatement(sql);
			for(int i = 0; i < obj.length ; i++) {
				ps.setObject(i + 1, obj[i]);
			}
			rs = ps.executeQuery();
			// 获取结果集的元数据
			ResultSetMetaData rsmd = rs.getMetaData();
			// 获取结果集列数
			int columnCount = rsmd.getColumnCount();
			while(rs.next()) {
				T t = clazz.newInstance();
				for(int i = 0;i<columnCount;i++) {
						Object vaule = rs.getObject(i+1);
						String columnName = rsmd.getColumnName(i + 1);
						// 获取列名
						Field field = clazz.getDeclaredField(columnName);
						field.setAccessible(true);
						field.set(t, vaule);
				}
				return t;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}  finally {
			JDBCUtils.closeResource(conn, ps, rs);
		}
		return null;
	}

2.2.4.4 查询任意表(返回多条)

	@Test
	public void getListTest() {
		String sql = "SELECT * from t_user  where id > ?";
		List<User> userList = getList(User.class, sql, 1002);
		userList.forEach(System.out::println);
	}

	public <T> List<T> getList(Class<T> clazz,String sql,Object ...obj) {
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			conn = JDBCUtils.getConnection();
			ps = conn.prepareStatement(sql);
			for(int i = 0; i < obj.length ; i++) {
				ps.setObject(i + 1, obj[i]);
			}
			rs = ps.executeQuery();
			// 获取结果集的元数据
			ResultSetMetaData rsmd = rs.getMetaData();
			// 获取结果集列数
			int columnCount = rsmd.getColumnCount();
			List<T> list = new ArrayList<T>();
			while(rs.next()) {
				T t = clazz.newInstance();
				for(int i = 0;i<columnCount;i++) {
						Object vaule = rs.getObject(i+1);
						String columnName = rsmd.getColumnName(i + 1);
						// 获取列名
						Field field = clazz.getDeclaredField(columnName);
						field.setAccessible(true);
						field.set(t, vaule);
				}
				list.add(t);
			}
			return list;
		} catch (Exception e) {
			e.printStackTrace();
		}  finally {
			JDBCUtils.closeResource(conn, ps, rs);
		}
		return null;
	}

2.2.5 Blob 类型字段

2.2.5.1 Blob 类型

(MySQL)

类型大小(单位:字节)
TinyBlob最大 255
Blob最大 65K
MediumBlob最大 16M
LongBlob最大 4G

注意:如果存储的文件过大,数据库的性能会下降。

2.2.5.2 插入

	public void inserBlobTest() throws Exception {
		Connection conn = null;
		PreparedStatement ps = null;
		try {
			conn = JDBCUtils.getConnection();
			String sql = "insert into t_user(name,pwd,email,photo) values(?,?,?,?)";
			ps = conn.prepareStatement(sql);
			ps.setObject(1, "chengyu");
			ps.setObject(2, "123456");
			ps.setObject(3, "aa@qq.com");

			FileInputStream is = new FileInputStream(new File("1.jpg"));
			ps.setBlob(4, is);

			ps.execute();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(conn, ps);
		}
	}

2.2.5.3 查看

	@Test
	public void queryBlobTest() throws Exception {
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		InputStream is  = null;
		FileOutputStream fos = null;

		try {
			conn = JDBCUtils.getConnection();
			String sql = "select id,name,photo from t_user where id = ?";
			ps = conn.prepareStatement(sql);
			ps.setObject(1, 1014);

			rs = ps.executeQuery();
			while(rs.next()) {
				int id = rs.getInt(1);
				String name = rs.getString("name");
				Blob photo = rs.getBlob("photo");
				is = photo.getBinaryStream();
				fos = new FileOutputStream("2.jpg");
				byte[] buffer = new byte[1024];
				int len;
				while((len = is.read(buffer)) != -1) {
					fos.write(buffer,0,len);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(conn, ps,rs);
			if(is != null) {
				try {
					is.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			if(fos != null) {
				try {
					fos.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}

3. 批量操作

3.1 Statement

Statement 语句因为数据内容不一样,每次都要进行编译、语法、语义检查、翻译成二进制命令、缓存等操作,效率低。

@Test
	public void insertTest01() {
		Connection conn = null;
		Statement ps = null;
		try {
			long start = System.currentTimeMillis();
			conn = JDBCUtils.getConnection();

			ps = conn.createStatement();
			for(int i = 1 ; i <= 20000 ; i++) {
				String sql = "INSERT INTO goods(name) VALUES('name_"+i +"')";
				ps.execute(sql);
			}
			long end = System.currentTimeMillis();
			System.out.println("花费的时间为:" + (end - start));
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(conn, ps);
		}
	}

548.384s

3.2 PreparedStatement

数据库会对预编译语句提供性能优化。因为可能被重复调用,所以编译后的执行代码会被缓存下来,下次调用时不需要编译,只要将参数直接传入编译过的语句执行代码即可。

	@Test
	public void insertTest02() {
		Connection conn = null;
		PreparedStatement ps = null;
		try {
			long start = System.currentTimeMillis();
			conn = JDBCUtils.getConnection();
			String sql = "INSERT INTO goods(name) VALUES(?)";
			ps = conn.prepareStatement(sql);
			for(int i = 1 ; i <= 20000 ; i++) {
				ps.setObject(1, "name_"+i);
				ps.execute();
			}
			long end = System.currentTimeMillis();
			System.out.println("花费的时间为:" + (end - start));
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(conn, ps);
		}
	}

553.783s

注意:
由于每条数据的插入都要与数据库进行一次交互(ps.execute()),此处仍然可以优化。详细做法参考 3.3。

3.3 PreparedStatement + Batch

mysql 数据库默认不支持batch 处理,需要开启:
(驱动要使用5.1.37 以后的版本)

url=jdbc:mysql://localhost:3306/book?rewriteBatchedStatements=true
	@Test
	public void insertTest03() {
		Connection conn = null;
		PreparedStatement ps = null;
		try {
			long start = System.currentTimeMillis();
			conn = JDBCUtils.getConnection();
			String sql = "INSERT INTO goods(name) VALUES(?)";
			ps = conn.prepareStatement(sql);
			for(int i = 1 ; i <= 20000 ; i++) {
				ps.setObject(1, "name_"+i);
				// 1. 攒SQL
				ps.addBatch();
				if(i % 500 == 0) {
					// 2. 执行
					ps.executeBatch();
					// 3. 清空batch
					ps.clearBatch();
				}
			}
			long end = System.currentTimeMillis();
			System.out.println("花费的时间为:" + (end - start));
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(conn, ps);
		}
	}

2.225s

一百万条数据执行时间为:30s

3.4 PreparedStatement + Batch + 不自动提交

设置不允许自动提交数据。

	@Test
	public void insertTest04() {
		Connection conn = null;
		PreparedStatement ps = null;
		try {
			long start = System.currentTimeMillis();
			conn = JDBCUtils.getConnection();
			// 设置不允许自动提交数据
			conn.setAutoCommit(false);

			String sql = "INSERT INTO goods(name) VALUES(?)";
			ps = conn.prepareStatement(sql);
			for(int i = 1 ; i <= 1000000 ; i++) {
				ps.setObject(1, "name_"+i);
				// 1. 攒SQL
				ps.addBatch();
				if(i % 500 == 0) {
					// 2. 执行
					ps.executeBatch();
					// 3. 清空batch
					ps.clearBatch();
				}
			}
			// 提交数据
			conn.commit();

			long end = System.currentTimeMillis();
			System.out.println("花费的时间为:" + (end - start));
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(conn, ps);
		}
	}

一百万条数据执行时间为:14.4s

4. 数据库事务

对一组数据单元的操作,成功则都成功,失败则都失败。

  • DML 默认情况下,一旦执行就会自动提交,通过 set autocommit = false 的方式可以取消自动提交;
    注意:即使在false 的状态下,连接关闭时也会自动提交。
  • DDL 就是自动提交,无法取消。

4.1 代码实现

// 用户转账
@Test
public void testUpdate() {
	Connection conn = null ;
	try {
		conn = JDBCUtils.getConnection();
		// 取消自动提交
		conn.setAutoCommit(false);
		String sql1 = " update account set balance = balance - 100 where name = ?";
		update(conn, sql1, "chengyu");
		// 制造异常
		// int a = 10 / 0;
		String sql2 = "  update account set balance = balance + 100 where name = ?";
		update(conn, sql2, "chengwei");
		System.out.println("转账成功!");
		// 事物提交
		conn.commit();
	} catch (Exception e) {
		e.printStackTrace();
		// 回滚
		try {
			System.out.println("转账失败!");
			conn.rollback();
		} catch (SQLException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
	} finally {
		JDBCUtils.closeResource(conn, null);
	}
}
// 通用增删改
public int update(Connection conn, String sql, Object ...obj) {
	PreparedStatement ps = null;
	try {
		ps = conn.prepareStatement(sql);
		for(int i = 0; i < obj.length ; i++) {
			ps.setObject(i + 1, obj[i]);
		}
		return ps.executeUpdate(); // 返回影响几条数据
	} catch (Exception e) {
		e.printStackTrace();
	} finally {
		JDBCUtils.closeResource(null, ps);
	}
	return 0;
}

4.2 事务的ACID 属性

4.2.1 原子性(Atomicity)

事务是一个不可分割的工作单位,操作要么都发生,要么都不发生。

4.2.2 一致性(Consistency)

事务必须使数据库从一个一致性状态变换到另一个一致性状态。

4.2.3 隔离性(Isolation)

一个事物的执行不能被其他事物干扰,即一个事物内部的操作及使用的数据对并发的其他事物是隔离的,并发执行的各个事物之间不能互相干扰。

4.2.4 持久性(Durability)

一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据故障不应该对其有任何影响。

4.3 数据库的并发

4.3.1 并发的问题

4.3.1.1 脏读

两个事务T1、T2,T1 读取了已经被T2 更新但还没有提交的字段,之后,若T2 回滚,T1 读取的内容就是临时且无效的;

4.3.1.2 不可重复读取

两个事务T1、T2,T1 读取了一个字段,然后T2 更新了该字段,之后,T1 再次读取同一个字段,值就不同了。

4.3.1.3 幻读

两个事务T1、T2,T1 从一个表中读取了一个字段,然后T2 在该表中插入了一些新的行,之后,如果T1 再次取同一个表,就会多出几行。

4.3.2 四种隔离级别

针对4.3 的问题,设置的隔离级别。

隔离级别名称MySqlOracle脏读不重读幻读描述
READ UNCOMMITED读未提交数据允许事务读取未被其他事务提交的数据
READ COMMITED读已提交数据○(默认)只允许事务读取已被其它事务提交的数据
REPEATABLE READ可重复读○(默认)一个事务多次读取相同值,这个事务持续期间,禁止其他事务对这个字段变更
SERIALIZABLE串行化事务可从一个表中读取相同的行,这个事务持续期间,禁止其他事务对该表进行插入、更新、删除操作,性能低

4.3.3 命令行设置隔离级别

取消自动提交

set autocommit = false;

查看隔离级别(8.0版本后)

select @@transaction_isolation;

修改隔离级别

set global transaction isolation level read committed;

需要退出,重新登陆。

4.3.4 代码设置隔离级别

取消自动提交

conn.setAutoCommit(false);

查看隔离级别

conn.getTransactionIsolation()

修改隔离级别

conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);

数据库重启之后恢复默认。

5. DAO

DAO(Database access object):数据库访问对象。

5.1 BaseDao

// 封装通用增删改查(含事物)
public abstract class BaseDao {
	// 通用增删改
	public int update(Connection conn, String sql, Object... obj) {
		PreparedStatement ps = null;
		try {
			ps = conn.prepareStatement(sql);
			for (int i = 0; i < obj.length; i++) {
				ps.setObject(i + 1, obj[i]);
			}
			return ps.executeUpdate(); // 返回影响几条数据
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(null, ps);
		}
		return 0;
	}

	// 通用查询一条(含事务)
	public <T> T getInstance(Connection conn, Class<T> clazz, String sql, Object... obj) {
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			ps = conn.prepareStatement(sql);
			for (int i = 0; i < obj.length; i++) {
				ps.setObject(i + 1, obj[i]);
			}
			rs = ps.executeQuery();
			// 获取结果集的元数据
			ResultSetMetaData rsmd = rs.getMetaData();
			// 获取结果集列数
			int columnCount = rsmd.getColumnCount();
			while (rs.next()) {
				T t = clazz.newInstance();
				for (int i = 0; i < columnCount; i++) {
					Object vaule = rs.getObject(i + 1);
					String columnName = rsmd.getColumnName(i + 1);
					// 获取列名
					Field field = clazz.getDeclaredField(columnName);
					field.setAccessible(true);
					field.set(t, vaule);
				}
				return t;
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(null, ps, rs);
		}
		return null;
	}
	// 通用查询多条(含事务)
	public <T> List<T> getList(Connection conn, Class<T> clazz, String sql, Object... obj) {
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			ps = conn.prepareStatement(sql);
			for (int i = 0; i < obj.length; i++) {
				ps.setObject(i + 1, obj[i]);
			}
			rs = ps.executeQuery();
			// 获取结果集的元数据
			ResultSetMetaData rsmd = rs.getMetaData();
			// 获取结果集列数
			int columnCount = rsmd.getColumnCount();
			List<T> list = new ArrayList<T>();
			while (rs.next()) {
				T t = clazz.newInstance();
				for (int i = 0; i < columnCount; i++) {
					Object vaule = rs.getObject(i + 1);
					String columnName = rsmd.getColumnName(i + 1);
					// 获取列名
					Field field = clazz.getDeclaredField(columnName);
					field.setAccessible(true);
					field.set(t, vaule);
				}
				list.add(t);
			}
			return list;
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(null, ps, rs);
		}
		return null;
	}
	// 获取特殊值
	public <T> T getValue(Connection conn,String sql, Object ...args) {
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			ps = conn.prepareStatement(sql);
			// 填充占位符
			for(int i = 0; i < args.length; i++) {
				ps.setObject(i + 1, args[i]);
			}
			rs = ps.executeQuery();
			if(rs.next()) {
				return (T)rs.getObject(1);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(null, ps, rs);
		}
		return null;
	}
}

5.2 AccountDao

public interface AccountDao {
	void insert(Connection conn,Account acc);
	void deleteById(Connection conn,int id);
	void update(Connection conn, int id, Account acc);
	Account getAccountById(Connection conn, int id);
	List<Account> getAll(Connection conn);
	Long getCount(Connection conn);
	BigDecimal getMaxBalance(Connection conn);
}

5.3 AccountDaoImpl

public class AccountDaoImpl extends BaseDao implements AccountDao {
	@Override
	public void insert(Connection conn, Account acc) {
		String sql = "insert into account(name,balance) values(?,?)";
		update(conn,sql,acc.getName(),acc.getBalance());
	}
	@Override
	public void deleteById(Connection conn, int id) {
		String sql = "delete from account where id = ?";
		update(conn,sql,id);
	}
	@Override
	public void update(Connection conn, int id, Account acc) {
		String sql = "update account set name = ?,balance = ? where id = ?";
		update(conn,sql,acc.getName(),acc.getBalance(),acc.getId());
	}
	@Override
	public Account getAccountById(Connection conn, int id) {
		String sql = "select * from account  where id = ?";

		return getInstance(conn,Account.class,sql,id);
	}
	@Override
	public List<Account> getAll(Connection conn) {
		String sql = "select * from account ";
		return getList(conn,Account.class,sql);
	}
	@Override
	public Long getCount(Connection conn) {
		String sql = " select count(*) from account ";
		return getValue(conn,sql);
	}
	@Override
	public BigDecimal getMaxBalance(Connection conn) {
		String sql = " select max(balance) from account ";
		return getValue(conn,sql);
	}
}

5.4 AccountDaoImplTest

public class AccountDaoImplTest {
	private AccountDaoImpl dao = new AccountDaoImpl();
	@Test
	public void testInsert() {
		Connection conn = null;
		try {
			conn = JDBCUtils.getConnection();
			Account acc = new Account(0 , "chenglong", new BigDecimal(500));
			dao.insert(conn, acc);
			System.out.println("添加成功!");
		} catch (Exception e) {
			e.printStackTrace();
		} finally{
			JDBCUtils.closeResource(conn, null);
		}
	}
	@Test
	public void testDeleteById() {
		Connection conn = null;
		try {
			conn = JDBCUtils.getConnection();
			dao.deleteById(conn, 1004);

			System.out.println("删除成功!");
		} catch (Exception e) {
			e.printStackTrace();
		} finally{
			JDBCUtils.closeResource(conn, null);
		}
	}
	@Test
	public void testUpdateConnectionIntAccount() {
		Connection conn = null;
		try {
			conn = JDBCUtils.getConnection();
			Account acc = new Account(1003,"chengyu",new BigDecimal(1500));
			dao.update(conn, 1003, acc);

			System.out.println("修改成功!");
		} catch (Exception e) {
			e.printStackTrace();
		} finally{
			JDBCUtils.closeResource(conn, null);
		}
	}
	@Test
	public void testGetAccountById() {
		Connection conn = null;
		try {
			conn = JDBCUtils.getConnection();
			Account acc = dao.getAccountById(conn, 1003);

			System.out.println(acc);
		} catch (Exception e) {
			e.printStackTrace();
		} finally{
			JDBCUtils.closeResource(conn, null);
		}
	}
	@Test
	public void testGetAll() {
		Connection conn = null;
		try {
			conn = JDBCUtils.getConnection();
			List<Account> list = dao.getAll(conn);
			list.forEach(System.out::println);

		} catch (Exception e) {
			e.printStackTrace();
		} finally{
			JDBCUtils.closeResource(conn, null);
		}
	}
	@Test
	public void testGetCount() {
		Connection conn = null;
		try {
			conn = JDBCUtils.getConnection();
			Long count = dao.getCount(conn);

			System.out.println("记录数为:" + count);

		} catch (Exception e) {
			e.printStackTrace();
		} finally{
			JDBCUtils.closeResource(conn, null);
		}
	}
	@Test
	public void testGetMaxBalance() {
		Connection conn = null;
		try {
			conn = JDBCUtils.getConnection();
			BigDecimal maxBalance = dao.getMaxBalance(conn);

			System.out.println("最大余额为:" + maxBalance);
		} catch (Exception e) {
			e.printStackTrace();
		} finally{
			JDBCUtils.closeResource(conn, null);
		}
	}
}

6. 数据库连接池

6.1 普通JDBC 问题

1)普通的数据库连接,每次都要将Connection 加载到内存,在验证用户名密码。执行完还需要将连接移除。数据库连接资源并没有得到很好的重复利用。
若同时有几百人甚至几千人在线,频繁的进行数据库连接操作,将占用很多系统资源,严重时甚至会造成服务器崩溃。
2)对于每次数据库连接,使用完后都得断开。否则,当程序出现异常而未能关闭,将会导致数据库系统中的内存泄露,最终将导致重启数据库。
3)不能控制被创建的连接对象数,系统资源会被毫无顾忌的分配出去,如果连接过多,超出符合,也可能导致内存泄漏,服务器崩溃。

6.2 数据库连接池基本思想

为数据库连接建立一个“缓冲池”,预先在缓冲池中放入一定数量的连接,当需要建立数据库连接的时候,只需从缓冲池中取出一个,使用完毕放回即可。

6.3 开源数据库连接池

6.3.1 C3P0

稳定性可以,速度相对慢。

导入下记jar 包:
c3p0-0.9.5.5.jar
mchange-commons-java-0.2.19.jar
mysql-connector-java-8.0.23.jar

6.3.1.1 方式一

	@Test
	public void getConnectionTest() throws Exception {
		// 获取C3P0 连接池
		ComboPooledDataSource cpds = new ComboPooledDataSource();
		cpds.setDriverClass( "com.mysql.jdbc.Driver" );
		cpds.setJdbcUrl( "jdbc:mysql://localhost:3306/book" );
		cpds.setUser("root");
		cpds.setPassword("tiger");
		// 初始连接数
		cpds.setInitialPoolSize(10);

		Connection conn = cpds.getConnection();
		System.out.println(conn);
	}

参数设定:
https://www.mchange.com/projects/c3p0/#configuration_properties

6.3.1.2 方式二

src 下创建c3p0-config.xml 文件

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <named-config name="helloc3p0">
    	<!-- 提供获取连接的基本信息 -->
	    <property name="driverClass">com.mysql.jdbc.Driver</property>
	    <property name="jdbcUrl">jdbc:mysql://localhost:3306/book</property>
	    <property name="user">root</property>
	    <property name="password">tiger</property>
		<!-- 数据库连接池管理的基本信息 -->
		<!-- 当连接数不够时,c3p0一次性向数据库服务器申请的连接数 -->
		<property name="acquireIncrement">5</property>
		<!-- 初始化连接数 -->
      	<property name="initialPoolSize">10</property>
      	<!-- 最小连接数 -->
      	<property name="minPoolSize">10</property>
      	<!--  最大连接数 -->
     	<property name="maxPoolSize">100</property>
      	<!--  最多维护的Statements 的个数 -->
	    <property name="maxStatements">50</property>
	    <!-- 每个连接中可以最多使用的Statements 的个数 -->
	    <property name="maxStatementsPerConnection">5</property>
  </named-config>
</c3p0-config>

JDBCUtils.java:

	// 连接池提供一个即可
	private static ComboPooledDataSource cpds = new ComboPooledDataSource("helloc3p0");
	
	public static Connection getConnection() throws SQLException {
		return cpds.getConnection();
	}

6.3.2 DBCP

tomcat 自带数据库连接池技术,速度快,不稳定。

导入下记jar 包:
mysql-connector-java-8.0.23.jar
commons-dbcp2-2.8.0.jar
commons-logging-1.2.jar
commons-pool2-2.9.0.jar

6.3.2.1 方式一

	@Test
	public void getConnectionTest() throws SQLException {
		// 创建DBCP 的数据库连接池
		BasicDataSource source = new BasicDataSource();
		// 设置基本信息
		source.setDriverClassName("com.mysql.jdbc.Driver");
		source.setUrl("jdbc:mysql://localhost:3306/book");
		source.setUsername("root");
		source.setPassword("tiger");
		// 连接池管理相关信息
		source.setInitialSize(10);

		Connection conn = source.getConnection();
		System.out.println(conn);
	}
}

6.3.2.1 方式二

src 下创建 dbcp.properties

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/book
username=root
password=tiger

initialSize=10
	@Test
	public void getConnectionTest1() throws Exception {
		Properties pros = new Properties();
		// 方式1-类的加载器
		// InputStream is = ClassLoader.getSystemClassLoader().getSystemResourceAsStream("dbcp.properties");
		// 方式2-
		FileInputStream is = new FileInputStream(new File("src/dbcp.properties"));
		pros.load(is);
		// 创建DBCP连接池
		DataSource source = BasicDataSourceFactory.createDataSource(pros);
		Connection conn = source.getConnection();
		System.out.println(conn);
	}

JDBCUtils.java:

// 声明数据库连接池
	private static DataSource source;
	static {
		try {
			Properties pros = new Properties();
			FileInputStream is = new FileInputStream(new File("src/dbcp.properties"));
			pros.load(is);
			// 创建DBCP连接池
			source = BasicDataSourceFactory.createDataSource(pros);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static Connection getConnection2() throws Exception {
		return source.getConnection();
	}

6.3.3 Druid “德鲁伊”

阿里提供的数据库连接池。兼顾稳定与效率。

导入下记jar 包:
druid-1.1.22.jar
mysql-connector-java-8.0.23.jar

public class DruidTest {
	@Test
	public void getConnection() throws Exception {
		Properties pros = new Properties();
		FileInputStream is = new FileInputStream(new File("src/druid.properties"));
		pros.load(is);
		DataSource source = DruidDataSourceFactory.createDataSource(pros);
		Connection conn = source.getConnection();
		System.out.println(conn);
	}
}

JDBCUtils.java:

	// 获取Druid连接池
	private static DataSource druidSource;
	static {
		try {
			Properties pros = new Properties();
			FileInputStream is = new FileInputStream(new File("src/druid.properties"));
			pros.load(is);
			druidSource = DruidDataSourceFactory.createDataSource(pros);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static Connection getConnection3() throws Exception{
		return druidSource.getConnection();
	}

7. Apache-DBUtils CRUD

是Apache 组织提供的一个开源JDBC 工具类库,封装了增删改查操作。

导入下记jar 包:
commons-dbutils-1.7.jar
druid-1.1.22.jar
mysql-connector-java-8.0.23.jar

druid.properties

url=jdbc:mysql://localhost:3306/book
username=root
password=tiger
driverClassName=com.mysql.jdbc.Driver

initialSize=10
maxActive=10

JDBCUtils.java

public class JDBCUtils {
	// 获取Druid连接池
	private static DataSource source;
	static {
		try {
			Properties pros = new Properties();
			FileInputStream is = new FileInputStream(new File("src/druid.properties"));
			pros.load(is);
			source = DruidDataSourceFactory.createDataSource(pros);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static Connection getConnection() throws Exception{
		return source.getConnection();
	}

	public static void closeResource(Connection conn, Statement ps) {
		DbUtils.closeQuietly(conn);
		DbUtils.closeQuietly(ps);
	}

	public static void closeResource(Connection conn, Statement ps,ResultSet rs) {
		DbUtils.closeQuietly(conn);
		DbUtils.closeQuietly(ps);
		DbUtils.closeQuietly(rs);
	}
}

QueryRunnerTest.java

public class QueryRunnerTest {

	// 增删改
	@Test
	public void insertTest() throws Exception {
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection();
			String sql = "insert into account(name,balance) values(?,?)";
			int insertCount = runner.update(conn, sql, "chenggong","1500");
			System.out.println("影响了几条数据:" + insertCount);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(conn, null);
		}
	}
	// 查询一项目
	@Test
	public void queryItemTest() throws Exception {
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection();
			String sql = "select count(*) from account";

			ScalarHandler handler = new ScalarHandler();
			Long count = (Long) runner.query(conn, sql, handler);

			System.out.println("条数为:" + count);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(conn, null);
		}
	}
	// 查询一项目
	@Test
	public void queryMaxItemTest() throws Exception {
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection();
			String sql = "select max(balance) from account";

			ScalarHandler handler = new ScalarHandler();
			BigDecimal maxBalance = (BigDecimal) runner.query(conn, sql, handler);

			System.out.println("最大余额为:" + maxBalance);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(conn, null);
		}
	}
	// 查询一条 Bean
	@Test
	public void queryTest() {
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection();
			String sql = "select * from account  where id = ?";
			// BeanHandler:ResultSetHandler 接口的实现类,用于封装一条记录
			BeanHandler<Account> handler = new BeanHandler<>(Account.class);
			Account account = runner.query(conn, sql, handler, 1001);
			System.out.println("查询结果为:" + account);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(conn, null);
		}
	}
	// 查询多条 BeanList
	@Test
	public void queryListTest() throws Exception {
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection();
			String sql = "select * from account  where id > ?";
			BeanListHandler<Account> handler = new BeanListHandler<>(Account.class);
			List<Account> accList = runner.query(conn, sql, handler, 1001);
			accList.forEach(System.out::println);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(conn, null);
		}
	}
	// 查询一条Map
	@Test
	public void queryMapTest() throws Exception {
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection();
			String sql = "select * from account  where id = ?";
			MapHandler handler = new MapHandler();
			Map<String,Object> map = runner.query(conn, sql, handler, 1001);
			System.out.println(map);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(conn, null);
		}
	}
	// 查询一条MapList
	@Test
	public void queryMapListTest() throws Exception {
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection();
			String sql = "select * from account  where id > ?";
			MapListHandler handler = new MapListHandler();
			List<Map<String, Object>> list = runner.query(conn, sql, handler, 1001);
			list.forEach(System.out::println);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(conn, null);
		}
	}
	// 自定义ResultSetHandler 的实现类
	@Test
	public void queryDiyTest() throws Exception {
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection();
			String sql = "select * from account  where id = ?";

			ResultSetHandler<Account> handler = new ResultSetHandler<Account>() {
				@Override
				public Account handle(ResultSet rs) throws SQLException {
					if(rs.next()) {
						int id = rs.getInt("id");
						String name = rs.getString("name");
						BigDecimal balance = rs.getBigDecimal("balance");

						return new Account(id,name,balance);
					}
					return null;
				}
			};

			Account acc = runner.query(conn, sql, handler, 1001);
			System.out.println(acc);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(conn, null);
		}
	}
}

最后

以上就是从容草莓为你收集整理的JDBC1. 获取数据库连接2. 操作和访问数据库3. 批量操作4. 数据库事务5. DAO6. 数据库连接池7. Apache-DBUtils CRUD的全部内容,希望文章能够帮你解决JDBC1. 获取数据库连接2. 操作和访问数据库3. 批量操作4. 数据库事务5. DAO6. 数据库连接池7. Apache-DBUtils CRUD所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(63)

评论列表共有 0 条评论

立即
投稿
返回
顶部