1 JDBC基础
1.1 jdbc入门
以前我们操作数据库都是通过远程连接mysql的客户端工具来登录数据库,登录成功之后,我们在客户端工具上(sqlYog)编写好sql语句,然后再将sql语句发送给数据库服务器执行,执行完的结果保存在数据库服务器上。
但是在我们实际开发中,我们往往需要将数据库里面的数据拿到程序中来,此时我们会在java程序中编写好sql语句,然后再通过这段java程序将sql语句发送给数据库服务器,数据库服务器执行完sql语句之后,再将执行的结果返回给我们的程序。这种技术就是jdbc技术。
一句话总结jdbc:使用java程序发送sql语句的技术。
接下来我们就使用jdbc技术去连接数据库。
使用jdbc连接数据库的前提是:
- 数据库的ip地址
- 数据库的端口号
- 数据库名称
- 数据库用户名和密码
连接数据库,需要用到数据库驱动包。所以我们必须将数据库驱动包自行引入到程序中。
1.1.1 使用jdbc技术连接数据库
此时这个依赖还没有真正的引入到项目中去,我们还需要手动的将这个依赖导入到项目中。
右键jar包–>add as Libary
连接数据库的方式有三种:
第一种:
public class JdbcDemo1 {
//定义连接数据库的url http:// sftp:// jdbc:mysql://
String url = "jdbc:mysql://192.168.10.140:3306/day01";
//定义连接数据库的用户名 用户名必须叫user
String user = "root";
//定义连接数据库的密码
String password = "Admin2022!";
/**
* 连接数据库的第一种方式
* @throws Exception
*/
@Test
public void test01() throws Exception{
//创建数据库驱动
Driver driver = new com.mysql.jdbc.Driver();
Properties properties = new Properties();
properties.setProperty("user",user);
properties.setProperty("password",password);
//connect:通过驱动类来连接数据库服务器的方法
Connection conn = driver.connect(url, properties);
System.out.println(conn);
}
}
第二种:
/**
* 连接数据库的第二种方式
* @throws Exception
*/
@Test
public void test02() throws Exception{
Driver driver = new com.mysql.jdbc.Driver();
//注册驱动
DriverManager.registerDriver(driver);
//连接数据库
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
}
第三种(推荐使用这种):
/**
* 连接数据库的第三种方式 -- 推荐使用这种
* 通过反射的机制动态加载数据库驱动程序
* @throws Exception
*/
@Test
public void test03() throws Exception{
//通过反射的机制加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
//连接数据库
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
}
1.1.2 jdbc中的核心API
-
Driver接口 数据库驱动程序的接口,所有的数据库厂商都需要实现这个接口。
-
connect(String url, java.util.Properties info):连接数据库的方法。
url: 连接数据库的地址。
user:连接数据库需要用到的用户名。
password:连接数据库需要用到的密码。
-
-
DriverManager类 数据库驱动管理器类,用于管理所有注册的驱动程序。
- registerDriver(Driver driver):注册驱动的方法。
- getConnection(String url,String username,String password) :获取数据库连接对象的方法。
-
Connection接口:表示java程序连接数据库的连接对象。如果我们可以打印输出这个连接对象的内存地址,说明我们成功的连接上了数据库。
- createStatement(): 创建Statement对象,这个对象帮助我们执行sql语句。
- prepareStatement(String sql):创建PreparedStatement对象,用于预编译sql语句。
- prepareCall(String sql): 创建CallableStatement对象,用来执行存储过程语句。
-
Statement接口: 用于执行静态的sql语句。
- int executeUpdate(String sql): 主要用来执行更新的语句(DML DDL)
- ResultSet executeQuery(String sql): 主要用来执行DQL语句,返回ResultSet对象。
-
PreparedStatement接口:用于预编译sql语句
- executeUpdate() 预编译DDL DML语句
- executeQuery() 预编译DQL语句
-
CallableStatement接口:主要用来执行存储过程的语句
- executeQuery() 调用存储过程的方法
-
ResultSet 接口:用来处理(封装)查询结果集
- boolean next() 判断是否存在下一行数据
- getXX() 取出对应列的值
1.2 使用Statement对象执行sql语句
1.2.1 执行DDL语句
public class JdbcDemo2 {
//定义连接数据库的url
String url = "jdbc:mysql://192.168.10.140:3306/jdbc";
//定义连接数据库的用户名
String username = "root";
//定义连接数据库的密码
String password = "Admin2022!";
//执行DDL语句
@Test
public void test01() throws Exception{
//首先需要获取数据库连接
Class.forName("com.mysql.jdbc.Driver");
//通过驱动管理类获取数据库连接
Connection connection = DriverManager.getConnection(url, username, password);
//创建Statement对象,准备执行sql语句
Statement statement = connection.createStatement();
String sql = "create table student(id int primary key auto_increment,name varchar(20) not null,gender varchar(2))";
//执行sql语句 返回值是int 描述的影响的行数
int count = statement.executeUpdate(sql);
System.out.println(count);
//关闭资源
statement.close();
connection.close();
}
}
以上代码我们可以抽取出工具类。
public class JdbcUtils {
//定义连接数据库的url
static String url = "jdbc:mysql://192.168.10.140:3306/jdbc";
//定义连接数据库的用户名
static String username = "root";
//定义连接数据库的密码
static String password = "Admin2022!";
//获取数据库连接的方法
public static Connection getConnection() throws Exception{
Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection(url, username, password);
return connection;
}
}
1.2.2 执行DML语句
- 执行新增的语句
//执行DML语句-- insert
@Test
public void test02() throws Exception{
Connection connection = JdbcUtils.getConnection();
//创建Statement对象
Statement statement = connection.createStatement();
String sql = "insert into student(name,gender) values('eric','男')";
//执行sql语句
int result = statement.executeUpdate(sql);
System.out.println("影响的行数是:" + result);
//关闭资源
statement.close();
connection.close();
}
- 执行修改的语句
//执行DML语句-- update
@Test
public void test03() throws Exception{
Connection connection = JdbcUtils.getConnection();
Statement statement = connection.createStatement();
String name = "kobe";
int id = 1;
String sql = "update student set name='"+name+"' where id = "+id+"";
int count = statement.executeUpdate(sql);
System.out.println("修改成功,影响记录的行数是:" + count);
//关闭资源
statement.close();
connection.close();
}
- 执行删除掉 语句
//执行DML语句--删除
@Test
public void test04() throws Exception{
Connection connection = JdbcUtils.getConnection();
Statement statement = connection.createStatement();
int id = 2;
String sql = "delete from student where id = "+id+"";
//执行sql语句
int count = statement.executeUpdate(sql);
System.out.println("删除成功,影响记录的行数是:" + count);
//关闭资源
statement.close();
connection.close();
}
1.2.3 执行DQL语句
//执行DQL语句--查询
@Test
public void test05() throws Exception{
Connection connection = JdbcUtils.getConnection();
Statement statement = connection.createStatement();
String sql = "select * from student";
//执行sql语句
ResultSet rs = statement.executeQuery(sql);
// rs封装了查询结果集 next() 获取一个指针,指针默认指向第一条记录的上一个单位,
// 每次判断当前指针的下一个单位是否存在记录,如果存在就进入while循环,否则跳出循环。
while(rs.next()){
//getXX 根据字段的数据类型取对应字段的值。 可以根据字段的编号去获取值。编号从左到右 依次从1开始编号。
/*int id = rs.getInt(1);
String name = rs.getString(2);
String gender = rs.getString(3);*/
//按照字段的名称去取值
int id = rs.getInt("id");
String name = rs.getString("name");
String gender = rs.getString("gender");
System.out.println("id:" + id + " 姓名:" + name + " 性别:" + gender);
}
//关闭资源
rs.close();
statement.close();
connection.close();
}
使用Statement对象执行sql语句的问题:没有预编译sql语句,容易引起sql注入的风险。
什么是sql注入?
不检测sql语句的语法格式,通过sql语句语法的一些漏洞,直接请求数据库。
我们举个例子:
我们要进行登录,通过输入用户名和密码就可以登录,sql语句定义如下:
select * from user where username = 'admin' and password = 'admin';
但是我们可以定义如下的sql语句,一样可以成功访问数据库
select * from user where username = 'aa' and password = ' ' or 1=1 -- '
-- ': 由于--在mysql中是注释,可以判定1=1后面的内容不会执行。由此sql语句会等价于
select * from user where username = 'aa' and password = '' or 1=1
由于1=1 条件是恒成立的。所以这条sql语句就是绕过密码直接登录,只要用户名正确就可以。
我们需要避免这个问题。对应的解决方案就是在sql语句执行之前,要对sql语句进行预编译(执行sql语句之前,检查sql语句的语法格式是否符合规范,如果符合规范就编译通过,可以执行。如果编译不通过,就不会执行这条sql语句)。
1.3 使用PreparedStatement对象执行sql语句
public class JdbcDemo3 {
//执行新增的方法
@Test
public void test01() throws Exception{
//获取数据库连接
Connection connection = JdbcUtils.getConnection();
String sql = "insert into student(name,gender) values(?,?)";// ?指代的是参数的占位符
PreparedStatement pst = connection.prepareStatement(sql);//预编译sql语句,检查sql语句的格式是否符合规范
//设置参数
pst.setString(1,"curry");
pst.setString(2,"男");
//执行sql语句
int count = pst.executeUpdate();
System.out.println("影响记录的行数是:" + count);
//关闭资源
pst.close();
connection.close();
}
//执行修改的方法
@Test
public void test02() throws Exception{
//获取数据库连接
Connection connection = JdbcUtils.getConnection();
//准备sql语句
String sql = "update student set name = ? where id = ?";
//预编译sql语句
PreparedStatement pst = connection.prepareStatement(sql);
//设置参数
pst.setString(1,"库里");
pst.setInt(2,5);
//执行sql语句
int count = pst.executeUpdate();
System.out.println("修改成功,影响记录的行数是:" + count);
//关闭资源
pst.close();
connection.close();
}
//执行删除的方法
@Test
public void test03() throws Exception{
Connection connection = JdbcUtils.getConnection();
String sql = "delete from student where id = ?";
PreparedStatement pst = connection.prepareStatement(sql);
pst.setInt(1,5);
int count = pst.executeUpdate();
System.out.println("删除成功,影响记录的行数是:" + count);
pst.close();
connection.close();
}
//执行查询的方法
@Test
public void test04() throws Exception{
Connection connection = JdbcUtils.getConnection();
String sql = "select * from student where id = ?";
PreparedStatement pst = connection.prepareStatement(sql);
pst.setInt(1,1);
ResultSet rs = pst.executeQuery();
while(rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name");
String gender = rs.getString("gender");
System.out.println("编号:" + id + " 姓名:" + name + " 性别:" + gender);
}
rs.close();
pst.close();
connection.close();
}
}
1.4 使用CallableStatement执行存储过程
1.4.1 准备存储过程语句
-- 只带有输入参数的存储过程语句
DELIMITER $
CREATE PROCEDURE proc_findByGender(IN sex VARCHAR(2))
BEGIN
SELECT * FROM student WHERE gender = sex;
END $
DELIMITER;
-- 调用存储过程
CALL proc_findByGender('男');
-- 带有输入 输出参数的存储过程语句
DELIMITER $
CREATE PROCEDURE proc_findCount(IN sex VARCHAR(2),OUT total INT)
BEGIN
SELECT COUNT(*) INTO total FROM student WHERE gender = sex;
END $
DELIMITER;
-- 调用存储过程
CALL proc_findCount('男',@total);
-- 输出输出参数的结果
SELECT @total;
1.4.2 执行存储过程语句
//执行只有输入参数的存储过程语句
@Test
public void test05() throws Exception{
Connection connection = JdbcUtils.getConnection();
//准备存储过程语句
String sql = "CALL proc_findByGender(?)";
//预编译存储过程语句
CallableStatement cst = connection.prepareCall(sql);
//设置输入参数
cst.setString(1,"女");
//执行存储过程语句
ResultSet resultSet = cst.executeQuery();
while(resultSet.next()){
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
String gender = resultSet.getString("gender");
System.out.println("编号:" + id + " 姓名:" + name + " 性别:" + gender);
}
cst.close();
connection.close();
}
//执行存储过程 -- 带有输入 输出参数的存储过程
@Test
public void test06() throws Exception{
Connection connection = JdbcUtils.getConnection();
//准备存储过程语句
String sql = "CALL proc_findCount(?,?)";
//预编译存储过程,prepareCall 不是 prepareStatement语句
CallableStatement cst = connection.prepareCall(sql);
//设置输入参数
cst.setString(1,"男");
//设置输出参数 输出参数的类型是jdbc类型
cst.registerOutParameter(2, Types.INTEGER);
//执行存储过程语句
cst.executeQuery();
//获取输出参数
int count = cst.getInt(2);
System.out.println("对应性别数量是:" + count);
//关闭资源
cst.close();
connection.close();
}
1.5 使用properties配置文件优化jdbc程序
- 定义properties配置文件
# 连接数据库的用户名
user=root
# 连接数据库的密码
password=Admin2022!
# 连接数据库的url
url=jdbc:mysql://192.168.10.140:3306/jdbc
# 数据库的驱动类
driverClass=com.mysql.jdbc.Driver
- 优化工具类
public class JdbcUtils {
private static String user = null;
private static String password = null;
private static String url = null;
private static String driverClass = null;
//读取properties配置文件,加载数据库驱动
static{
try {
Properties properties = new Properties();
InputStream in = JdbcUtils.class.getResourceAsStream("/db.properties");
properties.load(in);
user = properties.getProperty("user");
password = properties.getProperty("password");
url = properties.getProperty("url");
driverClass = properties.getProperty("driverClass");
Class.forName(driverClass);
} catch (Exception e) {
e.printStackTrace();
}
}
//获取数据库连接
public static Connection getConnection() throws Exception{
Connection connection = DriverManager.getConnection(url, user, password);
return connection;
}
//释放资源
public static void close(Connection conn, PreparedStatement pst){
if(pst != null){
try {
pst.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(Connection conn, PreparedStatement pst, ResultSet rs){
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(pst != null){
try {
pst.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(Connection conn, CallableStatement pst, ResultSet rs){
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(pst != null){
try {
pst.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(Connection conn, CallableStatement pst){
if(pst != null){
try {
pst.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
- 测试
public class TestUser {
@Test
public void test01() throws Exception{
Connection connection = JdbcUtils.getConnection();
String sql = "select * from tb_user";
PreparedStatement pst = connection.prepareStatement(sql);
ResultSet rs = pst.executeQuery();
while(rs.next()){
int id = rs.getInt("id");
String username = rs.getString("username");
String password = rs.getString("password");
System.out.println("用户编号:" + id + " 用户名:" + username + " 密码:" + password);
}
//关闭资源
JdbcUtils.close(connection,pst,rs);
}
}
1.6 连接池
在系统初始化的时候,将数据库连接作为对象存储在内存中(集合),当用户需要访问数据库时,并非建立一个新的连接,而是从连接池中取出一个已经建立的空闲连接对象。使用完毕后,用户也并非将连接关闭,而是将连接放回连接池中,以供下一个请求访问使用。而连接的建立、断开都由连 接池自身来管理。同时,还可以通过设置连接池的参数来控制连接池中的初始连接数、连接的上下限数 以及每个连接的最大使用次数、最大空闲时间等等,也可以通过其自身的管理机制来监视数据库连接的数量、使用情况等。
目前连接池可分为:自定义连接池、DBCP连接池、C3P0连接池、Druid德鲁伊连接池
连接池有很多属性:
-
最小连接数: 是数据库一直保持的数据库连接数,所以如果应用程序对数据库连接的使用量不大,将有大量的数据库资源被浪费。
-
初始化连接数: 连接池启动时创建的初始化数据库连接数量。
-
最大连接数: 是连接池能申请的最大连接数,如果数据库连接请求超过此数,后面的数据库连接请求被加入到等待队列中。
-
最大等待时间: 当没有可用连接时,连接池等待连接被归还的最大时间,超过时间则抛出异常,可设置参数为0或者负 数使得无限等待(根据不同连接池配置)。
数据库连接池又可以分为空闲连接池和活动连接池。
- 如果空闲连接池有连接对象,jdbc程序会直接找空闲连接池去要连接。
- 如果空闲连接池的连接全部被要走了,jdbc程序要找活动连接池获取连接对象,先看看活动连接池是否达到上限。
如果没有,就new一个连接对象,然后给活动连接池,再返回给jdbc程序,用完之后再归还空闲的连接对象。
如果达到了活动连接池的上限,那么就将最早创建的连接对象返回给用户使用。
1.6.1 自定义连接池
- 自定义连接池
/**
* 自定义连接池
*/
public class PoolCustomize {
//创建一个集合,专门存放连接对象
private static LinkedList<Connection> pools = new LinkedList<>();
//在静态代码块里面初始化一些连接对象
static{
try {
//加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
//初始化10个连接对象
for(int i = 0;i<10;i++){
Connection connection = JdbcUtils.getConnection();
pools.add(connection);
}
} catch (Exception e) {
e.printStackTrace();
}
}
//获取连接,从连接池里获取连接给用户使用
public static Connection getConnection(){
Connection connection = pools.removeFirst();
return connection;
}
//连接用完之后,需要将连接对象返回给连接池
public static void backToPool(Connection connection){
pools.addLast(connection);
}
}
- 测试连接池是否可用
//从连接池里面获取连接使用
@Test
public void test02() throws Exception{
Connection connection = PoolCustomize.getConnection();
//System.out.println(connection);
String sql = "select * from tb_user where id = ?";
PreparedStatement pst = connection.prepareStatement(sql);
pst.setInt(1,1);
ResultSet rs = pst.executeQuery();
if(rs.next()){
int id = rs.getInt("id");
String username = rs.getString("username");
String password = rs.getString("password");
System.out.println("用户ID:" + id + " 用户名:" + username + " 密码:" + password);
}
//归还连接给连接池
PoolCustomize.backToPool(connection);
}
2 jdbc加强
2.1 Dbutils组件的基本概述
2.2.1 什么是dbutils
Commons DbUtils是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,使用它能够简化JDBC应用程序的开发,同时也不会影响程序的性能。
一句话,dbutils可以简化jdbc的代码。如果我们使用dbutils,需要引入对应依赖:commons-dbutils-1.6.jar
2.1.2 DbUtils组件中的核心API
- QueryRunner: 是一个核心的工具类,通过这个工具类,我们实现数据的增删改查。
- int update(Connection conn,String sql,Object… params) 执行更新的操作
- int update(String sql,Object… params)
- T query(Connection conn,String sql,ResultSetHander hander,Object… params) 执行查询的操作
- BeanHandler 处理查询结果集,返回单个的查询对象。
- BeanListHandler 处理查询结果集,返回List集合。
- ArrayHandler 返回查询结果集的第一行数据,并将数据封装成对象数组。Object[]
- ScalarHandler 返回聚合函数的结果。
- MapHandler 将查询结果集封装成Map集合。
2.2 dbutils的使用
- 引入依赖
c3p0-0.9.1.2.jar
commons-dbutils-1.6.jar
mysql-connector-java-5.1.6.jar
- 引入c3p0连接池的配置文件,放在src目录下面。配置文件的名称必须叫c3p0-config.xml。
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://192.168.10.140:3306/jdbc</property>
<property name="user">root</property>
<property name="password">Admin2022!</property>
<!--
初始化的连接数量 在连接池里面初始化10个连接对象
-->
<property name="initialPoolSize">10</property>
<!--
最大空闲时间
某一个连接对象空闲时长最多是30s,超过了30s,该连接对象会被自动回收
-->
<property name="maxIdleTime">30</property>
<!--
最大连接数量
在连接池里面存在最多的连接数量
-->
<property name="maxPoolSize">100</property>
<!--
最小连接数量
-->
<property name="minPoolSize">10</property>
</default-config>
</c3p0-config>
- 编写工具类
public class JdbcUtils {
//初始化一个数据源对象
static DataSource dataSource = new ComboPooledDataSource();
//获取数据源的方法
public static DataSource getDataSource() {
return dataSource;
}
//获取连接对象的方法
public static Connection getConnection() throws Exception{
return dataSource.getConnection();
}
}
- 编写实体类(编写一个类和数据表形成映射关系)
public class Student {
private int id;
private String name;
private String gender;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", gender='" + gender + '\'' +
'}';
}
}
- 编写测试类
public class TestDbUtils {
@Test
public void test01() throws Exception{
Connection connection = JdbcUtils.getConnection();
System.out.println(connection);
}
//执行更新的操作 -- insert
@Test
public void test02() throws Exception{
QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
//QueryRunner runner = new QueryRunner();
String sql = "insert into student(name,gender) values(?,?)";
int result = runner.update(sql, "铁蛋", "男");
System.out.println("影响的行数是:" + result);
}
//执行更新操作 -- update
@Test
public void test03() throws Exception{
QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
String sql = "update student set name = ?,gender = ? where id = ?";
int count = runner.update(sql,"小丽","女",6);
System.out.println("修改成功,影响的行数是:" + count);
}
//执行更新操作 -- delete
@Test
public void test04() throws Exception{
QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
String sql = "delete from student where id = ?";
int count = runner.update(sql, 6);
System.out.println("删除成功,影响的行数是:" + count);
}
//查询操作 -- select
@Test
public void test05() throws Exception{
QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
String sql = "select * from student";
/**
* BeanListHandler 专门用来处理数据表结果集的。负责将结果集转换成List集合。
*/
List<Student> list = runner.query(sql, new BeanListHandler<Student>(Student.class));
/*for(Student student : list){
System.out.println(student);
}*/
list.forEach(student -> {
System.out.println(student);
});
}
@Test
public void test06() throws Exception{
QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
String sql = "select * from student where id = ?";
Student student = runner.query(sql, new BeanHandler<>(Student.class),1);
System.out.println(student);
}
}
2.3 BeanUtils组件
BeanUtils组价是apache提供的一套开源的api。用来简化javabean的操作。我们要使用BeanUtils的话,需要引入以下几个jar包:
commons-beanutils-1.8.3.jar
commons-logging-1.1.3.jar --这个包一定要引入,否则会报异常
2.3.1 beanutils的基本用法
- 前期准备
public class Admin {
private String username;
private String password;
private Integer age;
public Admin(){}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Admin{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", age=" + age +
'}';
}
}
public class Student {
private String name;
private Integer age;
private Date birthday;
public Student(){}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", birthday=" + birthday +
'}';
}
}
- 使用BeanUtils组件进行基本操作
public class BeanUtilsDemo1 {
@Test
public void test01() throws Exception{
Admin admin = new Admin();
//以前的做法
//admin.setUsername("eric");
//admin.setPassword("eric");
/**
* BeanUtils组件可以实现对象属性的拷贝
* copyProperty 实现对象属性的拷贝
* 参数1:给哪个对象拷贝属性
* 参数2:属性名称
* 参数3: 属性的值
* 如果涉及到基本数据类型的值的拷贝,BeanUtils还可以对数据类型进行自动转换。
*/
BeanUtils.copyProperty(admin,"username","sunny");
BeanUtils.copyProperty(admin,"password","sunny");
//BeanUtils.copyProperty(admin,"age",12);
BeanUtils.copyProperty(admin,"age","12");
System.out.println(admin);
/**
* 实现对象的拷贝
* copyProperties
* 参数1:需要拷贝的新对象
* 参数2:被拷贝的对象
*/
Admin newAdmin = new Admin();
BeanUtils.copyProperties(newAdmin,admin);
System.out.println(newAdmin);
/**
* 将Map集合中的数据进行拷贝
*/
Admin adminMap = new Admin();
Map<String,Object> map = new HashMap<>();
map.put("username","kobe");
map.put("password","kobe");
BeanUtils.populate(adminMap,map);
System.out.println(adminMap);
}
@Test
public void test02() throws Exception{
Student student = new Student();
ConvertUtils.register(new Converter() {
@Override
public Object convert(Class type, Object value) {
if(type != Date.class){
return null;
}
if(value == null || value.toString().trim().equals("")){
return null;
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
return sdf.parse(value.toString());
} catch (ParseException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}, Date.class);
BeanUtils.copyProperty(student,"name","eric");
BeanUtils.copyProperty(student,"age",18);
BeanUtils.copyProperty(student,"birthday","1999-12-03");
System.out.println(student);
}
@Test
public void test03() throws Exception{
Student student = new Student();
ConvertUtils.register(new DateLocaleConverter(),Date.class);
BeanUtils.copyProperty(student,"name","eric");
BeanUtils.copyProperty(student,"age",18);
BeanUtils.copyProperty(student,"birthday","1999-12-03");
System.out.println(student);
}
}
2.4 Jdbc进行事务处理
我们也可以在jdbc中实现事务的控制。jdbc提供了相关事务控制的api,具体如下:
connection.setAutoCommit(false); 关闭事务的自动提交,手动提交事务。
connection.commit(); 提交事务
connection.rollback(); 回滚事务
案例:使用jdbc进行转账事务的控制
/**
* 实现转账功能
* eric 向 james 转账500
* 如果不出现异常,转账成功(进行事务的提交)
* 如果出现异常,转账失败(进行事务的回滚)
*/
public class TestAccount {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement pst1 = null;
PreparedStatement pst2 = null;
try {
connection = JdbcUtils.getConnection();
//手动提交事务 默认为true 就是自动提交 false 手动提交事务
connection.setAutoCommit(false);
//准备sql语句--扣钱的语句
String sql1 = "update account set money = money - ? where id = ?";
//准备sql语句--加钱的语句
String sql2 = "update account set money = money + ? where id = ?";
//预编译sql
pst1 = connection.prepareStatement(sql1);
pst2 = connection.prepareStatement(sql2);
//设置参数
pst1.setInt(1,500);
pst1.setInt(2,1);
pst2.setInt(1,500);
pst2.setInt(2,2);
//执行sql
pst1.executeUpdate();
//手动模拟异常,表示转账失败
int i = 10 / 0;
pst2.executeUpdate();
//提交事务
connection.commit();
} catch (Exception e) {
if(connection != null){
try {
//回滚事务
connection.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
}finally {
//关闭资源
JdbcUtils.close(connection,pst1);
JdbcUtils.close(connection,pst2);
}
}
}
2.5 分层开发
- 准备工具类
package com.qf.utils;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class JdbcUtils {
private static String user = null;
private static String password = null;
private static String url = null;
private static String driverClass = null;
//读取properties配置文件,加载数据库驱动
static{
try {
Properties properties = new Properties();
InputStream in = JdbcUtils.class.getResourceAsStream("/db.properties");
properties.load(in);
user = properties.getProperty("user");
password = properties.getProperty("password");
url = properties.getProperty("url");
driverClass = properties.getProperty("driverClass");
Class.forName(driverClass);
} catch (Exception e) {
e.printStackTrace();
}
}
//获取数据库连接
public static Connection getConnection() throws Exception{
Connection connection = DriverManager.getConnection(url, user, password);
return connection;
}
//释放资源
public static void close(Connection conn, PreparedStatement pst){
if(pst != null){
try {
pst.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(Connection conn, PreparedStatement pst, ResultSet rs){
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(pst != null){
try {
pst.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(Connection conn, CallableStatement pst, ResultSet rs){
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(pst != null){
try {
pst.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(Connection conn, CallableStatement pst){
if(pst != null){
try {
pst.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
- 准备实体类
package com.qf.pojo;
public class Account {
private Integer id;
private String name;
private Integer money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getMoney() {
return money;
}
public void setMoney(Integer money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
- 准备dao及其实现类
package com.qf.dao;
import com.qf.pojo.Account;
import java.util.List;
public interface AccountDao {
//根据id查询对应的账户信息
public Account findAccountById(Integer id);
//查询所有的账户信息
public List<Account> findAll();
//新增账户信息
public int addAccount(Account account);
//修改账户信息
public int updateAccount(Account account);
//删除账户信息
public int deleteAccount(Integer id);
//根据姓名查询对应的账户信息
Account findAccountByName(String name);
//更新账户余额
void updateMoney(String source, Integer money);
}
package com.qf.dao.impl;
import com.qf.dao.AccountDao;
import com.qf.pojo.Account;
import com.qf.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
public class AccountDaoImpl implements AccountDao {
//根据id查询对应的账户信息
@Override
public Account findAccountById(Integer id) {
Connection connection = null;
PreparedStatement pst = null;
ResultSet rs = null;
try {
connection = JdbcUtils.getConnection();
String sql = "select * from account where id = ? ";
pst = connection.prepareStatement(sql);
pst.setInt(1,id);
rs = pst.executeQuery();
Account account = new Account();
if(rs.next()){
account.setId(rs.getInt("id"));
account.setName(rs.getString("name"));
account.setMoney(rs.getInt("money"));
}
return account;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
JdbcUtils.close(connection,pst,rs);
}
}
//查询所有账户信息
@Override
public List<Account> findAll() {
Connection connection = null;
PreparedStatement pst = null;
ResultSet rs = null;
try {
connection = JdbcUtils.getConnection();
String sql = "select * from account";
pst = connection.prepareStatement(sql);
rs = pst.executeQuery();
List<Account> accountList = new ArrayList<>();
while(rs.next()){
Account account = new Account();
account.setId(rs.getInt("id"));
account.setName(rs.getString("name"));
account.setMoney(rs.getInt("money"));
accountList.add(account);
}
return accountList;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
JdbcUtils.close(connection,pst,rs);
}
}
//新增账户信息
@Override
public int addAccount(Account account) {
Connection connection = null;
PreparedStatement pst = null;
try {
connection = JdbcUtils.getConnection();
String sql = "insert into account(name,money) values(?,?)";
pst = connection.prepareStatement(sql);
pst.setString(1,account.getName());
pst.setInt(2,account.getMoney());
int count = pst.executeUpdate();
return count;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
JdbcUtils.close(connection,pst);
}
}
//修改账户信息
@Override
public int updateAccount(Account account) {
Connection connection = null;
PreparedStatement pst = null;
try {
connection = JdbcUtils.getConnection();
String sql = "update account set name = ?,money = ? where id = ?";
pst = connection.prepareStatement(sql);
pst.setString(1,account.getName());
pst.setInt(2,account.getMoney());
pst.setInt(3,account.getId());
int count = pst.executeUpdate();
return count;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
JdbcUtils.close(connection,pst);
}
}
//删除账户信息
@Override
public int deleteAccount(Integer id) {
Connection connection = null;
PreparedStatement pst = null;
try {
connection = JdbcUtils.getConnection();
String sql = "delete from account where id = ?";
pst = connection.prepareStatement(sql);
pst.setInt(1,id);
int count = pst.executeUpdate();
return count;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
JdbcUtils.close(connection,pst);
}
}
@Override
public Account findAccountByName(String name) {
Connection connection = null;
PreparedStatement pst = null;
ResultSet rs = null;
try {
connection = JdbcUtils.getConnection();
String sql = "select * from account where name = ? ";
pst = connection.prepareStatement(sql);
pst.setString(1,name);
rs = pst.executeQuery();
Account account = new Account();
if(rs.next()){
account.setId(rs.getInt("id"));
account.setName(rs.getString("name"));
account.setMoney(rs.getInt("money"));
}
return account;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
JdbcUtils.close(connection,pst,rs);
}
}
//更新账户信息
@Override
public void updateMoney(String source, Integer money) {
Connection connection = null;
PreparedStatement pst = null;
try {
connection = JdbcUtils.getConnection();
String sql = "update account set money = money -? where name = ?";
pst = connection.prepareStatement(sql);
pst.setInt(1,money);
pst.setString(2,source);
//执行sql语句
pst.executeUpdate();
}catch (Exception e){
e.printStackTrace();
throw new RuntimeException(e);
}finally {
JdbcUtils.close(connection,pst);
}
}
}
- 准备service及其实现类
package com.qf.service;
import com.qf.pojo.Account;
import java.util.List;
public interface AccountService {
//根据id查询对应的账户信息
public Account findAccountById(Integer id);
//查询所有的账户信息
public List<Account> findAll();
//新增账户信息
public int addAccount(Account account);
//修改账户信息
public int updateAccount(Account account);
//删除账户信息
public int deleteAccount(Integer id);
//定义一个转账的业务
public void transfer(String source,String target,Integer money);
}
package com.qf.service.impl;
import com.qf.dao.AccountDao;
import com.qf.dao.impl.AccountDaoImpl;
import com.qf.pojo.Account;
import com.qf.service.AccountService;
import com.qf.utils.JdbcUtils;
import java.util.List;
public class AccountServiceImpl implements AccountService {
AccountDao accountDao = new AccountDaoImpl();
@Override
public Account findAccountById(Integer id) {
return accountDao.findAccountById(id);
}
@Override
public List<Account> findAll() {
return accountDao.findAll();
}
@Override
public int addAccount(Account account) {
return accountDao.addAccount(account);
}
@Override
public int updateAccount(Account account) {
return accountDao.updateAccount(account);
}
@Override
public int deleteAccount(Integer id) {
return accountDao.deleteAccount(id);
}
//转账业务实现的方法
@Override
public void transfer(String source, String target, Integer money) {
//查询扣款人的信息
Account sourceAccount = accountDao.findAccountByName(source);
//查询入账人的信息
Account targetAccount = accountDao.findAccountByName(target);
//如果两个账户信息都存在这些转账操作
if(sourceAccount != null && targetAccount != null){
//继续判断账户余额是否足够
if(sourceAccount.getMoney() >= money){
//执行转账操作 -- 转出
accountDao.updateMoney(source,money);
//执行转账操作 -- 转入
accountDao.updateMoney(target,-money);
}else{
System.out.println("余额不足....");
}
}
}
}
- 测试类
package com.qf.test;
import com.qf.pojo.Account;
import com.qf.service.AccountService;
import com.qf.service.impl.AccountServiceImpl;
import org.junit.jupiter.api.Test;
import java.util.List;
public class TestAccount {
AccountService accountService = new AccountServiceImpl();
@Test
public void test01(){
Account account = accountService.findAccountById(1);
System.out.println(account);
}
@Test
public void test02(){
List<Account> accountList = accountService.findAll();
for(Account account : accountList){
System.out.println(account);
}
}
@Test
public void test03(){
Account account = new Account();
account.setName("kobe");
account.setMoney(2000);
int count = accountService.addAccount(account);
System.out.println("新增成功,影响的记录行数是:" + count);
}
@Test
public void test04(){
Account account = new Account();
account.setName("curry");
account.setMoney(2000);
account.setId(2);
int count = accountService.updateAccount(account);
System.out.println("修改成功,影响记录的行数是:" + count);
}
@Test
public void test05(){
int count = accountService.deleteAccount(3);
System.out.println("删除成功,影响记录的行数是:" + count);
}
//执行转账业务操作
@Test
public void test06(){
accountService.transfer("eric","curry",500);
}
}