jdbcAPI:与数据库建立连接、执行SQL 语句、处理结果
DriverManager :依据数据库的不同,管理JDBC驱动
Connection :负责【连接数据库并担任传送】数据的任务
Statement :由 Connection 产生、【负责执行SQL】语句
ResultSet:负责【保存Statement执行后所产生的查询结果】
由JDBC驱动直接访问数据库
优点:完全Java代码,快速、跨平台
缺点:访问不同的数据库需要下载专用的JDBC驱动
将对JDBC API的调用,转换为对另一组数据库连接API的调用
优点:可以访问所有ODBC可以访问的数据库
缺点:执行效率低、功能不够强大
jdbc:mysql:///kgc?serverTimezone=UTC&useSSL=true&useUnicode=true&characterEncoding=utf-8
推荐:Class.forName(“com.mysql.cj.jdbc.Driver”);
不推荐DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
url 中添加useSSL=true
Jdbc程序中的DriverManager用于加载驱动,并创建与数据库的链接,这个API的常用方法:
DriverManager.registerDriver(new Driver()) DriverManager.getConnection(url, user, password)
注意:在实际开发中并不推荐采用registerDriver方法注册驱动。原因有二:
1. 查看Driver的源代码可以看到,如果采用此种方式,会导致驱动程序注册两次,也就是在内存中会有两个Driver对象【一次是加载驱动,一次是获得连接】。
2. 程序依赖mysql的api,脱离mysql的jar包,程序将无法编译,将来程序切换底层数据库将会非常麻
烦。
推荐方式:Class.forName("com.mysql.cj.jdbc.Driver");
采用此种方式不会导致驱动对象在【内存中重复出现】,并且采用此种方式,程序仅仅只需要一个字符串,
不需要依赖【具体的驱动】,使程序的【灵活性更高】
set global time_zone = ‘+8:00’;
set time_zone = ‘+8:00’;
show variables like ‘%time_zone%’;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DofrZuXg-1604303154959)(JDBC/image-20201023192142434.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UYa55F1A-1604303154961)(C:UsersASUSAppDataRoamingTyporatypora-user-imagesimage-20201020152224124.png)]
con=DriverManager.getConnection(“jdbc:mysql://localhost:3306/kgc?serverTimezone=UTC”, “root”, “123456”);
功能:驱动管理使用前需要先进行导包注意:
导入驱动jar包 mysql-connector-java-5.1.37-bin.jar或者mysql-connector-java-8.0.20.jar【1.复制mysql-connector-java-5.1.37-bin.jar到项目的libs目录下2.右键–>【idea 用Add As Library】【ecliise用build path放入到【在src包下建一个lib包】中】
1-1 注册驱动:告诉程序该使用哪一个数据库驱动jar
static void registerDriver(Driver driver) :注册与给定的驱动程序 DriverManager 。
//DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());不推荐使用
这种方式来加载驱动
通过查看源码发现:在com.mysql.jdbc.Driver类中存在静态代码块
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException(“Can’t register driver!”);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kp5FYlxe-1604303154963)(JDBC/image-20201022075412981.png)]
注意:mysql5之后的驱动jar包可以省略注册驱动的步骤。
方法1:【三个参数分开写】
static Connection getConnection(String url, String user, String password)
方法2:【三个参数写在一起】
static Connection getConnection("jdbc:/mysql:///kgc&useSSL=true? user=root&password=123456”)
参数:
url:指定连接的路径
例子:jdbc:mysql://localhost:3306/db3
细节:如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称
user:用户名
password:密码
Connection:数据库连接对象
功能:
获取执行sql 的对象
开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
提交事务:commit()
回滚事务:rollback()
Jdbc程序中的Statement对象用于【向数据库发送SQL语句】,
Statement对象常用方法: 【如果是preparedstatement不加参数sql】
executeQuery(String sql) :用于向数据发送查询语句。
executeUpdate(String sql):用于向数据库发送insert、update或delete语句
execute(String sql):用于向数据库发送任意sql语句
addBatch(String sql) :把多条sql语句放到一个批处理中。
executeBatch():向数据库发送一批sql语句执行。
Statement createStatement()
PreparedStatement prepareStatement(String sql)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G11kZbSG-1604303154964)(JDBC/image-20201021113028668.png)]
执行sql
2-3-1boolean execute(String sql) :可以执行任意的sql 了解
2-3-3 ResultSet executeQuery(String sql) :执行DQL(select)语句
account表 添加一条记录
int count = stmt.executeUpdate(sql);//影响的行数
System.out.println(count);
if(count > 0){
System.out.println("添加成功!");
}else{
System.out.println("添加失败!");
}
boolean next(): 【游标】向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果是,则返回false,如果不是则返回true
默认游标指向第一行结果之前
getXxx(参数):获取数据
Xxx:代表数据类型 如: int getInt() , String getString()
while(it.next()){
syso(rs.getInt(1)+“\t学员名字”+rs.getString(student_name));
}
【增删改时,在预编译后,通过预编译对象可以用setString()给任何下标进行赋值】
参数:
int:代表列的编号,从1开始 如: getString(1)
String:代表列名称。 如: getDouble(“balance”)
使用步骤:
游标向下移动一行
判断是否有数据
获取数据
//循环判断游标是否是最后一行末尾。
ResultSet【用来接收stm预编译对象执行SQL语句的结果】
既然用于封装执行结果的,所以该对象提供的都是用于获取数据的get方法:
获取任意类型的数据
getObject(int index)
getObject(string columnName)
获取指定类型的数据,例如:
getString(int index) //根据下标获取对象的值
getString(String columnName) //根据字段名(列)获取对象的值
ResultSet还提供了对结果集进行滚动的方法:
next():移动到下一行
Previous():移动到前一行
absolute(int row):移动到指定行
beforeFirst():移动resultSet的最前面。
afterLast() :移动到resultSet的最后面
preparedStatement.setDate(5,new Date(new java.util.Date().getTime()));//给第五个占位符? 的值赋值为new Date(new java.util.Date().getTime());//插入当前时间
也可以手动输入stm.setString(“ 2020-10-20 12-00-00”);
select UTC_TIMESTAMP() 获取世界标准是时间
select EXTRACT(HOUR FROM TIMEDIFF(NOW() ,UTC_TIMESTAMP())) 数据库时间与标准时间 相差的时间
DATE_FORMAT()函数
将时间戳转换为日期格式:比如降1455504268→2016-02-15 10:44:28
1 select device.register_time a,FROM_UNIXTIME(device.register_time,'%Y-%m-%d %H:%i:%s') as registerTime from tm_data_newdevice device
1 select device.register_time a,FROM_UNIXTIME(device.register_time,'%Y-%m-%d %H:%i:%s') as registerTime from tm_data_newdevice device
while ( rs.next() ) {
System.out.print( rs.getInt(1) +"t");
System.out.print( rs.getString(2) +"t");
System.out.print(rs.getInt("loginPassword")+ "t");
… …
}
while(rs.next()){
//获取数据
System.out.println(rs.getInt(1) + "---" + rs.getString("name") + "---" + rs.getDouble(3)+rs.getDate(4));
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dXJENiEs-1604303154965)(C:UsersASUSAppDataRoamingTyporatypora-user-imagesimage-20201020191837104.png)]
例如查询count(1)查询学生人数结果是单列,
1.可以直接在
while(rs.next()){
system.out.println(rs.getInt(1));//第一列的值StringInt
//2system.out.println(rs.getInt(“总人数”));//要么聚合函数必须起别名。
package cn.kgc.tangcco.test1021;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Scanner;
public class TestJuheFunction {
public static void main(String[] args) throws SQLException {
//声明接口和手动输入关键字;
Connection con=null;
ResultSet rs=null;
PreparedStatement stm=null;
Scanner input = new Scanner(System.in);
try {
Class.forName("com.mysql.cj.jdbc.Driver");
// 2.获得连接
con=DriverManager.getConnection("jdbc:mysql:///kgc", "root", "123456");
// 3.编写sql语句并预编译,执行
String sql="select count(1) 总人数 from student";
// String sql="select count(1) studentNums from student";//起中英文别名都行
stm=con.prepareStatement(sql);
// 4.接收预编译对象的数据并遍历
rs=stm.executeQuery();
while (rs.next()) {
System.out.println(rs.getInt("总人数"));
// System.out.println(rs.getInt(studentNums)); //此时用英文做变量名
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if (rs!=null) {
rs.close();
}
if (stm!=null) {
stm.close();
}
if (con!=null) {
con.close();
}
}
}
}
/
48
/
}
释放ResultSet, Statement,Connection。
数据库连接(Connection)是非常稀有的资源,用完后必须马上释放,如果Connection不能及时正确的关闭将导致【系统宕机】Connection的使用原则是尽量晚创建,尽量早的释放
。关闭资源
可以关闭结果集、命令、连接
注意:注意顺序,倒着关闭
【利用or或者是– 注释符号及引号。获取所有人的密码或者免验证登录;利用statement需要拼接sql语句的漏洞】在预编译的过程中添加的内容只会作为字符段的一部分,不会改变sql语句的结构;
不要以为在输入框做个检查就够了,不要忘记了,我们web提交表单,是可以模拟url直接访问过去,绕开前段检查。因此,必须是后端,或是数据来检查才能有效防止。
(1)检查用户输入的合法性;【输入框验证】
(2)将用户的登录名、密码等数据加密保存。【md5加盐】
(3)预处理SQL。【采用占位符,无论输入什么,都作为字段内容】
(4)使用存储过程实现查询,虽然不推荐,但也是一个方法。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9pqvimka-1604303154966)(C:UsersASUSAppDataRoamingTyporatypora-user-imagesimage-20201020173808157.png)]
其实是因为SQL语句在程序运行前已经进行了预编译,在程序运行时第一次操作数据库之前,SQL语句已经被数据库分析,编译和优化,对应的执行计划也会缓存下来并允许数据库已参数化的形式进行查询,当运行时动态地把参数传给PreprareStatement时,即使参数里有敏感字符如 or '1=1’也数据库会作为【一个参数一个字段的属性值】来处理而不会作为一个SQL指令,如此,就起到了SQL注入的作用了
:在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全性问题
输入用户随便,输入密码:a’ or ‘a’ = 'a
sql:select from user where username = ‘fhdsjkf’ and password = ‘a’ or ‘a’ = ‘a’
解决sql注入问题:使用PreparedStatement对象来解决
预编译的SQL:参数使用?作为占位符
步骤:
导入驱动jar包 mysql-connector-java-5.1.37-bin.jar
注册驱动
获取数据库连接对象 Connection
定义sql
注意:sql的参数使用?作为占位符。 如:select from user where username = ? and password = ?;
方法: setXxx(参数1,参数2)
参数1:?的位置编号 从1 开始
参数2:?的值
执行sql,接受返回结果,不需要传递sql语句
处理结果
释放资源
注意:后期都会使用PreparedStatement来完成增删改查的所有操作
可以防止SQL注入
效率更高
方法名 | 说 明 |
---|---|
boolean next() | 将游标从当前位置向下移动一行 |
boolean previous() | 游标从当前位置向上移动一行 |
void close() | 关闭ResultSet 对象 |
int getInt(int colIndex) | 以int形式获取结果集当前行指定列号值 |
int getInt(String colLabel) | 以int形式获取结果集当前行指定列名值 |
float getFloat(int colIndex) | 以float形式获取结果集当前行指定列号值 |
float getFloat(String colLabel) | 以float形式获取结果集当前行指定列名值 |
String getString(int colIndex) | 以String 形式获取结果集当前行指定列号值 |
String getString(String colLabel) | 以String形式获取结果集当前行指定列名值 |
ResultSet executeQuery(String sql) 执行SQL查询并获取到ResultSet对象
int executeUpdate(String sql) 可以执行插入、删除、更新等操作,返回值是执行该操作所影响的行数
boolean execute(String sql) 可以执行任意SQL语句,然后获得一个布尔值,表示是否返回ResultSet
更新:/,修改删除
package cn.kgc.tyl.test1020;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class UpdateStatement {
public static void main(String[] args) throws SQLException {
Connection con=null;
Statement stm=null;
try {
// 1.加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2.获得连接
con=DriverManager.getConnection("jdbc:mysql://localhost:3306/kgc?serverTimezone=UTC", "root", "123456");
if (con!=null) {
System.out.println("连接成功");
}
// 3.写sql语句并编译【先写SQL语句再执行可能出现SQL注入的问题,不安全;】编译必须用编译对象接收,否则空指针异常
String sql3="update student set student_name= '远哥' where student_num=3";
//
stm = con.createStatement();
int count=stm.executeUpdate(sql3);//返回为int类型
if (count>0) {
System.out.println("修改成功");
}else {
System.out.println("修改失败");
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if (stm!=null) {
stm.close();
}
if (con!=null) {
con.close();
}
}
}
}
/更新成功/
查询
package cn.kgc.tyl.test1020;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class SelectStatement {
public static void main(String[] args) throws SQLException {
Connection con=null;
Statement stm=null;
ResultSet rs=null;
try {
// 1.加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2.获得连接
con=DriverManager.getConnection("jdbc:mysql://localhost:3306/kgc?serverTimezone=UTC", "root", "123456");
if (con!=null) {
System.out.println("连接成功");
}
// 3.写sql语句并编译【先写SQL语句再执行可能出现SQL注入的问题,不安全;】
String sql1="select * from student";
stm= con.createStatement();
// 4.用结果集接收执行的sql语句并输出
rs=stm.executeQuery(sql1);
while (rs.next()) {
System.out.println(rs.getInt("student_num")+"姓名"+rs.getString("student_name"));//com.mysql.cj.jdbc.result.ResultSetImpl@6366ebe0
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if (rs!=null) {
rs.close();
}
if (stm!=null) {
stm.close();
}
if (con!=null) {
con.close();
}
}
}
}
/
连接成功
1姓名远哥
2姓名远哥
3姓名Lucy
4姓名韩文
5姓名果果
6姓名狂龙
7姓名赵1
8姓名赵2
9姓名赵3
10姓名赵4
11姓名赵5
12姓名赵6
14姓名赵14
15姓名赵15
20000姓名赵16
17姓名赵17
18姓名赵18
19姓名赵19
20姓名赵20
21姓名赵21
22姓名赵22
24姓名赵24
25姓名赵李
72姓名赵李李1
73姓名赵李1
74姓名李赵1
75姓名李1
63姓名赵25
26姓名赵26
27姓名赵27
28姓名赵28
29姓名赵29
30姓名赵30
31姓名赵31
32姓名赵32
33姓名赵33
34姓名赵34
35姓名赵35
36姓名赵36
37姓名赵37
38姓名赵38
39姓名赵39
48姓名赵40
10000姓名张三
11000姓名张非
/
1、预编译之后的 SQL 多数情况下可以直接执行,DBMS 不需要再次编译。
2、越复杂的SQL,编译的复杂度将越大,预编译阶段可以合并多次操作为一个操作。
3、相同的预编译 SQL 可以重复利用。(把一个 SQL 预编译后产生的 PreparedStatement 对象缓存下来,
下次对于同一个 SQL,可以直接使用这个缓存的 PreparedState 对象。)
4、可以将这类SQL语句中的值用占位符替代,不需要每次编译,可以直接执行,
只需执行的时候,直接将每次请求的不同的值设置到占位符的位置。
5、预编译可以视为将sql语句模板化或者说参数化。
因为不同的数据库会有不同的实现,对数据库的操作一般抽取成接口,在以后的开发中可以降低耦合
【stm主要好处安全,防止非法sQL注入;编译一次,可返回执行多次,效率高】
1.Statement由方法createStatement()创建,该对象用于发送简单的SQL语句
PreparedStatement由方法prepareStatement()创建,该对象用于发送带有一个或者多个输入参数的SQL语句
SQL语句使用“?”作为数据占位符
使用setXxx()方法设置数据
PreparedStatement—预编译
2.效率、性能、开销
3.安全性
4代码可读性
5-4三大对象(接口)
① 连接
Connection
② 命令
父父接口Statement 不推荐使用
父接口PreparedStatement 广泛使用
1。年月日时 String starttime=input.next();
2.年月日时分秒时格式为Sting endTime=input.nextLine(); //中间有空格,不会换行,
3.为日期赋值时当插入当前时间时外层new java.util.Date,内层new java.sql.Date.然后调用getTime()可以获得当前年月日【时分秒默认都是0】
package cn.kgc.tangcco.test1021;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Scanner;
import com.mysql.cj.util.Util;
public class InsertNowTime {
public static void main(String[] args) throws SQLException {
//声明接口和手动输入关键字;
Connection con=null;
ResultSet rs=null;
PreparedStatement stm=null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
// 2.获得连接
con=DriverManager.getConnection("jdbc:mysql:///kgc", "root", "123456");
// 3.编写sql语句并预编译,执行sql
String sql=" insert into student (student_num,student_name,modify_date ) values (?,?,?)";
//
stm=con.prepareStatement(sql);
// 外部导入java.sql.Date.对象;里面导入java.util.Date.对象;获得当前时间;
stm.setInt(1, 299);
stm.setString(2, "光头强2");
stm.setDate(3, new Date(new java.util.Date().getTime()) );//只能返回年月日
// 4.接收预编译对象的数据并判断是否执行成功
int i=stm.executeUpdate();
if (i>0) {
System.out.println("修改成功");
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if (rs!=null) {
rs.close();
}
if (stm!=null) {
stm.close();
}
if (con!=null) {
con.close();
}
}
}
}
/
48
/
③ 结果
增删改:int的行数
查询:表-> ResultSet接口
package cn.kgc.tangcco.test1021;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Scanner;
public class MoHuChaXun {
public static void main(String[] args) throws SQLException {
//声明接口和手动输入关键字;
Connection con=null;
ResultSet rs=null;
PreparedStatement stm=null;
Scanner input = new Scanner(System.in);
System.out.println("请输入要查找的信息");
String student_num=input.nextLine();//声明模糊查询的变量
student_num="%"+student_num+"%";
try {
Class.forName("com.mysql.cj.jdbc.Driver");
// 2.获得连接
con=DriverManager.getConnection("jdbc:mysql:///kgc", "root", "123456");
// 3.编写sql语句并预编译,执行
String sql="select from student where student_name like ?";
stm=con.prepareStatement(sql);
stm.setString(1, student_num);
// 4.接收预编译对象的数据并遍历
rs=stm.executeQuery();
while (rs.next()) {
System.out.println("学号t学生姓名t密码t性别");
System.out.println(rs.getInt(1)+"t"+rs.getString(2)+"t"+rs.getString(3)+"t"+rs.getString(4));
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if (rs!=null) {
rs.close();
}
if (stm!=null) {
stm.close();
}
if (con!=null) {
con.close();
}
}
}
}
/
请输入要查找的信息
福
学号 学生姓名 密码 性别
100 22 孙福祥 null
/
packagecn.kgc.tyl.test1020;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class SelectPreparedStatement {
public static void main(String[] args) throws SQLException {
PreparedStatement stm=null;
Connection con=null;
PreparedStatement stm2=null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");//1.加载驱动
con=DriverManager.getConnection("jdbc:mysql:///kgc","root","123456");//2.建立连接
String sql = "insert into student (student_num,student_name)values(?,?)";//3.获得预编译对象并给预编译对象赋值
String sq2="delete from student where student_name=?";
stm = con.prepareStatement(sql);
stm.setInt(1, 110);
stm.setString(2,"范永光");
stm.executeUpdate();//4.执行更新SQL语句
stm2=con.prepareStatement(sq2);
stm2.setString(1, "远哥");
stm2.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}finally {
if (stm!=null) {
stm.close();
}
if (con!=null) {
con.close();
}
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nFrdycSv-1604303154966)(JDBC/image-20201021173111564.png)]
package cn.kgc.tyl.test1020;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class UpdatePareparedStatement {
public static void main(String[] args) throws SQLException {
PreparedStatement stm=null;
Connection con=null;
try {
// 1.加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");//先mysql再jdbc
// 2.获得连接
con=DriverManager.getConnection("jdbc:mysql:///kgc","root","123456");//先jdbc再mysql
// 3.进行预编译,并给预编译对象赋值;
String sq="update student set student_name=? where student_num=?";
stm=con.prepareStatement(sq);
stm.setString(1, "范增");
stm.setInt(2, 10);
// 4.执行
int i=stm.executeUpdate();
if (i>0) {
System.out.println("更新成功;");
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if (stm!=null) {
stm.close();
}
if (con!=null) {
con.close();
}
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xz5d9MAw-1604303154968)(JDBC/image-20201022073142446.png)]
mybatis 在调用 connection 进行 sql 预编译之前,会对sql语句进行动态解析,动态解析主要包含如下的功能:
注: mybatis 默认情况下,将对所有的 sql 进行预编译。
【封装工具】
封装把公共的属性方法进行封装,用静态 的类只要加载,就可以执行;
1.三个接口变量,增删改的方法;获得连接方法;释放资源方法【当关闭资源时,有多余的参数用null传递,比如查询方法三个变量
,通用增删改第三个变量就只需要传递为null就可以了】
2.封装时,返回对象尽量用Integer或者Long【数据量上千万条时】。是对象,数量可以为null;
3.可变长数组Object…objects可以代表0-多个对象;
4.Dao包按照实体类分为多个包,【还可以细分为用户模块,订单模块】比如student,teacher然后在各个dao的子包中建立接口类和具体实现类比如
/demo/src/cn/kgc/tangcco/test1022/dao/teacher/impl/TeacherDaoImpl.java;
/demo/src/cn/kgc/tangcco/test1022/dao/student/impl/StudentDaoImpl.java
5.输出语句时,先输出结果,再输出原因【登录失败,密码或用户不正确】
6.dao层连接实体类和工具类,包的作用时安全性和分门别类,把具有不同共同的类放在不同的包中;
7.工具类中方法尽量用trycatch包裹声明异常,否则在在工具类中thorows抛出异常就必须在daoimpl中声明处理异常;那么daoimpl中格式不美观
进行JDBC封装原因:
采用面向接口编程,可以降低代码间的耦合性
具体有三个好处
1.增加可读性;
2.利于代码复用
3.利于后期修改和维护:简化书写
DAO模式优势:隔离了数据访问代码和业务逻辑代码;隔离了不同数据库实现
面向接口编程:只关心类实现的功能,不关心具体实现过程,【实现数据无损替换】
持久化数据存储在dao层,DB【用户请求到右边,DB端dao层返回数据到界面】
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W81zUzSA-1604303154968)(JDBC/image-20201022135451856.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ublkuO6h-1604303154969)(JDBC/image-20201020215752147.png)]
为解决业务代码和数据访问代码的紧耦合给修改和维护代码带来的不便,推荐使用DAO模式封装JDBC
Java中的配置文件常为properties文件
后缀为.properties
格式是“键=值”格式
使用“#”来注释
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql:///kgc
username=root
password=123666
储在MySQL数据库中,但在开发和部署时有可能使用不同的数据库,也可能因为客户的需求而更换数据库产品。
弊端数据库发生改变时,要重新修改代码,重新编译和部署
解决将数据库信息写在配置文件当中,让程序通过读取配置文件来获得这些信息
ResourceBundle rb=ResourceBundle.getBundle(“db”);//直接获得db.properties文件对象;
方法名 | 说 明 |
---|---|
String getProperty(String key) | 用指定的键在此属性列表中搜索属性。通过参数key得到其所对应的值 |
Object setProperty(String key,String value) | 调用Hashtable的方法put。通过调用基类的put()方法来设置键-值对 |
void load(InputStream inStream) | 从输入流中读取属性列表 (键和元素对)。通过对指定文件进行装载获取该文件中所有键-值对 |
void clear() | 清除所装载的键-值对,该方法由基类Hashtable提供 |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lJVKsCOK-1604303154970)(JDBC/image-20201020221812503.png)]
6-3-2抽取一个连接对象
需求:不想传递参数(麻烦),还得保证工具类的通用性。
解决:配置文件
jdbc.properties
url=
user=
password=
代码实现:
public class JDBCUtils {
//封装驱动和连接资源的参数【文件的读取,只需要读取一次即可拿到这些值。使用静态代码块】
private static String url, user, password,driver;
static{
//读取资源文件,获取值。
try {
//1. 创建Properties集合类。
Properties pro = new Properties();
//获取src路径下的文件的方式--->ClassLoader 类加载器
ClassLoader classLoader = JDBCUtils.class.getClassLoader();【通过class对象获得字节码加载器对象】
URL res = classLoader.getResource("jdbc.properties");
//inputStream in=classloader.getResourceAsStream("jdbc.properties");【通过类加载方法获得输入流】
String path = res.getPath();
System.out.println(path);///D:/IdeaProjects/hou/out/production/day04_jdbc/jdbc.properties
//2. 加载文件
//pro.load(in);
// pro.load(new FileReader("D:IdeaProjectshouday04_jdbcsrcjdbc.properties"));
pro.load(new FileReader(path));
//3. 获取数据,赋值
url = pro.getProperty("url");
user = pro.getProperty("user");
password = pro.getProperty("password");
driver = pro.getProperty("driver");
//4. 注册驱动
Class.forName(driver);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//M1获取连接
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, user, password);
}
//M2增删改释放资源:
public static void close(Statement stmt,Connection conn)throws Exception{
if( stmt != null){ stmt.close(); }
if( conn != null){ conn.close(); }
}
//M3.查询释放资源
public static void close(ResultSet rs,Statement stmt, Connection conn){
if( rs != null){ rs.close(); }
if( stmt != null){ stmt.close();}
if( conn != null){ conn.close();}
}
}
}
通过键盘录入用户名和密码
判断用户是否登录成功
select from user where username = “” and password = “”;
如果这个sql有查询结果,则成功,反之,则失败
步骤:
1. 创建数据库表 user
CREATE TABLE USER(
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(32),
PASSWORD VARCHAR(32)
);
INSERT INTO USER VALUES(NULL,'zhangsan','123');
INSERT INTO USER VALUES(NULL,'lisi','234');
2. 代码实现:
public class JDBCDemo9 {
public static void main(String[] args) {
//1.键盘录入,接受用户名和密码
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = sc.nextLine();
System.out.println("请输入密码:");
String password = sc.nextLine();
//2.调用方法
boolean flag = new JDBCDemo9().login(username, password);
//3.判断结果,输出不同语句
if(flag){
//登录成功
System.out.println("登录成功!");
}else{
System.out.println("用户名或密码错误!");
}
}
public boolean login(String username ,String password){
if(username == null || password == null){
return false;
}
//连接数据库判断是否登录成功
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
//1.获取连接
try {
conn = JDBCUtils.getConnection();
//2.定义sql
String sql = "select from user where username = '"+username+"' and password = '"+password+"' ";
//3.获取执行sql的对象
stmt = conn.createStatement();
//4.执行查询
rs = stmt.executeQuery(sql);
//5.判断
/ if(rs.next()){//如果有下一行,则返回true
return true;
}else{
rturn false;
}/
return rs.next();//如果有下一行,则返回true
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.close(rs,stmt,conn);
}
return false;
}
}
事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。
操作: 1. 开启事务 2. 提交事务 3. 回滚事务
使用Connection对象来管理事务
开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
在执行sql之前开启事务 提交事务:commit() 当所有sql都执行完提交事务
回滚事务:rollback() 在catch中回滚事务
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-065NwI33-1604303154970)(JDBC/image-20201020231327127.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hyGMHKt1-1604303154971)(JDBC/image-20201021184000064.png)]
Connection conn = null;
PreparedStatement pstmt1 = null;
PreparedStatement pstmt2 = null;
try {
//1.获取连接
conn = JDBCUtils.getConnection();
//开启事务
conn.setAutoCommit(false);
//2.定义sql
//2.1 张三 - 500
String sql1 = "update account set balance = balance - ? where id = ?";
//2.2 李四 + 500
String sql2 = "update account set balance = balance + ? where id = ?";
//3.获取执行sql对象
pstmt1 = conn.prepareStatement(sql1);
pstmt2 = conn.prepareStatement(sql2);
//4. 设置参数
pstmt1.setDouble(1,500);
pstmt1.setInt(2,1);
pstmt2.setDouble(1,500);
pstmt2.setInt(2,2);
//5.执行sql
pstmt1.executeUpdate();
// 手动制造异常
int i = 3/0;
pstmt2.executeUpdate();
//提交事务
conn.commit();
} catch (Exception e) {
//事务回滚
try {
if(conn != null) {
conn.rollback();
}
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally {
JDBCUtils.close(pstmt1,conn);
JDBCUtils.close(pstmt2,null);
}
}
}
.数据库连接池负责分配**,【管理和释放】数据库连接,**它允许应用程序【重复使用】一个现有的
数据库连接**,**而不是重新建立一个。
数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中, 这些数据库连接的数量是由最小数
据库连接数来设定的.无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量.
连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数
超过最大连接数量时,这些请求将被加入到【等待队列】中.
数据库连接池的最小连接数和最大连接数的设置要考虑到以下几个因素:
\1. 最小连接数:是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有
大量的数据库连接资源被浪费.
\2. 最大连接数:是连接池能申请的最大连接数,如果数据库连接请求超过次数,后面的数据库连接请求将
被加入到等待队列中,这会影响【以后的数据库操作】
\3. 如果最小连接数与最大连接数相差很大:那么最先连接请求将会获利,之后超过最小连接数量的连接
请求等价于建立一个新的数据库连接.不过,这些大于最小连接数的数据库连接在使用完【不会马上被
释放】,他将被放到连接池中等待【重复使用或是空间超时后】被释放.
JDBC : JDBC Template
数据库连接池
当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器。
好处:1. 节约资源2. 用户访问高效
实现:
标准接口:DataSource javax.sql包下的
方法:
一般我们不去实现它,有数据库厂商来实现
C3P0:数据库连接池技术
Druid:数据库连接池实现技术,由阿里巴巴提供的
步骤:
不要忘记导入数据库驱动jar包java .connector.jar
名称: c3p0.properties 或者 c3p0-config.xml
路径:直接将文件放在src目录下即可。
创建核心对象 数据库连接池对象 ComboPooledDataSource
获取连接: getConnection
//1.创建数据库连接池对象
DataSource ds = new ComboPooledDataSource();
//2. 获取连接对象
Connection conn = ds.getConnection();
步骤:
导入jar包 druid-1.0.9.jar
定义配置文件:
是properties形式的 可以叫任意名称,可以放在任意目录下3. 加载配置文件。Properties
//3.加载配置文件
Properties pro = new Properties();
InputStream is = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
//4.获取连接池对象
DataSource ds = DruidDataSourceFactory.createDataSource(pro);
//5.获取连接
Connection conn = ds.getConnection();
提供静态代码块加载配置文件,初始化连接池对象
提供方法
获取连接方法:通过数据库连接池获取连接
释放资源
获取连接池的方法
public class JDBCUtils { //1.定义成员变量 DataSource
private static DataSource ds ;
static{
try { //1.加载配置文件
Properties pro = new Properties(); pro.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
//2.获取DataSource
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (Exception e) {
e.printStackTrace();
}
}
M1:获取连接
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
//M2增删改释放资源:
public static void close(Statement stmt,Connection conn)throws Exception{
if( stmt != null){ stmt.close(); }
if( conn != null){ conn.close(); }
}
//M3.查询释放资源
public static void close(ResultSet rs,Statement stmt, Connection conn){
if( rs != null){ rs.close(); }
if( stmt != null){ stmt.close();}
if( conn != null){ conn.close();}//归还连接
}
//M4 获取连接池方法
public static DataSource getDataSource(){ return ds; }
}
Spring框架对JDBC的简单封装。提供了一个JDBCTemplate对象简化JDBC的开发
步骤:
导入jar包
创建JdbcTemplate对象。依赖于数据源DataSource
JdbcTemplate template = new JdbcTemplate(ds);
update():执行DML语句。增、删、改语句
注意:这个方法查询的结果集长度只能是1
注意:将每一条记录封装为一个Map集合,再将Map集合装载到List集合中
query():查询结果,将结果封装为JavaBean对象
query的参数:RowMapper
一般我们使用BeanPropertyRowMapper实现类。可以完成数据到JavaBean的自动封装
new BeanPropertyRowMapper<类型>(类型.class)
queryForObject:查询结果,将结果封装为对象
一般用于聚合函数的查询
需求:1. 修改1号数据的 salary 为 10000
添加一条记录
删除刚才添加的记录
查询id为1的记录,将其封装为Map集合
查询所有记录,将其封装为List
查询所有记录,将其封装为Emp对象的List集合
查询总记录数
import cn.hou.domain.Emp;
import cn.hou.utils.JDBCUtils;
import org.junit.Test;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
public class JdbcTemplateDemo2 {
//Junit单元测试,可以让方法独立执行
//. 获取JDBCTemplate对象
private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
1. 修改1号数据的 salary 为 10000
@Test
public void test1(){//提供SQL语句并执行【更改比较特殊】
String sql = "update emp set salary = 10000 where id = 1001";
int count = template.update(sql);
System.out.println(count);
}
// 2. 添加一条记录
@Test
public void test2(){
String sql = "insert into emp(id,ename,dept_id) values(?,?,?)";
int count = template.update(sql, 1015, "郭靖", 10);
System.out.println(count);
}
//3.删除刚才添加的记录
@Test
public void test3(){
String sql = "delete from emp where id = ?";
int count = template.update(sql, 1015);
System.out.println(count);
}
//
4.查询id为1001的记录,将其封装为Map集合
注意:这个方法查询的结果集长度只能是1
@Test
public void test4(){
String sql = "select from emp where id = ? or id = ?";
Map<String, Object> map = template.queryForMap(sql, 1001,1002);
System.out.println(map);
//输出结果为 {id=1001, ename=孙悟空, job_id=4, mgr=1004, joindate=2000-12-17, salary=10000.00, bonus=null, dept_id=20}
}
// 5. 查询所有记录,将其封装为List
@Test
public void test5(){
String sql = "select from emp";
List<Map<String, Object>> list = template.queryForList(sql);
for (Map<String, Object> stringObjectMap : list) {
System.out.println(stringObjectMap);
}
}
//6. 查询所有记录,将其封装为Emp对象的List集合
@Test
public void test6(){
String sql = "select from emp";
List<Emp> list = template.query(sql, new RowMapper<Emp>() {
@Override
public Emp mapRow(ResultSet rs, int i) throws SQLException {
Emp emp = new Emp();
int id = rs.getInt("id");
String ename = rs.getString("ename");
int job_id = rs.getInt("job_id");
int mgr = rs.getInt("mgr");
Date joindate = rs.getDate("joindate");
double salary = rs.getDouble("salary");
double bonus = rs.getDouble("bonus");
int dept_id = rs.getInt("dept_id");
emp.setId(id);
emp.setEname(ename);
emp.setJob_id(job_id);
emp.setMgr(mgr);
emp.setJoindate(joindate);
emp.setSalary(salary);
emp.setBonus(bonus);
emp.setDept_id(dept_id);
return emp;
}
});
for (Emp emp : list) {
System.out.println(emp);
}
}
//6. 查询所有记录,将其封装为Emp对象的List集合
@Test
public void test6_2(){
String sql = "select from emp";
List<Emp> list = template.query(sql, new BeanPropertyRowMapper<Emp>(Emp.class));
for (Emp emp : list) {
System.out.println(emp);
}
}
// 7. 【查询总记录数】
@Test
public void test7(){
String sql = "select count(id) from emp";
Long total = template.queryForObject(sql, Long.class);
System.out.println(total);
}
}
3.1 创建一个类customer
public class Customer {
private int id;
private String name;
private String email;
private Date birth;
public Customer() {
super();
}
public Customer(int id, String name, String email, Date birth) {
super();
this.id = id;
this.name = name;
this.email = email;
this.birth = birth;
}
3.2 创建jdbc连接,并封装为方法
// 获取数据库的连接
public static Connection getConnection() {
Connection connection = null;
InputStream is = null;
try {
is = JDBCUtils.class.getClassLoader().getResourceAsStream("sqlDriver.properties");
Properties ps = new Properties();
ps.load(is);
String user = ps.getProperty("user");
String password = ps.getProperty("password");
String driverClass = ps.getProperty("driverClass");
String url = ps.getProperty("url");
// 创建Driver对象
Class clazz = Class.forName(driverClass);
Object obj = clazz.newInstance();
Driver driver = (Driver) obj;
// 注册驱动
DriverManager.registerDriver(driver);
connection = DriverManager.getConnection(url, user, password);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return connection;
}
3.3 在操作完成后要进行连接的关闭
public static void close(Connection connection, PreparedStatement ps) {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(Connection connection, PreparedStatement ps, ResultSet rs) {
close(connection,ps);
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
3.4 增删查改操作
// 对数据库的 增 ,删,改,查
public class CRUDTest {
// 从数据库中查找一条数据
@Test
public void select() throws Exception {
Customer customer = getCustomerById();
System.out.println(customer);
List<Customer> customers = getCustomers();
for (Customer customer2 : customers) {
System.out.println(customer2);
}
}
// 查询多条数据
public List<Customer> getCustomers() throws Exception {
Connection connection = JDBCUtils.getConnection();
String sql = "select from customers";
// 2.预编译 -- 如果没有占位符可以不填充数据
PreparedStatement ps = connection.prepareStatement(sql);
// 4.执行sql语句
ResultSet rs = ps.executeQuery(); // 查找的结果都已经放到ResultSet中了
List<Customer> list = new ArrayList<>();
while(rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name");
String email = rs.getString("email");
Date birth = rs.getDate("birth");
//将每条数据放入集合中
list.add(new Customer(id, name, email, birth));
}
// 6.关闭资源
JDBCUtils.close(connection, ps, rs);
return list;
}
//查询一条数据
public Customer getCustomerById() throws Exception {
Connection connection = JDBCUtils.getConnection();
String sql = "select from customers where id = ?";
// 2.预编译
PreparedStatement ps = connection.prepareStatement(sql);
// 3.填充数据
ps.setInt(1, 20);
// 4.执行sql语句
ResultSet rs = ps.executeQuery(); // 查找的结果都已经放到ResultSet中了
Customer customer = null;
// 5.取数据【根据下标或者字符段名称】
if (rs.next()) { // 如果有数据就返回true,根据列的索引获取对应的列上的值
// int id = rs.getInt(1);
// String name = rs.getString(2);
// String email = rs.getString(3);
// Date date = rs.getDate(4);
// 获取列上值的第二种方式
int id = rs.getInt("id");
String name = rs.getString("name");
String email = rs.getString("email");
Date birth = rs.getDate("birth");
【// 考虑将数据进行封装】
customer = new Customer(id, name, email, birth);
// 输出获取到的内容
System.out.println(id + " " + name + " " + email + " " + birth);
}
// 6.关闭资源
JDBCUtils.close(connection, ps, rs);
return customer;
}
//向数据库中插入一条数据
@Test
public void insert() {
//多个方法作为局部变量单独声明
Connection connection = null;
PreparedStatement ps = null;
try {
// 1 : 连接数据库
connection = JDBCUtils.getConnection();
// sql语句
String sql = "INSERT INTO customers(NAME,email,birth) " + "VALUES(?,?,?);";
// 2.预编译
ps = connection.prepareStatement(sql);
// 3.设置内容
ps.setString(1, "强哥"); // 第一个参数:第几个问号 第二个参数:内容
ps.setString(2, "[email protected]");
// 获取一个sql下的Date
Date date = new Date(new java.util.Date().getTime());
ps.setDate(3, date);
// 4.执行sql语句
ps.execute(); // 如果是查询语句返回true
//或者int i=ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 5.关闭资源
JDBCUtils.close(connection, ps);
}
}
//删除一条件数
@Test
public void delete() throws Exception {
// 1. 获取数据库的连接
Connection connection = JDBCUtils.getConnection();
String sql = "DELETE FROM customers WHERE id = ?";
// 2. 预编译
PreparedStatement ps = connection.prepareStatement(sql);
// 3.填充数据
ps.setInt(1, 19);
// 4.执行sql语句
int executeUpdate = ps.executeUpdate();
System.out.println(executeUpdate + "条受到影响");
// 5.关闭资源
JDBCUtils.close(connection, ps);
}
//修改一条件数
@Test
public void update() throws Exception {
Connection connection = null;
PreparedStatement ps = null;
try {
connection = JDBCUtils.getConnection();
String sql = "UPDATE customers SET NAME = ? WHERE id = ?";
ps = connection.prepareStatement(sql);
ps.setString(1, "成哥");
ps.setInt(2, 18);
int executeUpdate = ps.executeUpdate();
System.out.println(executeUpdate + "条数据受到影响");
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.close(connection, ps);
}
}
}
2、rs.getMetaData().getColumnCount()字段数
3、rs.getMetaData().getColumnName(i));字段名
创建类 User.java
public class User {
private int id;
private String name;
private String address;
private String phone;
4.1 创建通用连接方案 - 通用增删改
// 通用的增删改
public static int UID(String sql,Object ...objects) {
Connection connection = null;
PreparedStatement ps = null;
int executeUpdate = -1;
try {
connection = JDBCUtils.getConnection();
ps = connection.prepareStatement(sql);
for (int i = 0; i < objects.length; i++) { // i 表示索引
ps.setObject(i + 1, objects[i]); // i + 1表示的是第几个占位符
}
executeUpdate = ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}finally{
JDBCUtils.close(connection, ps);
}
return executeUpdate;
}
测试代码:
// 测试通用的增删改
@Test
public void insert(){
String sql = "INSERT INTO customers(NAME,email,birth) VALUES(?,?,?)";
Date date = new java.sql.Date(new java.util.Date().getTime());
int uid = JDBCUtils.UID(sql, "强哥","[email protected]",date);
System.out.println(uid + "条数据受到影响");
}
4.2 通用查找(一条或多条)
只查一条数据:
// 通用的查找 (只有一条数据)
Class<T> clazz : 运行时类的对象
public static <T> T getObject(Class<T> clazz,String sql,Object ...objects) throws Exception{
Connection connection = JDBCUtils.getConnection();
PreparedStatement ps = connection.prepareStatement(sql);
for (int i = 0; i < objects.length; i++) {
ps.setObject(i + 1, objects[i]);
}
//4.执行sql语句
ResultSet rs = ps.executeQuery();
//获取列的数量
int columnCount = rs.getMetaData().getColumnCount();
//创建对象
T t = clazz.newInstance();
while(rs.next()){ //遍历每一条数据
for (int i = 0; i < columnCount; i++) {
//获取某列的列名(如果有别名获取的是别名)
String columnLabel = rs.getMetaData().getColumnLabel(i + 1)
//通过列名获取列中的数据。
Object value = rs.getObject(rs.getMetaData().getColumnLabel(i + 1));
//封装
//通过反射 - 给对象中的属性进行赋值
//将表中的列名当作类中的属性名。如果列名和属性名不一样,可以通过别名的方式(别名 = 属性名)
//通过列名就获取到了类中的对应的属性
Field declaredField = clazz.getDeclaredField(columnLabel);
declaredField.setAccessible(true);
// 第一个参数 : 是给哪个对象进行赋值
// 第二个参数 : 属性值
declaredField.set(t, value);
}
}
//关闭资源
BCUtils.close(connection, ps, rs);
return t;
}
查多条数据:
// 通用的查找 (返回多条数据)
public static <T> List<T> getObjects(Class<T> clazz,String sql,Object ...objects) throws Exception{
//1.获取数据库的连接
Connection connection = JDBCUtils.getConnection();
//2.预编译
PreparedStatement ps = connection.prepareStatement(sql);
//3.填充数据
for (int i = 0; i < objects.length; i++) {
ps.setObject(i + 1, objects[i]);
}
//4.执行sql语句
ResultSet rs = ps.executeQuery();
ResultSetMetaData md = rs.getMetaData(); //获取元数据。
//获取列的数量
int columnCount = md.getColumnCount();
//创建一个集合
List<T> list = new ArrayList<>();
while(rs.next()){ //遍历每一条数据
//创建对象
T t = clazz.newInstance();
// 第一个问题 : 如何获取到表的列数
// 第二个问题 : 需要知道类中的属性
// 第三个问题 : 对象中属性的名字怎么来
for (int i = 0; i < columnCount; i++) {
//获取某列的列名(如果有别名获取的是别名)
String columnLabel = md.getColumnLabel(i + 1);
//通过列名获取列中的数据。
Object value = rs.getObject(columnLabel);
//封装
//通过反射 - 给对象中的属性进行赋值
//将表中的列名当作类中的属性名。如果列名和属性名不一样,可以通过别名的方式(别名 = 属性名)
//通过列名就获取到了类中的对应的属性
Field declaredField = clazz.getDeclaredField(columnLabel);
declaredField.setAccessible(true);
第一个参数 : 是给哪个对象进行赋值
第二个参数 : 属性值
declaredField.set(t, value);
}
//将对象放入到集合中
list.add(t);
}
//关闭资源
JDBCUtils.close(connection, ps, rs);
return list;
}
测试代码:
String sql = "select id,name,email,birth from customers where id = ?";
Customer customer = getObject(Customer.class, sql, 5);
System.out.println(customer);
sql = "select from users where id = ?";
User object = getObject(User.class, sql, 1);
System.out.println(object);
sql = "select from users";
List<User> users = getObjects(User.class, sql);
for (User user : users) {
System.out.println(user);
}
系统运行期间,有且仅有一个实例
一个类只有一个实例——最基本的要求
只提供私有构造器;它必须自行创建这个实例
定义了静态的该类私有对象;它必须自行向整个系统提供这个实例
提供一个静态的公有方法,返回创建或者获取本身的静态私有对象
若在并发,它严重的弊端,线程不安全,很有可能会出现多个configManager实例,所以在实际开发应用中并不会使用上述方式来实现单例
单例模式 | 懒汉模式 | 饿汉模式 |
---|---|---|
概念 | 在类加载时不创建实例,采用延迟加载的方式,在运行调用时创建实例 | 在类加载的时候,就完成初始化 |
特点 | 类加载速度快,但是运行时获取对象的速度较慢。——“时间换空间” | 类加载较慢,但获取对象速度快。——“空间换时间” |
延迟加载(lazy loa ding) | 具备 | 不具备 |
线程安全 | 线程不安全 | 线程安全 |
通过预编译对象查询到用户名是否存在,然后通过结果集获得密码,如果密码匹配则登录成功。否则密码不正确,登陆失败
13-1流程图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-laWMR7Kg-1604303154971)(JDBC/image-20201022094143479.png)]
package cn.kgc.tangcco.test1021;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Scanner;
public class TestLogin {
public static void main(String[] args) throws SQLException {
Scanner input = new Scanner(System.in);
System.out.println("请输入用户名");
String userName=input.next();
System.out.println("请输入密码");
String userPwd=input.next();
Connection con=null;
PreparedStatement stm=null;
ResultSet rs=null;
try {
// 1.连接驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2.获得连接
con=DriverManager.getConnection("jdbc:mysql:///kgc?useUnicode=true&characterEncoding=utf-8", "root", "123456");
// 3.写sql语句,预编译,并执行sql语句
String sql="select from student where student_name=?";
stm=con.prepareStatement(sql);
stm.setString(1, userName);
rs=stm.executeQuery();
// 4.执行;
boolean existsUserName=false;
String pwd=null;
while (rs.next()) {
existsUserName=true;
pwd=rs.getString("login_password");
}
if (existsUserName) {
if (pwd.equals(userPwd)) {
System.out.println("登录成功,欢迎"+userName);
}else {
System.out.println("密码或账号不正确");
}
}else {
System.out.println("密码或账号不正确");
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if (rs!=null) {
rs.close();
}
if (stm!=null) {
stm.close();
}
if (con!=null) {
con.close();
}
}
}
}
/
请输入用户名
孙福祥
请输入密码
22
登录成功,欢迎孙福祥
/
随着互联网的发展,给我们的生活带来便利的同时,也伴随着很多网络钓鱼、信息泄露、网络诈骗等事件的频繁发生,企业网站被钓鱼网站仿冒,遭受经济损失,影响品牌形象。
如果网站不使用SSL证书,数据以HTTP明文传输,极容易被第三方监听、截取和篡改,而导致用户信息被泄露,给在线用户带来直接损失。
而部署SSL证书后能确保信息传输的安全性,可防止信息泄露。
SSL(Secure Sockets Layer安全套接字协议),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS与SSL在传输层与应用层之间对网络连接进行加密。
SSL证书是数字证书(数字证书包括:SSL证书、客户端证书、代码签名证书等)的一种,因为配置在服务器上也称为服务器SSL证书。SSL证书就是遵守SSL协议,由受信任的数字证书颁发机构CA(如:沃通CA)在验证服务器身份后颁发的一种数字证书。
使用SSL证书的优势:
· SSL证书用于实现数据加密传输,防止数据被泄露和篡改
· 用于认证服务器身份,防范被钓鱼网站攻击
· 用于消除浏览器地址栏发出的“不安全”的警告
· 提高SEO搜索引擎排名
· 提升用户对网站的信任
· 有助于提高网站的在线销售业绩
网站加密算法RSA,DSA,ECDSA;
HTTPS (全称:Hyper Text Transfer Protocol over SecureSocket Layer),是以安全为目标的 HTTP 通道,在HTTP的基础上通过传输加密和身份认证保证了传输过程的安全性。HTTPS 在HTTP 的基础下加入SSL层,HTTPS的安全基础是 SSL,因此加密的详细内容就需要 SSL。 HTTPS 存在不同于 HTTP 的默认端口及一个加密/身份验证层(在 HTTP与TCP之间)。这个系统提供了身份验证与加密通讯方法。
网站使用HTTPS的原因有很多:
· HTTPS有助于在服务器和浏览器之间建立安全通信
· 它可以保护网站免受篡改或窃听
· 它可以保护用户免受中间人攻击
· 各大主流浏览器纷纷要求网站从HTTP升级HTTPS访问
· 它广泛地被银行、医疗、电子商务、社交媒体和政府等行业使用
三、SSL证书申请
安全可信的SSL证书需要向CA机构(证书授权颁发中心)申请,通过严格的审查后才给予颁发。
如果是个人网站,建议选择DVSSL证书。申请者只需要验证域名管理权,10-50分钟颁发,无需递交纸质文件,也不需要确认申请者单位身份,是一种非常经济的 SSL证书!
如果是中小型企业网站、电子商务网站,建议选择OVSSL证书。申请者需要通过证书颁发机构的审核。审核内容包括企业身份和域名所有权,以证明申请单位是一个合法存在的真实主体,CA机构将在人工核实后签发证书。
EVSSL 证书具有最高级别可信度及安全性,需要通过极其严格的审核。审核内容包括企业身份,地址,电话等信息的真实性,以及域名所有权,确保网站身份的真实可靠,是访问者最值得信赖的 SSL 证书。这种证书能在浏览器地址栏显示公司名称,从而使访问者更加放心相信他们所进行交易的网站是真实合法的,提升在线交易量。
!完成SSL证书的申请和安装流程。您可以访问SSL盾官网,选择SSL证书类型和品牌,自助申请,完成域名验证,等待审核通过即可下载SSL证书,最后安装部署到服务器上即可。【ssl证书作用个人信息,支付安全,私密信息,交易数据】
【业务逻辑层 Business Logic LayeDAL 是数据访问层 Data Access Layer用户界面(User Interface) 】
1、【实体类user】作为参数贯穿于三层之间;
2、通过传参、方法调用来实现功能;
3、各层之间各负其责;互不影响
就是为了符合“高内聚,低耦合”思想,把各个功能模块划分为表示层(UI)、业务逻辑层(BLL)和数据访问层(DAL)三层架构,各层之间采用接口相互访问,并通过对象模型的实体类(Model)作为数据传递的载体,不同的对象模型的实体类一般对应于数据库的不同表,实体类的属性与数据库表的字段名一致。 [1]
是为了 “高内聚,低耦合”。开发人员分工更明确,将精力更专注于应用系统核心业务逻辑的分析、设计和开发,加快项目的进度,提高了开发效率,有利于项目的更新和维护工
UI(表现层):【与用户交互、展示数据】 主要是指与用户交互的界面。用于接收用户输入的数据和显示处理后用户需要的数据。
界面交互设计的原则
统一性原则
界面风格统一
用相同方式展现相同类型的数据,如:日期类型
交互风格统一
用相同方式完成相同类型的操作,如:录入日期
美观性原则
界面美观大方
易用性原则
操作方式自然、易理解
BLL:(业务逻辑层): 【控制业务流程及事务】UI层和DAL层之间的桥梁。实现业务逻辑。业务逻辑具体包含:验证、计算、业务规则等等。
DAL:(数据访问层):【实现数据库操作】 与数据库打交道。主要实现对数据的增、删、改、查。将存储在数据库中的数据提交给业务层,同时将业务层处理的数据保存到数据库。(当然这些操作都是基于UI层的。用户的需求反映给界面(UI),UI反映给BLL,BLL反映给DAL,DAL进行数据的操作,操作后再一一返回,直到将用户所需数据反馈给用户
**【Entity(实体层):**它不属于三层中的任何一层,但是它是必不可少的一层。】
三层架构的优势:
三层架构的优劣势:
1、降低了系统的性能。这是不言而喻的。如果不采用分层式结构,很多业务可以直接造访数据库,以此获取相应的数据,如今却必须通过中间层来完成。
2、有时会导致级联的修改。这种修改尤其体现在自上而下的方向。如果在表示层中需要增加一个功能,为保证其设计符合分层式结构,可能需要在相应的业务逻辑层和数据访问层中都增加相应的代码
3、增加了代码量,增加了工作量
每一层都有自己的职责分层结构中,不同层之间通过【实体类】传输数据
上一层不用关心下一层的实现细节,上一层通过下一层提供的对外接口来使用其功能
上一层调用下一层的功能,下一层不能调用上一层功能
分层原则1——【顺序访问原则】下一层为上一层服务,但不使用上层的服务
分层原则2——【封装性】原则 每个层次向外【公开接口】,但是隐藏内部细节
面向接口编程:只关系实现的功能,不关心具体的实现细节
代码复用
职责清晰,分工明确,分离开发人员的关注
无损替换【工具类之间切换,比如MySQL,切换成Oracle;当前层替换】
降低了系统间的依赖利于维护扩展
dbutils 封装了driver,url,root,pwd运用的是 连接池工具,或者properies配置文件;使用了数据连接池;
不用专门关闭资源,只是归还连接;
BaseDao中工具都是私有的静态的。
1.7版本以后有个直接获得配置文件的方法,不用输出流
package cn.kgc.tangyingli.utils;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ResourceBundle;
/**
*
工具类
* */
public class DBUtil {
static Connection con= null;
static PreparedStatement stm=null;
static ResultSet rs=null;
//获得连接方法
ResourceBundle rb=ResourceBundle.getBundle("db");//直接获得db.properties文件对象;
public static Connection getConnection() {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
con=DriverManager.getConnection("jdbc:/mysql:///kgc&useSSL=true", "root", "123456");
} catch (Exception e) {
e.printStackTrace();
}
return con;
}
//关闭资源方法
public static void releaseResources(Connection con,ResultSet rs,PreparedStatement stm) {
try {
if (rs!=null) {
rs.close();
}
if (stm!=null) {
stm.close();
}
if (con!=null) {
con.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
//增删改通用方法
public static int update(String sql, Object...objects) {
con=getConnection();
int result=0;
try {
stm=con.prepareStatement(sql);
if (objects!=null) {
for (int i = 0; i < objects.length; i++) {
stm.setObject(i+1, objects[i]);
}
}
result= stm.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}finally {
releaseResources(con, null, stm);
}
return result;
}
}
封装jdbc工具时,增删改通用方法可以返回值改为布尔类型,那么,查询的方法只需要多加一句用结果集接收即可
**方法一、**eclipse 方法: 打开file–new–other 选择general–file–next 在file name后输入:文件名.properties,finish即可。
**方法二、**新建普通的文本文件,然后修改后缀为properties即可创建。
**方法三、**如果是编程实现的话,使用输出流,输出保存时后缀文件是properties即可配置文件
使用c3p0连接池时ssl安全套字协议必须默认为false。【配置文件用连接池时用true/false均可】
数据库工作累,封装了jdbc操作的常用功能;
数据库的相关信息是写在源文件中的,如果我们要修改数据库,就必须要切换源码,;我们应该降低代码的耦合度,源代码一点都不需要改动;
配置文件一定要放在src文件夹下面,里面是常量,每个常量的内容后面不能加空格或者引号;
ResourceBundle rb=ResourceBundle.getBundle(“db”);//直接获得db.properties文件对象;【相当于25-33行的代码】
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tiPhnh19-1604303154972)(JDBC/image-20201023200336991.png)]
useSSL=true&verifyServerCertificate=false
SSL协议提供服务主要:
1)认证用户服务器,确保数据发送到正确的服务器; .
2)加密数据,防止数据传输途中被窃取使用;
3)维护数据完整性,验证数据在传输过程中是否丢失;
当前支持SSL协议两层:
SSL记录协议(SSL【Record 】Protocol):建立靠传输协议(TCP)高层协议提供数据封装、压缩、加密等基本功能支持
SSL握手协议(SSL 【Handshake】 Protocol):建立SSL记录协议用于实际数据传输始前通讯双进行身份认证、协商加密
算法、 交换加密密钥等。
在mysql连接字符串url中加入ssl=true或者false即可
c3p0与dbcp****区别
dbcp没有自动回收空闲连接的功能
c3p0有自动回收空闲连接功能
#properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/kgc? useUnicode=true&characterEncoding=utf8&useSSL=false;
com.mysql.username=root
com.mysql.password=123456
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<!-- 必填 -->
<!-- 驱动类 -->
<property name="driver">com.mysql.jdbc.Driver</property>
<!-- URL -->
<property name="url">jdbc:mysql://localhost:3306/kgc? useUnicode=true&characterEncoding=utf8&useSSL=false</property>
<!-- 账号 -->
<property name="user">root</property>
<!-- 密码 -->
<property name="password">root</property>
<!-- 可选 -->
<!-- 初始连接数 -->
<property name="initialPoolSize">10</property>
<!-- 最大闲置时间 -->
<property name="maxIdleTime">30</property>
<!-- 最大连接数 -->
<property name="maxPoolSize">100</property>
<!-- 最小连接数 -->
<property name="minPoolSize">10</property>
<!-- 最大SQL语句数 -->
<property name="maxStatements">200</property>
</default-config>
</c3p0-config>
1)DBCP
DBCP是一个依赖Jakarta commons-pool对象池机制的数据库连接池.DBCP可以直接的在应用程序中使用,Tomcat的数据源使用的就是DBCP。
2)c3p0
c3p0是一个开放源代码的JDBC连接池,它在lib目录中与Hibernate一起发布,包括了实现jdbc3和jdbc2扩展规范说明的Connection 和Statement 池的DataSources 对象。
3)Druid
阿里出品,淘宝和支付宝专用数据库连接池,但它不仅仅是一个数据库连接池,它还包含一个ProxyDriver,一系列内置的JDBC组件库,一个 SQL Parser。支持所有JDBC兼容的数据库,包括Oracle、MySql、Derby、Postgresql、SQL Server、H2等等。Druid针对Oracle和MySql做了特别优化,比如Oracle的PS Cache内存占用优化,MySql的ping检测优化。Druid提供了MySql、Oracle、Postgresql、SQL-92的SQL的完整支持,这是一个手写的高性能SQL Parser,支持Visitor模式,使得分析SQL的抽象语法树很方便。简单SQL语句用时10微秒以内,复杂SQL用时30微秒。通过Druid提供的SQL Parser可以在JDBC层拦截SQL做相应处理,比如说分库分表、审计等。Druid防御SQL注入攻击的WallFilter就是通过Druid的SQL Parser分析语义实现的。
基本配置是指连接池进行数据库连接的四个基本必需配置:
传递给JDBC驱动的用于连接数据库的用户名、密码、URL以及驱动类名。
DBCP | c3p0 | Druid | |
---|---|---|---|
用户名 | username | user | username |
密码 | password | password | password |
URL | url | jdbcUrl | jdbcUrl |
驱动类名 | driverClassName | driverClass | driverClassName |
最小连接数 | minIdle(0) | minPoolSize(3) | minIdle(0) |
---|---|---|---|
初始化连接数 | initialSize(0) | initialPoolSize(3) | initialSize(0) |
最大连接数 | maxTotal(8) | maxPoolSize(15) | maxActive(8) |
最大等待时间 | maxWaitMillis(毫秒) | maxIdleTime(0秒) | maxWait(毫秒) |
开启缓存功能 | poolPreparedStatements | maxStatements | poolPreparedStatements |
---|---|---|---|
单个连接拥有的最大缓存数 | maxOpenPrepared- Statements | maxStatementsPer- Connection | maxOpenPrepared- Statement |
申请连接检测 | testOnBorrow | testConnectionOnCheckin | testOnBorrow |
---|---|---|---|
是否超时检测 | testWhileIdle | testWhileIdle | |
空闲时间 | timeBetweenEvictionRunsMillis | idleConnectionTestPeriod | timeBetweenEvictionRunsMillis |
校验用sql语句 | validationQuery | preferredTestQuery | validationQuery |
归还连接检测 | testOnReturn | testConnectionOnCheckout | testO |
是否超时关闭连接 | removeAbandoned | breakAfterAcquireFailure | removeAbandoned |
---|---|---|---|
超时时间 | removeAbandonedTimeout | checkoutTimeout | removeAbandonedTimeout |
是否记录日志 | logAbandoned | logAbandoned |
DBCP | c3p0 | Druid | |
---|---|---|---|
重连次数 | acquireRetryAttempts | ||
间隔时间 | acquireRetryDelay |
属性(Parameter) | 默认值(Default) | 描述(Description) |
---|---|---|
username | 传递给JDBC驱动的用于建立连接的用户名(The connection username to be passed to our JDBC driver to establish a connection.) | |
password | 传递给JDBC驱动的用于建立连接的密码(The connection password to be passed to our JDBC driver to establish a connection.) | |
url | 传递给JDBC驱动的用于建立连接的URL(The connection URL to be passed to our JDBC driver to establish a connection.) | |
driverClassName | 使用的JDBC驱动的完整有效的java 类名(The fully qualified Java class name of the JDBC driver to be used.) | |
defaultAutoCommit | driver default | 连接池创建的连接的默认的auto-commit状态,没有设置则不会自动提交(The default auto-commit state of connections created by this pool. If not set then the setAutoCommit method will not be called.) |
initialSize | 0 | 初始化连接:连接池启动时创建的初始化连接数量(The initial number of connections that are created when the pool is started. |
maxTotal | 8 | 最大活动连接:连接池在同一时间能够分配的最大活动连接的数量, 如果设置为非正数则表示不限制(The maximum number of active connections that can be allocated from this pool at the same time, or negative for no limit.) |
maxIdle | 8 | 最大空闲连接:连接池中容许保持空闲状态的最大连接数量,超过的空闲连接将被释放,如果设置为负数表示不限制(The maximum number of connections that can remain idle in the pool, without extra ones being released, or negative for no limit.) |
minIdle | 0 | 最小空闲连接:连接池中容许保持空闲状态的最小连接数量,负数表示没有现在(The maximum number of connections that can remain idle in the pool, without extra ones being released, or negative for no limit.) |
注意:如果在某些负载比较大的系统中将maxIdel设置过小时,很可能会出现连接关闭的同时新连接马上打开的情况.这是由于关闭连接的线程比打开的快导致的.所以,对于这种系统中,maxIdle的设定值是不同的但是通常首选默认值 | ||
(NOTE: If maxIdle is set too low on heavily loaded systems it is possible you will see connections being closed and almost immediately new connections being opened. This is a result of the active threads momentarily closing connections faster than they are opening them, causing the number of idle connections to rise above maxIdle. The best value for maxIdle for heavily loaded system will vary but the default is a good starting point.) | ||
maxWaitMillis | indefinitely | 最大等待时间:当没有可用连接时,连接池等待连接被归还的最大时间(以毫秒计数),超过时间则抛出异常,如果设置为-1表示无限等待(The maximum number of milliseconds that the pool will wait (when there are no available connections) for a connection to be returned before throwing an exception, or -1 to wait indefinitely.) |
validationQuery | SQL查询,用来验证从连接池取出的连接,在将连接返回给调用者之前.如果指定,则查询必须是一个SQL SELECT并且必须返回至少一行记录(The SQL query that will be used to validate connections from this pool before returning them to the caller. If specified, this query MUST be an SQL SELECT statement that returns at least one row. If not specified, connections will be validation by calling the isValid() method.) | |
testOnCreate | false | 指明是否在建立连接之后进行验证,如果验证失败,则尝试重新建立连接(The indication of whether objects will be validated after creation. If the object fails to validate, the borrow attempt that triggered the object creation will fail.) |
testOnBorrow | true | 指明是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个.注意: 设置为true后如果要生效,validationQuery参数必须设置为非空字符串(The indication of whether objects will be validated before being borrowed from the pool. If the object fails to validate, it will be dropped from the pool, and we will attempt to borrow another.) |
testOnReturn | false | 指明是否在归还到池中前进行检验(The indication of whether objects will be validated before being returned to the pool.) |
testWhileIdle | false | 指明连接是否被空闲连接回收器(如果有)进行检验.如果检测失败,则连接将被从池中去除.注意: 设置为true后如果要生效,validationQuery参数必须设置为非空字符串(The indication of whether objects will be validated by the idle object evictor (if any). If an object fails to validate, it will be dropped from the pool.) |
timeBetweenEviction- RunsMillis | -1 | 在空闲连接回收器线程运行期间休眠的时间值,以毫秒为单位.如果设置为非正数,则不运行空闲连接回收器线程(The number of milliseconds to sleep between runs of the idle object evictor thread. When non-positive, no idle object evictor thread will be run.) |
numTestsPerEvictionRun | 3 | 在每次空闲连接回收器线程(如果有)运行时检查的连接数量(The number of objects to examine during each run of the idle object evictor thread (if any).) |
minEvictableIdleTime-Millis | 10006030 | 连接在池中保持空闲而不被空闲连接回收器线程(如果有)回收的最小时间值,单位毫秒(The minimum amount of time an object may sit idle in the pool before it is eligable for eviction by the idle object evictor (if any).) |
softMiniEvictableIdle- TimeMillis | -1 | 说明(The minimum amount of time a connection may sit idle in the pool before it is eligible for eviction by the idle connection evictor, with the extra condition that at least “minIdle” connections remain in the pool. When miniEvictableIdleTimeMillis is set to a positive value, miniEvictableIdleTimeMillis is examined first by the idle connection evictor - i.e. when idle connections are visited by the evictor, idle time is first compared against miniEvictableIdleTimeMillis (without considering the number of idle connections in the pool) and then against softMinEvictableIdleTimeMillis, including the minIdle constraint.) |
maxConnLifetimeMillis | -1 | 说明(The maximum lifetime in milliseconds of a connection. After this time is exceeded the connection will fail the next activation, passivation or validation test. A value of zero or less means the connection has an infinite lifetime.) |
logExpiredConnections | true | 说明(Flag to log a message indicating that a connection is being closed by the pool due to maxConnLifetimeMillis exceeded. Set this property to false to suppress expired connection logging that is turned on by default. |
connectionInitSqls | null | 说明(A Collection of SQL statements that will be used to initialize physical connections when they are first created. These statements are executed only once - when the configured connection factory creates the connection.) |
info | true | 说明(True means that borrowObject returns the most recently used (“last in”) connection in the pool (if there are idle connections available). False means that the pool behaves as a FIFO queue - connections are taken from the idle instance pool in the order that they are returned to the pool.) |
poolPreparedState-ments | false | 开启池的prepared statement 池功能(Enable prepared statement pooling for this pool.) |
maxOpenPreparedState-ments | unlimited | statement池能够同时分配的打开的statements的最大数量, 如果设置为0表示不限制(The maximum number of open statements that can be allocated from the statement pool at the same time, or negative for no limit.) |
NOTE - Make sure your connection has some resources left for the other statements. Pooling PreparedStatements may keep their cursors open in the database, causing a connection to run out of cursors, especially if maxOpenPreparedStatements is left at the default (unlimited) and an application opens a large number of different PreparedStatements per connection. To avoid this problem, maxOpenPreparedStatements should be set to a value less than the maximum number of cursors that can be open on a Connection. | ||
accessToUnderlyingConnectionAllowed | false | 控制PoolGuard是否容许获取底层连接(Controls if the PoolGuard allows access to the underlying connection.) 默认false不开启, 这是一个有潜在危险的功能, 不适当的编码会造成伤害.(关闭底层连接或者在守护连接已经关闭的情况下继续使用它).请谨慎使用,并且仅当需要直接访问驱动的特定功能时使用.注意: 不要关闭底层连接, 只能关闭前面的那个. Default is false, it is a potential dangerous operation and misbehaving programs can do harmful things. (closing the underlying or continue using it when the guarded connection is already closed) Be careful and only use when you need direct access to driver specific extensions. NOTE: Do not close the underlying connection, only the original one. |
removeAbandoned | false | 标记是否删除泄露的连接,如果他们超过了removeAbandonedTimout的限制.如果设置为true, 连接被认为是被泄露并且可以被删除,如果空闲时间超过removeAbandonedTimeout. 设置为true可以为写法糟糕的没有关闭连接的程序修复数据库连接. (Flags to remove abandoned connections if they exceed the removeAbandonedTimout. A connection is considered abandoned and eligible for removal if it has not been used for longer than removeAbandonedTimeout. Setting one or both of these to true can recover db connections from poorly written applications which fail to close connections.) |
removeAbandonedTimeout | 300 | 泄露的连接可以被删除的超时值, 单位秒 (Timeout in seconds before an abandoned connection can be removed.) |
logAbandoned | false | 标记当Statement或连接被泄露时是否打印程序的stack traces日志。被泄露的Statements和连接的日志添加在每个连接打开或者生成新的Statement,因为需要生成stack trace。(Flag to log stack traces for application code which abandoned a Statement or Connection. Logging of abandoned Statements and Connections adds overhead for every Connection open or new Statement because a stack trace has to be generated.) |
abandonedUsageTracking | false | 如果为true, 那么连接池会记录每个方法调用时候的堆栈信息以及废弃连接的调试信息(If true, the connection pool records a stack trace every time a method is called on a pooled connection and retains the most recent stack trace to aid debugging of abandoned connections. There is significant overhead added by setting this to true.) |
注:如果开启"removeAbandoned",那么连接在被认为泄露时可能被池回收. 这个机制在(getNumIdle() < 2)and (getNumActive() > getMaxActive() - 3)时被触发. 举例当maxActive=20, 活动连接为18,空闲连接为1时可以触发"removeAbandoned".但是活动连接只有在没有被使用的时间超过"removeAbandonedTimeout"时才被删除,默认300秒.在resultset中游历不被计算为被使用. | ||
If you have enabled removeAbandonedOnMaintenance or removeAbandonedOnBorrow then it is possible that a connection is reclaimed by the pool because it is considered to be abandoned. This mechanism is triggered when (getNumIdle() < 2) and (getNumActive() > getMaxTotal() - 3) and removeAbandonedOnBorrow is true; or after eviction finishes and removeAbandonedOnMaintenance is true. For example, maxTotal=20 and 18 active connections and 1 idle connection would trigger removeAbandonedOnBorrow, but only the active connections that aren’t used for more then “removeAbandonedTimeout” seconds are removed (default 300 sec). Traversing a resultset doesn’t count as being used. Creating a Statement, PreparedStatement or CallableStatement or using one of these to execute a query (using one of the execute methods) resets the lastUsed property of the parent connection. |
属性(Parameter) | 默认值(Default) | 描述(Description) |
---|---|---|
user | 同DBCP中的username属性 | |
password | 同DBCP中的password属性 | |
jdbcUrl | 同DBCP中的jdbcUrl属性 | |
driverClass | 同DBCP中的driverClass属性 | |
autoCommitOnClose | false | 默认值false表示回滚任何未提交的任务,设置为true则全部提交,而不是在关闭连接之前回滚 (C3P0’s default policy is to rollback any uncommitted, pending work.Setting autoCommitOnClose to true causes uncommitted pending work to be committed, rather than rolled back on Connection close.) *参见DBCP中的defaultAutoCommit属性 |
initialPoolSize | 3 | 初始化连接:连接池启动时创建的初始化连接数量(The initial number of connections that are created when the pool is started. *参见DBCP中的initialSize属性 |
maxPoolSize | 15 | 连接池中保留的最大连接数(Maximum number of Connections a pool will maintain at any given time.) *参见DBCP中的maxIdle属性 |
minPoolSize | 3 | 连接池中保留的最小连接数(Minimum number of Connections a pool will maintain at any given time.) *参见DBCP中的maxIdle属性 |
maxIdleTime | 0 | 最大等待时间:当没有可用连接时,连接池等待连接被归还的最大时间(以秒计数),超过时间则抛出异常,如果设置为0表示无限等待(Seconds a Connection can remain pooled but unused before being discarded. Zero means idle connections never expire.) *参见DBCP中maxWaitMillis 属性 |
preferredTestQuery | null | 定义所有连接测试都执行的测试语句。在使用连接测试的情况下这个一显著提高测试速度。注意:测试的表必须在初始数据源的时候就存在。(Defines the query that will be executed for all connection tests, if the default ConnectionTester (or some other implementation of QueryConnectionTester, or better yet FullQueryConnectionTester) is being used. Defining a preferredTestQuery that will execute quickly in your database may dramatically speed up Connection tests.) |
testConnectionOn- Checkin | false | 如果设为true那么在取得连接的同时将校验连接的有效性。(If true, an operation will be performed asynchronously at every connection checkin to verify that the connection is valid. Use in combination with idleConnectionTestPeriod for quite reliable, always asynchronous Connection testing.) *参见DBCP中的testOnBorrow属性 |
testConnectionOn- Checkout | false | 如果设为true那么在每个connection提交的时候都将校验其有效性,但是要确保配置的preferredTestQuery的有效性(If true, an operation will be performed at every connection checkout to verify that the connection is valid. Be sure to set an efficient preferredTestQuery or automaticTestTable if you set this to true.) *参见DBCP中的testOnBorrow属性 |
idleConnectionTest- Period | 0 | 如果设置大于0,表示过了多少秒检查一次空闲连接,结合testConnectionOnCheckin以及testConnectionOnCheckout使用(If this is a number greater than 0, c3p0 will test all idle, pooled but unchecked-out connections, every this number of seconds.) |
acquireRetryAttempts | 30 | 定义在从数据库获取新连接失败后重复尝试的次数, 如果小于0则表示无限制的连接。(Defines how many times c3p0 will try to acquire a new Connection from the database before giving up. If this value is less than or equal to zero, c3p0 will keep trying to fetch a Connection indefinitely.) |
acquireRetryDelay | 1000 | 两次连接中的间隔时间,单位毫秒。(Milliseconds, time c3p0 will wait between acquire attempts.) |
breakAfterAcquire- Failure | false | 获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。但是数据源仍有效保留,并在下次调用 getConnection() 的时候继续尝试获取连接。如果为 true,那么在尝试获取连接失败后该数据源将声明已断开并永久关闭。(If true, a pooled DataSource will declare itself broken and be permanently closed if a Connection cannot be obtained from the database after making acquireRetryAttempts to acquire one. If false, failure to obtain a Connection will cause all Threads waiting for the pool to acquire a Connection to throw an Exception, but the DataSource will remain valid, and will attempt to acquire again following a call to getConnection().) |
checkoutTimeout | 0 | 当连接池用完时客户端调用 getConnection() 后等待获取新连接的时间,潮湿后将抛出SQLException,如设为0,则为无限期等待。单位毫秒。(The number of milliseconds a client calling getConnection() will wait for a Connection to be checked-in or acquired when the pool is exhausted. Zero means wait indefinitely. Setting any positive value will cause the getConnection() call to time-out and break with an SQLException after the specified number of milliseconds.) |
maxStatements | 0 | 控制数据源内加载的PreparedStatements数量(Enable prepared statement pooling for this pool.) |
maxStatementsPer- Connection | 0 | 定义了连接池内单个连接所拥有的最大缓存statements数(The maximum number of open statements that can be allocated from the statement pool at the same time, or negative for no limit.) |
属性(Parameter) | 默认值(Default) | 描述(Description) |
---|---|---|
username | 连接数据库的用户名 | |
password | 连接数据库的密码 | |
jdbcUrl | 同DBCP中的jdbcUrl属性 | |
driverClassName | 根据url自动识别 | 这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName |
initialSize | 0 | 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 *参见DBCP中的initialSize属性 |
maxActive | 8 | 最大连接池数量(Maximum number of Connections a pool will maintain at any given time.) *参见DBCP中的maxTotal属性 |
maxIdle | 8 | 已经不再使用,配置了也没效果*参见DBCP中的maxIdle属性 |
minIdle | 最小连接池数量 | |
maxWait | 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。 | |
poolPreparedState- ments | false | 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。 |
maxOpenPrepared- Statements | -1 | 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100 |
testOnBorrow | true | 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 |
testOnReturn | false | 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能 |
testWhileIdle | false | 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 |
validationQuery | 用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、 testWhileIdle都不会其作用。在mysql中通常为select ‘x’,在oracle中通常为select 1 from dual | |
timeBetweenEviction-RunsMillis | 1) Destroy线程会检测连接的间隔时间 2) testWhileIdle的判断依据 | |
minEvictableIdle- TimeMillis | Destory线程中如果检测到当前连接的最后活跃时间和当前时间的差值大于minEvictableIdleTimeMillis,则关闭当前连接。 | |
removeAbandoned | 对于建立时间超过removeAbandonedTimeout的连接强制关闭 | |
removeAbandoned-Timeout | 指定连接建立多长时间就需要被强制关闭 | |
logAbandoned | false | 指定发生removeabandoned的时候,是否记录当前线程的堆栈信息到日志中 |
filters | 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 1)监控统计用的filter:stat 2)日志用的filter:log4j 3)防御sql注入的filter:wall |
简介:metadata一般指元数据。元数据(Metadata),又称中介数据、中继数据,为描述数据的数据(data about data),主要是描述数据属性(property)的信息,用来支持如指示存储位置、历史数据、资源查找、文件记录等功能按应用包括数据结构:数据集的名称、关系、字段、约束等;
//列的名称
String columnName = rs_metaData.getColumnName(i + 1);
// 获取每一行的每一列的值
Object columnValue = rs.getObject(columnName);
package com;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
public class testMatadata {
public static void main(String[] args) {
Connection conn=null;
//1. JDBC连接MYSQL的代码很标准。
String DRIVER="com.mysql.jdbc.Driver";
String URL="jdbc:mysql://127.0.0.1:3306/test";
String USER="root";
String PASSWORD="root";
try {
//1.加载驱动程序
Class.forName(DRIVER);
//2.获得数据库链接
conn=DriverManager.getConnection(URL, USER, PASSWORD);
DatabaseMetaData dbmd = conn.getMetaData();
ResultSet rs = null;
System.out.println("数据库已知的用户: "+ dbmd.getUserName());
System.out.println("数据库的系统函数的逗号分隔列表: "+ dbmd.getSystemFunctions());
System.out.println("数据库的时间和日期函数的逗号分隔列表: "+ dbmd.getTimeDateFunctions());
System.out.println("数据库的字符串函数的逗号分隔列表: "+ dbmd.getStringFunctions());
System.out.println("数据库供应商用于 'schema' 的首选术语: "+ dbmd.getSchemaTerm());
System.out.println("数据库URL: " + dbmd.getURL());
System.out.println("是否允许只读:" + dbmd.isReadOnly());
System.out.println("数据库的产品名称:" + dbmd.getDatabaseProductName());
System.out.println("数据库的版本:" + dbmd.getDatabaseProductVersion());
System.out.println("驱动程序的名称:" + dbmd.getDriverName());
System.out.println("驱动程序的版本:" + dbmd.getDriverVersion());
System.out.println("数据库中使用的表类型");
rs = dbmd.getTableTypes();
while (rs.next()) {
System.out.println(rs.getString("TABLE_TYPE"));
}
// 获取连接
PreparedStatement pstmt = conn.prepareStatement("select * from test.user ");
rs = pstmt.executeQuery();
// 得到结果集元数据
//目标:通过结果集元数据,得到列的名称
ResultSetMetaData rs_metaData = rs.getMetaData();
while (rs.next()) {
int count = rs_metaData.getColumnCount();
for (int i=0; i
一个类成为另外一个类的属性,这种关系成为合成/聚合关系,此时其中一个类是另外一个类的一部分;
一个类成为另外一个类的方法或参数类型,这种关系称为依赖。【不能写人依赖于迈腾,这样代码写死,耦合度太高】
Person p=new Person();
Office o=new office();
Car car=o.getCar("ceo");//返回一个具体的对象,并实现了car接口;【我们不知道具体哪个类,但是代码能运行,代码耦合性降低了】
p.driver(car);
多态:用接口或父类声明引用指向实现类或具体子类对象【父类引用指向子类】,动态绑定;
封装jdbc工具是,用常量封装user原因,把它放到配置文件里解耦;一定要在src目录下,并且里面的值不能用符号或空格。只用变量=具体值 【类没发现因为没有获得连接,因为配置文件不正确,因为值后面多了空格】
行级锁又称为悲观锁,在当前事务没有结束之前,被锁定的记录,精确到表中的行,其他事务是无法进行修改的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wXAUWP54-1604303154973)(JDBC/image-20201027233058626.png)]
batch批处理注意事项,数据量特别大的时候,应该用statement,因为预编译存储数量有限,容易产生异常