目录
JDBC
JDBC (Java DataBase Connection) 是通过JAVA访问数据库
MySQL
所以需要对数据库有基本的理解和应用
Hello JDBC
package MyJDBC;
import java.sql.*;
import static java.sql.DriverManager.getConnection;
/**
* Hello JDBC
*
* @author Aike
* @create 2021-05-17 15:23
*/
public class test1 {
public static void main(String[] args) {
// Connection c = null;
// Statement s = null;
//
// try {
// Class.forName("com.mysql.jdbc.Driver");
//
// c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai", "root",
// "12311534zZ&");
//
// s=c.createStatement();
//
// String sql="insert into hero values(null," + "'提莫'" + "," + 313.0f + "," + 50 + ")";
//
// s.execute(sql);
//
// System.out.println("插入成功");
// } catch (ClassNotFoundException e) {
// e.printStackTrace();
// } catch (SQLException throwables) {
// throwables.printStackTrace();
// }finally {
// if(s!=null) {
// try {
// s.close();
// } catch (SQLException throwables) {
// throwables.printStackTrace();
// }
// }
// if(c!=null) {
// try {
// c.close();
// } catch (SQLException throwables) {
// throwables.printStackTrace();
// }
// }
// }
//使用try-with-resource的方式自动关闭连接
// 驱动初始化
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try (
Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai", "root", "admin");
Statement s = c.createStatement();
) {
String sql = "select * from hero";
// 执行sql语句,将结果集返回给ResultSet(使用executeQuery执行sql语句)
ResultSet rs = s.executeQuery(sql);
// 迭代循环直到ResultSet的末尾
while (rs.next()) {
// 使用字段名查询
int id = rs.getInt("id");
// 使用字段顺序查询, 注意:get(2)表示获取第二列,而不是第三列
String name = rs.getString(2);
float hp = rs.getFloat("hp");
int damage = rs.getInt(4);
System.out.printf("%d\t%s\t%f\t%d\n", id, name, hp, damage);
}
// 不一定要在这里关闭ReultSet,因为Statement关闭的时候,会自动关闭ResultSet
// rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
增、删、改
CRUD是最常见的数据库操作,即增删改查
C 增加(Create)
R 读取查询(Retrieve)
U 更新(Update)
D 删除(Delete)
在JDBC中增加,删除,修改的操作都很类似,只是传递不同的SQL语句就行了。
查询因为要返回数据,所以和上面的不一样,将在下一节中讲解。
增加
package MyJDBC;
import java.sql.*;
import static java.sql.DriverManager.getConnection;
/**
* Hello JDBC
*
* @author Aike
* @create 2021-05-17 15:23
*/
public class test1 {
public static void main(String[] args) {
// 驱动初始化
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try (
Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai", "root", "admin");
Statement s = c.createStatement();
) {
String sql = "insert into hero values(null," + "'提莫'" + "," + 313.0f + "," + 50 + ")";
System.out.println(sql);
s.execute(sql);
System.out.println("插入成功");
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
删除
package MyJDBC;
import java.sql.*;
import static java.sql.DriverManager.getConnection;
/**
* Hello JDBC
*
* @author Aike
* @create 2021-05-17 15:23
*/
public class test1 {
public static void main(String[] args) {
// 驱动初始化
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try (
Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai", "root", "admin");
Statement s = c.createStatement();
) {
String sql = "delete from hero where id=5";
System.out.println(sql);
s.execute(sql);
System.out.println("删除成功");
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
修改
package MyJDBC;
import java.sql.*;
import static java.sql.DriverManager.getConnection;
/**
* Hello JDBC
*
* @author Aike
* @create 2021-05-17 15:23
*/
public class test1 {
public static void main(String[] args) {
// 驱动初始化
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try (
Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai", "root", "admin");
Statement s = c.createStatement();
) {
String sql = "update hero set name = 'name-5' where id = 3";
System.out.println(sql);
s.execute(sql);
System.out.println("修改成功");
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
查询
普通查询
package MyJDBC;
import java.sql.*;
import static java.sql.DriverManager.getConnection;
/**
* Hello JDBC
*
* @author Aike
* @create 2021-05-17 15:23
*/
public class test1 {
public static void main(String[] args) {
// 驱动初始化
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try (
Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai", "root", "admin");
Statement s = c.createStatement();
) {
String sql = "select * from hero";
// 执行sql语句,将结果集返回给ResultSet(使用executeQuery执行sql语句)
ResultSet rs = s.executeQuery(sql);
// 迭代循环直到ResultSet的末尾
while (rs.next()) {
// 使用字段名查询
int id = rs.getInt("id");
// 使用字段顺序查询, 注意:get(2)表示获取第二列,而不是第三列
String name = rs.getString(2);
float hp = rs.getFloat("hp");
int damage = rs.getInt(4);
System.out.printf("%d\t%s\t%f\t%d\n", id, name, hp, damage);
}
// 不一定要在这里关闭ReultSet,因为Statement关闭的时候,会自动关闭ResultSet
// rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
SQL语句判断账号密码是否正确
创建一个用户表,专门存储帐号和密码
CREATE TABLE user (
id int(11) AUTO_INCREMENT,
name varchar(30) ,
password varchar(30),
PRIMARY KEY (id)
) ;
insert into user values(null,'dashen','thisispassword');
判断账号密码的正确方式是根据账号和密码到表中去找数据,如果有数据,就表明密码正确了,如果没数据,就表明密码错误。
不恰当的方式 是把uers表的数据全部查到内存中,挨个进行比较。 如果users表里有100万条数据呢? 内存都不够用的。
package MyJDBC;
import java.sql.*;
import static java.sql.DriverManager.getConnection;
/**
* SQL语句判断账号密码是否正确
*
* @author Aike
* @create 2021-05-17 19:31
*/
public class test2 {
public static void main(String[] args) {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try (
Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai", "root", "admin");
Statement s = c.createStatement();
) {
String name = "dashen";
// 正确密码为:thisispassword
String password = "thisispassword";
String sql = "select * from user where name = '" + name + "' and password = '" + password + "'";
System.out.println(sql);
ResultSet rs = s.executeQuery(sql);
if (rs.next())
System.out.println("密码正确");
else
System.out.println("密码错误");
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
获取数据数量
package MyJDBC;
import java.sql.*;
import static java.sql.DriverManager.getConnection;
/**
* SQL语句判断账号密码是否正确
*
* @author Aike
* @create 2021-05-17 19:31
*/
public class test2 {
public static void main(String[] args) {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try (
Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai", "root", "admin");
Statement s = c.createStatement();
) {
// 查询总数
int total = 0;
String sql = "select count(*) from hero";
ResultSet rs = s.executeQuery(sql);
while (rs.next()) {
total=rs.getInt(1);
}
System.out.println("数据总数:"+total);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
预编译PreparedStatement
PreparedStatement的使用
和 Statement一样,PreparedStatement也是用来执行sql语句的
与创建Statement不同的是,需要根据sql语句创建PreparedStatement
除此之外,还能够通过设置参数,指定相应的值,而不是Statement那样使用字符串拼接
- 使用prepareStatement(sql)进行创建
- 这是JAVA里唯二的基1的地方,另一个是查询语句中的ResultSet也是基1的。
预编译Statement的优点:
- 参数设置
- 性能表现
- 防止SQL注入式攻击
package MyJDBC;
import java.sql.*;
import static java.sql.DriverManager.getConnection;
/**
* SQL语句判断账号密码是否正确
*
* @author Aike
* @create 2021-05-17 19:31
*/
public class test2 {
public static void main(String[] args) {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
String sql = "insert into hero values(null,?,?,?)";
try (
Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai", "root", "admin");
// Statement s = c.createStatement();
// 根据sql语句创建PreparedStatement
PreparedStatement ps = c.prepareStatement(sql);
) {
// 设置参数
// 这是JAVA里唯二的基1的地方,另一个是查询语句中的ResultSet也是基1的。
ps.setString(1,"后裔");
ps.setFloat(2,520.0f);
ps.setInt(3,120);
System.out.println(ps.toString());
ps.execute();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
PreparedStatement与Statement的性能比较
package MyJDBC;
import java.sql.*;
/**
* 比较PreparedStatement和Statement之间的性能差异
*
* @author Aike
* @create 2021-05-17 20:07
*/
public class test3 {
public static void main(String[] args) {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
String sql1="insert into hero values(null,?,?,?)";
try (
Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1/how2java?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai", "root", "admin");
Statement s = c.createStatement();
PreparedStatement ps=c.prepareStatement(sql1);
) {
// Statement执行
long start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
String sql = "insert into hero values(null," + "'" + "hero" + i + "'" + "," + 520.0f + "," + 120 + ")";
// System.out.println(sql);
s.execute(sql);
}
long end = System.currentTimeMillis();
System.out.println("Statement执行消耗时间:" + (end - start));
start=System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
ps.setString(1,"hero"+i);
ps.setFloat(2,520.0f);
ps.setInt(3,120);
// System.out.println(ps.toString());
ps.execute();
}
end=System.currentTimeMillis();
System.out.println("PreparedStatement执行消耗时间:" + (end - start));
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
execute与executeUpdate
相同
都可以执行增加,删除,修改的SQL语句
不同
-
不同1
- execute可以执行查询语句,然后通过getResultSet,把结果集取出来
- executeUpdate不能执行查询语句
-
不同2
- execute返回boolean类型,true表示是执行的是查询语句,false表示执行的是insert,delete,update等等
- executeUpdate返回的是int类型,表示有多少条数据受到了影响
实例:
package MyJDBC;
import java.sql.*;
/**
* execute和executeUpdate的区别
*
* @author Aike
* @create 2021-05-18 19:24
*/
public class test4 {
public static void main(String[] args) {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try (
Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1/how2java?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai", "root", "admin");
Statement s = c.createStatement();
) {
String sqlInsert = "insert into Hero values(null,'盖伦',616,120)";
String sqlDelete = "delete from Hero where id=100";
String sqlUpdate = "update Hero set hp = 300 where id=90";
// 相同点:都可以执行增、删、改的sql语句
s.execute(sqlInsert);
s.execute(sqlDelete);
s.execute(sqlUpdate);
s.executeUpdate(sqlInsert);
s.executeUpdate(sqlDelete);
s.executeUpdate(sqlUpdate);
// 不同点:
// execute可以执行查询语句,不过要通过getResults将结果集取出来
String sqlSelect = "select * from Hero";
s.execute(sqlSelect);
ResultSet rs = s.getResultSet();
while (rs.next()) {
System.out.printf("%d\t%s\t%f\n",rs.getInt("id"),rs.getString("name"),rs.getFloat("hp"));
}
// executeUpdate不能执行查询语句
// s.executeUpdate(sqlSelect);
// 出现java.sql.SQLException错误
// 返回类型不同
System.out.println(s.execute(sqlSelect));
sqlUpdate="update hero set hp = 0 where id < 100";
System.out.println(s.executeUpdate(sqlUpdate));
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
特殊操作
获取自增长id
在Statement通过execute或者executeUpdate执行完插入语句后,MySQL会为新插入的数据分配一个自增长id,(前提是这个表的id设置为了自增长,在Mysql创建表的时候,AUTO_INCREMENT就表示自增长)
无论是excute还是excutUpdate都不会返回这个增长id是多少。需要通过Statement的getGeneratedKeys获取该id。
有的时候还会加上Statement.RETURN_GENERATED_KEYS参数,通常不需要加,有的时候需要加,所以先加上,保险一些
PreparedStatement ps = c.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
获取表的元数据
元数据概念:和数据库服务器相关的数据,比如数据库版本,有哪些表,表有哪些字段,字段类型是什么等等。
实例:
package MyJDBC;
import java.sql.*;
/**
* 特殊操作
* 获取自动增长id
* 获取表的元数据
*
* @author Aike
* @create 2021-05-18 19:44
*/
public class test5 {
public static void main(String[] args) {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
String sql = "insert into hero values(null,?,?,?)";
try (
Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1/how2java?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai", "root", "admin");
PreparedStatement ps = c.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
) {
ps.setString(1, "盖伦");
ps.setFloat(2, 520f);
ps.setInt(3, 120);
ps.execute();
// 获取自动增长id
ResultSet rs = ps.getGeneratedKeys();
if (rs.next()) {
int id = rs.getInt(1);
System.out.println(id);
}
// 查看数据库层面的元数据
// 即数据库服务器版本,驱动版本,都有哪些数据库等等
DatabaseMetaData dbmd=c.getMetaData();
// 获取数据库服务器产品名称
System.out.println("数据库服务器产品名称:\t"+dbmd.getDatabaseProductName());
// 获取数据库服务器产品版本号
System.out.println("数据库服务器产品版本号:\t"+dbmd.getDatabaseProductVersion());
// 获取数据库服务器用作类别和表名之间的分隔符 如test.user
System.out.println("数据库和表分隔符:\t"+dbmd.getCatalogSeparator());
// 获取驱动版本
System.out.println("驱动版本:\t"+dbmd.getDriverVersion());
// 获取驱动名称
System.out.println("驱动名称:\t"+dbmd.getDriverName());
System.out.println("可用的数据库列表:");
// 获取数据库名称
rs=dbmd.getCatalogs();
while (rs.next()){
System.out.println("数据库名称:\t"+rs.getString(1));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
事务
事务(Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。在计算机术语中,事务通常就是指数据库事务。
事务的使用
JDBC中不使用事务,则可能出现以条SQL语句执行成功,一条不成功
在事务中的多个操作,要么都成功,要么都失败
通过 c.setAutoCommit(false);关闭自动提交
使用 c.commit();进行手动提交
事务使用的前提
在Mysql中,只有当表的类型是INNODB的时候,才支持事务,所以需要把表的类型设置为INNODB,否则无法观察到事务.
修改表的类型为INNODB的SQL:
alter table hero ENGINE = innodb;
查看表的类型的SQL
show table status from how2java;
前提,就是当前的MYSQL服务器本身要支持INNODB,如果不支持, 开启MYSQL INNODB的办法
实例:
package MyJDBC;
import java.sql.*;
/**
* @author Aike
* @create 2021-05-20 16:10
*/
public class test6 {
public static void main(String[] args) {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try (
Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1/how2java?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai", "root", "admin");
Statement s = c.createStatement();
){
// 在没有事务的情况下
// 业务操作为加血一次 减血一次 = 血量不变
String sql1="update hero set hp = hp + 1 where id = 58004";
// 不小心写错写成了 updata(而非update)
String sql2="updata hero set hp = hp - 1 where id = 58004";
s.execute(sql1);
s.execute(sql2);
// 此时加血执行 减血没有执行
// 有事务的前提下
// 在事务中的多个操作,要么都成功,要么都失败
// 关闭自动提交
c.setAutoCommit(false);
s.execute(sql1);
s.execute(sql2);
// 手动提交
c.commit();
}catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
ORM
ORM概念
ORM=Object Relationship Database Mapping
ORM是一种思想,将数据库记录与对象相对应
对象和关系数据库的映射
简单说,一个对象,对应数据库里的一条记录
ORM练习
根据id返回一个Hero对象
package MyJDBC;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* ORM的学习与使用
* 将数据库的记录与对象相对应
*
* @author Aike
* @create 2021-05-20 16:27
*/
public class test7 {
// 把一个Hero对象插入到数据库中
public static void add(Hero h) {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try (
Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1/how2java?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai", "root", "admin");
Statement s = c.createStatement();
) {
String sql = "insert into hero values(" + h.id + ",'" + h.name + "'," + h.hp + "," + h.damage + ")";
System.out.println(sql);
s.execute(sql);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
// 把这个Hero对象对应的数据删除掉
public static void delete(Hero h) {
String sql = "delete from hero where id=?";
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try (
Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1/how2java?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai", "root", "admin");
PreparedStatement ps = c.prepareStatement(sql);
) {
ps.setInt(1, h.id);
System.out.println(ps.toString());
ps.execute();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
// 更新这条Hero对象
public static void update(Hero h) {
String sql = "update hero set name=?,hp=?,damage=?,id=?";
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try (
Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1/how2java?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai", "root", "admin");
PreparedStatement ps = c.prepareStatement(sql);
) {
ps.setString(1,h.name);
ps.setFloat(2,h.hp);
ps.setInt(3,h.damage);
ps.setInt(4,h.id);
System.out.println(ps.toString());
ps.execute();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
// 把所有的Hero数据查询出来,转换为Hero对象后,放在一个集合中返回
public static List<Hero> list() {
List<Hero> heros=new ArrayList<>();
String sql = "select * from hero";
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try (
Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1/how2java?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai", "root", "admin");
PreparedStatement ps = c.prepareStatement(sql);
) {
ResultSet rs=ps.executeQuery();
while(rs.next()){
Hero hero=new Hero();
hero.id=rs.getInt(1);
hero.name=rs.getString(2);
hero.hp =rs.getFloat(3);
hero.damage=rs.getInt(4);
heros.add(hero);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return heros;
}
public static Hero get(int id) {
Hero hero = null;
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try (
Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1/how2java?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai", "root", "admin");
Statement s = c.createStatement();
) {
String sql = "select * from hero where id = " + id;
ResultSet rs = s.executeQuery(sql);
if (rs.next()) {
hero = new Hero();
hero.name = rs.getString("name");
hero.hp = rs.getFloat("hp");
hero.damage = rs.getInt("damage");
hero.id = id;
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return hero;
}
public static void main(String[] args) {
Hero h = get(58004);
System.out.println(h.name);
Hero h1 = new Hero(1, "后裔", 520.02f, 120);
// add(h1);
// delete(h1);
System.out.println(list());
}
}
DAO
DAO概念
DAO=DataAccess Object
数据访问对象
实际上就是运用了练习-ORM中的思路,把数据库相关的操作都封装在这个类里面,其他地方看不到JDBC的代码
DAO接口
public interface DAO{
//增加
public void add(Hero hero);
//修改
public void update(Hero hero);
//删除
public void delete(int id);
//获取
public Hero get(int id);
//查询
public List<Hero> list();
//分页查询
public List<Hero> list(int start, int count);
}
HeroDAO
设计类HeroDAO,实现接口DAO
这个HeroDAO和ORM很接近,做了几个改进:
- 把驱动的初始化放在了构造方法HeroDAO里:
public HeroDAO() {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
因为驱动初始化只需要执行一次,所以放在这里更合适,其他方法里也不需要写了,代码更简洁
- 提供了一个getConnection方法返回连接
所有的数据库操作都需要事先拿到一个数据库连接Connection,以前的做法每个方法里都会写一个,如果要改动密码,那么每个地方都需要修改。 通过这种方式,只需要修改这一个地方就可以了。 代码变得更容易维护,而且也更加简洁。
实例:
package MyJDBC;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* DAO
* 数据访问对象
* 实际上就是运用了练习-ORM中的思路,把数据库相关的操作都封装在这个类里面,其他地方看不到JDBC的代码
*
* @author Aike
* @create 2021-05-20 17:19
*/
interface DAO {
//增加
public void add(Hero hero);
//修改
public void update(Hero hero);
//删除
public void delete(int id);
//获取
public Hero get(int id);
//查询
public List<Hero> list();
//分页查询
public List<Hero> list(int start, int count);
}
class HeroDAO implements DAO {
public HeroDAO() {
// 加载驱动
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
// 获取一个数据库连接
public Connection getConnection() throws SQLException {
return DriverManager.getConnection("jdbc:mysql://127.0.0.1/how2java?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai", "root", "admin");
}
// 获取数量
public int getTotal() {
int total = 0;
try (Connection c = getConnection(); Statement s = c.createStatement();) {
String sql = "select count(*) from hero";
ResultSet rs = s.executeQuery(sql);
if (rs.next()) {
total = rs.getInt(1);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return total;
}
public void add(Hero hero) {
String sql = "insert into hero values(null,?,?,?)";
try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql);) {
ps.setString(1, hero.name);
ps.setFloat(2, hero.hp);
ps.setInt(3, hero.damage);
ps.execute();
ResultSet rs = ps.getGeneratedKeys();
if (rs.next()) {
int id = rs.getInt(1);
hero.id = id;
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public void update(Hero hero) {
String sql = "update hero set name= ?, hp = ? , damage = ? where id = ?";
try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql);) {
ps.setString(1, hero.name);
ps.setFloat(2, hero.hp);
ps.setInt(3, hero.damage);
ps.setInt(4, hero.id);
ps.execute();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void delete(int id) {
try (Connection c = getConnection(); Statement s = c.createStatement();) {
String sql = "delete from hero where id = " + id;
s.execute(sql);
} catch (SQLException e) {
e.printStackTrace();
}
}
public Hero get(int id) {
Hero hero = null;
try (Connection c = getConnection(); Statement s = c.createStatement();) {
String sql = "select * from hero where id = " + id;
ResultSet rs = s.executeQuery(sql);
if (rs.next()) {
hero = new Hero();
String name = rs.getString(2);
float hp = rs.getFloat("hp");
int damage = rs.getInt(4);
hero.name = name;
hero.hp = hp;
hero.damage = damage;
hero.id = id;
}
} catch (SQLException e) {
e.printStackTrace();
}
return hero;
}
public List<Hero> list() {
return list(0, Short.MAX_VALUE);
}
public List<Hero> list(int start, int count) {
List<Hero> heros = new ArrayList<Hero>();
String sql = "select * from hero order by id desc limit ?,? ";
try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql);) {
ps.setInt(1, start);
ps.setInt(2, count);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
Hero hero = new Hero();
int id = rs.getInt(1);
String name = rs.getString(2);
float hp = rs.getFloat("hp");
int damage = rs.getInt(4);
hero.id = id;
hero.name = name;
hero.hp = hp;
hero.damage = damage;
heros.add(hero);
}
} catch (SQLException e) {
e.printStackTrace();
}
return heros;
}
}
public class test8 {
public static void main(String[] args) {
HeroDAO heroDAO=new HeroDAO();
System.out.println(heroDAO.getTotal());
}
}
数据库连接池
为什么要使用数据库连接池
节约空间、时间
避免数据库连接失败
数据库连接池原理——传统方式
当有多个线程,每个线程都需要连接数据库执行SQL语句的话,那么每个线程都会创建一个连接,并且在使用完毕后,关闭连接。
创建连接和关闭连接的过程也是比较消耗时间的,当多线程并发的时候,系统就会变得很卡顿。
同时,一个数据库同时支持的连接总数也是有限的,如果多线程并发量很大,那么数据库连接的总数就会被消耗光,后续线程发起的数据库连接就会失败。
数据库连接池原理-使用池
与传统方式不同,连接池在使用之前,就会创建好一定数量的连接。
如果有任何线程需要使用连接,那么就从连接池里面借用,而不是自己重新创建.
使用完毕后,又把这个连接归还给连接池供下一次或者其他线程使用。
倘若发生多线程并发情况,连接池里的连接被借用光了,那么其他线程就会临时等待,直到有连接被归还回来,再继续使用。
整个过程,这些连接都不会被关闭,而是不断的被循环使用,从而节约了启动和关闭连接的时间。
ConnectionPool构造方法和初始化
-
ConnectionPool() 构造方法约定了这个连接池一共有多少连接
-
在init() 初始化方法中,创建了size条连接。 注意,这里不能使用try-with-resource这种自动关闭连接的方式,因为连接恰恰需要保持不关闭状态,供后续循环使用
-
getConnection, 判断是否为空,如果是空的就wait等待,否则就借用一条连接出去
-
returnConnection, 在使用完毕后,归还这个连接到连接池,并且在归还完毕后,调用notifyAll,通知那些等待的线程,有新的连接可以借用了。
注:连接池设计用到了多线程的wait和notifyAll。
package MyJDBC;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.ArrayList;
/**
* 数据库连接池
*
* @author Aike
* @create 2021-05-22 12:39
*/
class ConnectionPool {
List<Connection> cs = new ArrayList<Connection>();
int size;
public ConnectionPool(int size) {
this.size = size;
init();
}
public void init() {
// 不能使用try-with-resource的方式,因为这些连接必须都是活的
try {
Class.forName("com.mysql.jdbc.Driver");
for (int i = 0; i < size; i++) {
Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1/how2java?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai", "root", "admin");
cs.add(c);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
// 获取数据库连接对象
public synchronized Connection getConnection() {
while (cs.isEmpty()) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Connection c = cs.remove(0);
return c;
}
// 返回数据库连接对象
public synchronized void returnConnection(Connection c) {
cs.add(c);
this.notifyAll();
}
}
class WorkingThread extends Thread {
private ConnectionPool cp;
public WorkingThread(String name, ConnectionPool cp) {
super(name);
this.cp = cp;
}
public void run() {
Connection c = cp.getConnection();
System.out.println(this.getName() + "\t获取了一根连接,并开始工作");
try (
Statement s = c.createStatement();
) {
Thread.sleep(1000);
s.execute("select * from hero");
} catch (SQLException | InterruptedException throwables) {
throwables.printStackTrace();
}
cp.returnConnection(c);
}
}
public class test9 {
public static void main(String[] args) {
ConnectionPool cp = new ConnectionPool(5);
WorkingThread workingThread1 = new WorkingThread("1号", cp);
WorkingThread workingThread2 = new WorkingThread("2号", cp);
WorkingThread workingThread3 = new WorkingThread("3号", cp);
WorkingThread workingThread4 = new WorkingThread("4号", cp);
workingThread1.run();
workingThread2.run();
workingThread3.run();
workingThread4.run();
}
}