1.JDBC
Java DataBase Connector
JDBC是Java数据库连接技术的简称,提供连接各种常用数据库的能力
2.JDBC编程模板
1、加载JDBC驱动
Class.forName(JDBC驱动类);
与数据库创建连接
Connection con=DriverManager.getConnection(URL,数据库用户名,密码);
URL: 数据库类型、数据库服务所在计算记得ip地址、服务端口号、默认数据仓库、配置参数
通过连接对象创建statement对象发送SQL语句,并得到返回结果
处理结果集,查询的结果集是ResultSet,增删改查返回的就是一个int值是表达受影响的行数
释放资源
1. 如何下载jdbc驱动包
- https://mvnrepository.com/
- 在搜索框中输入mysql
- 找到名字: MySQL Connector/J
- 选择对应版本
- 点击版本号,进入可以选择jar进行下载或者其他的方法引入
2.案例代码
import java.sql.*;
public class Test {
public static void main(String[] args) {
//1. 注册驱动
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
System.err.println("mysql驱动加载异常!");
e.printStackTrace();
}
//2. 连接数据库
String url = "jdbc:mysql://localhost:3306/javatest?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai";
String userName = "root";
String pwd = "cxk666";
Connection connection = null;
try {
connection = DriverManager.getConnection(url,userName,pwd);
} catch (SQLException e) {
System.err.println("数据库连接出现异常!");
e.printStackTrace();
}
//3. 操作数据库
Statement statement = null;
ResultSet resultSet = null;
try {
statement = connection.createStatement();
//查询返回结果集
resultSet = statement.executeQuery("select * from student");
} catch (SQLException e) {
System.err.println("操作数据库异常!");
e.printStackTrace();
}
//4. 处理数据
try{
while(resultSet.next()){
String s_id = resultSet.getString("s_id");
System.out.print(s_id+"\t");
String s_name = resultSet.getString("s_name");
System.out.print(s_name+"\t");
System.out.println();
}
}catch (SQLException e){
System.err.println("数据处理异常");
e.printStackTrace();
}
//5. 关闭资源
try {
resultSet.close();
statement.close();
connection.close();
} catch (SQLException e) {
System.err.println("连接关闭异常");
e.printStackTrace();
}
}
}
方法名 | 说 明 |
---|---|
ResultSet executeQuery(String sql) | 执行SQL查询并获取到ResultSet对象 |
int executeUpdate(String sql) | 可以执行插入、删除、更新等操作,返回值是执行该操作所影响的行数 |
boolean execute(String sql**)** | 可以执行任意SQL语句,然后获得一个布尔值,表示是否返回****ResultSet |
3. JDBC-execute方法执行增删改查语句
//execute 方法执行:增、删、改返回的都是false,执行查询语句返回的是true boolean execute = statement.execute(sql);//一般不用于查询
/* 注册驱动、创建连接的方法和2.代码案例中一致 **/ //3. 插入数据 String sql = "INSERT INTO `student` ( `s_id`, `s_name`, `s_birth`, `s_sex` )\n" + "VALUES\n" + "\t( '09', '旭宝宝', '1990-01-01', '男' );\n"; //3.1 获取statement对象 Statement statement = null; try { statement = connection.createStatement(); // execute 方法执行:增、删、改返回的都是false,执行查询语句返回的是true boolean execute = statement.execute(sql); System.out.println(execute); } catch (SQLException e) { System.err.println("数据插入错误!"); e.printStackTrace(); } /* 关闭连接操作省略 */
4.JDBC-executeUpdate执行增删改语句
// executeUpdate:返回值是受影响的行数,可以使用在增、删、改语句,不能使用于查询语句 int i = statement.executeUpdate(sql);
//3. 更新数据
String sql = "update student set s_name='卢本伟' where s_sex='人妖';";
//3.1 获取statement对象
Statement statement = null;
try {
statement = connection.createStatement();
// executeUpdate:返回值是受影响的行数,可以使用在增、删、改语句,不能使用于查询语句
int i = statement.executeUpdate(sql);
System.out.println(i);
} catch (SQLException e) {
System.err.println("数据插入错误!");
e.printStackTrace();
}
5. JDBC-查询语句-executeQuery方法
// executeQuery:只能运用于查询语句,返回的是查询结果集
ResultSet resultSet = statement.executeQuery(sql);
//3. 操作数据库
Statement statement = null;
ResultSet resultSet = null;
try {
statement = connection.createStatement();
//查询返回结果集
resultSet = statement.executeQuery("select * from student");
} catch (SQLException e) {
System.err.println("操作数据库异常!");
e.printStackTrace();
}
//4. 处理数据
try{
while(resultSet.next()){
String s_id = resultSet.getString("s_id");
System.out.print(s_id+"\t");
String s_name = resultSet.getString("s_name");
System.out.print(s_name+"\t");
System.out.println();
}
}catch (SQLException e){
System.err.println("数据处理异常");
e.printStackTrace();
}
5.1 ResultSet对象操作方法
方法名 | 说 明 |
---|---|
boolean next() | 将游标从当前位置向下移动一行 |
boolean previous() | 游标从当前位置向上移动一行 |
void close() | 关闭****ResultSet 对象 |
获取字段的操作方法:
get数据类型(字段名\字段序号值)
取值方法:
1.字段顺序:1代表第一列 2.字段名称取值:查询结果中投影怎么写,字段名就怎么写
常见的mysql数据类型匹配jdbc方法:
getInt : int\tinyint\bigint
getDouble: float\double\deccamial
getString: char\varchar\text\longText
getDate: date\time\datetime\timestamp (java.sql.Date extends java.util.Date)
while(resultSet.next()){
String string = resultSet.getString("s_name");
System.out.println(string);
}
3.SQL 注入问题
sql注入是什么?
通过标点符号或特殊符号注入到参数中,使后端java运行sql语句异常
存在的安全问题:
1. 数据泄露和遗失 1. 会让数据库操作全部崩溃
// 输入用户名和密码
String sys_userName = "yyy";
String sys_pwd = "' or 1=1 or ''='";
//3. 操作数据库
Statement statement = null;
ResultSet resultSet = null;
String sql = "select * from sys_user where userName = '"+sys_userName+"' and pwd='"+sys_pwd+"'";
//select * from sys_user where userName = 'yyy' and pwd='' or 1=1 or ''=''
System.out.println("sql:"+sql);
try {
statement = connection.createStatement();
//查询返回结果集
resultSet = statement.executeQuery(sql);
} catch (SQLException e) {
System.err.println("操作数据库异常!");
e.printStackTrace();
}
SQL注入问题解决方案
1. 方案-一刀切
禁止在系统中允许用户输入:空白、制表符、or、标点符号等特殊字符
2.方案-PreparedStatement 接口
使用方法:
- connection对象调用prepareStatement方法创建PreparedStatement对象:注意,调用prepareStatement方法把sql提交给对象进行预编译
- 参数插入时要保证sql字符串中有?占位符,根据站位符的顺序添加参数
- 调用set方法规则: set数据类型(占位符编号,参数数值) 注意: 根据数据库的数据类型调用对应的set方法,占位符的编号从左往右从1开始
- 执行sql:如果是增、删、改,调用的是无参executeUpdate或execute方法;查询调用无参executeQuery方法
// 输入用户名和密码
String sys_userName = "yyy";
String sys_pwd = "' or 1=1 or ''='";
//3. 操作数据库
PreparedStatement statement = null;
ResultSet resultSet = null;
String sql = "select * from sys_user where userName = ? and pwd= ?";
try {
//1. 创建 PreparedStatement 对象- 把sql提交给对象进行预编译
statement = connection.prepareStatement(sql);
//2. 参数插入
statement.setString(1,sys_userName);// 给第一个站位符填值
statement.setString(2,sys_pwd);// 给第二个站位符填值
//查看sql
String s = statement.toString();
System.out.println("sql:"+s);
//查询返回结果集 - 调用无参方法-不需要传入sql
resultSet = statement.executeQuery();
} catch (SQLException e) {
System.err.println("操作数据库异常!");
e.printStackTrace();
}
4.在开发过程中使用的是PreparedStatement 接口,不会使用Statement 接口方法
5.JDBC工具封装
java代码
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
/**
* JDBC工具类
*/
public class MyJDBCUtil {
private static String userName;//数据库用户名
private static String pwd;//数据库密码
private static String connectionURL;//数据库连接URL地址
private MyJDBCUtil(){
}
static{
//1.驱动注册
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
System.err.println("mysql驱动加载异常!");
e.printStackTrace();
}
//2. 从配置文件中读取用户名和密码配置
InputStream is = MyJDBCUtil.class.getResourceAsStream("database.properties");
Properties properties = new Properties();
try {
properties.load(is);
} catch (IOException e) {
System.err.println("mysql配置加载异常!");
e.printStackTrace();
}
String p_userName = (String) properties.get("userName");
String p_pwd = (String) properties.get("pwd");
String p_mysqlURL = (String) properties.get("mysqlURL");
userName = p_userName;
pwd = p_pwd;
connectionURL = p_mysqlURL;
}
/**
* 获取数据库连接
* @return 数据库连接对象 Connection
*/
public static Connection getConnection(){
Connection connection = null;
try {
connection = DriverManager.getConnection(connectionURL, userName, pwd);
} catch (SQLException e) {
System.err.println("mysql连接异常!");
e.printStackTrace();
}
return connection;
}
/**
* 查询之后的关闭操作
* @param statement 操作对象
* @param resultSet 查询结果集
* @param connection 连接对象
*/
public static void close(ResultSet resultSet,Statement statement,Connection connection){
try {
resultSet.close();
statement.close();
connection.close();
} catch (SQLException e) {
System.err.println("连接关闭异常");
e.printStackTrace();
}
}
/**
* 增删改之后关闭
* @param statement 操作对象
* @param connection 连接对象
*/
public static void close(Statement statement,Connection connection){
try {
statement.close();
connection.close();
} catch (SQLException e) {
System.err.println("连接关闭异常");
e.printStackTrace();
}
}
}
配置文件
userName = root
pwd = cxk666
mysqlURL = jdbc:mysql://localhost:3306/javatest?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
6. 企业级开发包结构要求
区分的目的:为了让开发者清晰项目的内容资源(工具、业务代码、接口、相关依赖)
src
-- 项目根包:一般是公司域名反写
-- 业务包1
-- 代码功能包
-- 业务包2
src
com.huawei -- 根目录包结构
sys -- 系统管理业务包
utils -- 系统管理业务中的工具类包
service -- 系统管理业务中的接口
impl -- 系统管理业务中的接口的实现类
student
...
7. VO\DTO\BO\PO 对象
VO(View Object):视图对象,作用是保存视图组件传递的数据或者给视图传递数据的对象
DTO(Data Transfer Object):万金油,适用于所有服务间数据转发过程
BO(Bussiness Object):业务对象,使用在java代码业务之间传递值
PO(Persistent Object):持久化对象,该对象是和数据库字段一一对应,改对象描述的就是数据库的表数据
/* 对应数据库student表的po对象 */
package com.huawei.sys.POs;
import java.io.Serializable;
import java.util.Date;
public class StudentPO implements Serializable {
private String sID;
private String sName;
private Date sBirth;
private String sSex;
public String getsID() {
return sID;
}
public void setsID(String sID) {
this.sID = sID;
}
public String getsName() {
return sName;
}
public void setsName(String sName) {
this.sName = sName;
}
public Date getsBirth() {
return sBirth;
}
public void setsBirth(Date sBirth) {
this.sBirth = sBirth;
}
public String getsSex() {
return sSex;
}
public void setsSex(String sSex) {
this.sSex = sSex;
}
}
8. JDBC-连接池
8.1 手工连接池
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
/**
* JDBC工具类
*/
public class MyJDBCUtil {
private static String userName;//数据库用户名
private static String pwd;//数据库密码
private static String connectionURL;//数据库连接URL地址
private static LinkedList<Connection> connectorPool; // 连接池
private MyJDBCUtil(){}
static{
//1.驱动注册
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
System.err.println("mysql驱动加载异常!");
e.printStackTrace();
}
//2. 从配置文件中读取用户名和密码配置
InputStream is = MyJDBCUtil.class.getResourceAsStream("database.properties");
Properties properties = new Properties();
try {
properties.load(is);
} catch (IOException e) {
System.err.println("mysql配置加载异常!");
e.printStackTrace();
}
String p_userName = (String) properties.get("userName");
String p_pwd = (String) properties.get("pwd");
String p_mysqlURL = (String) properties.get("mysqlURL");
int initPoolSize = Integer.valueOf((String) properties.get("initPoolSize"));
userName = p_userName;
pwd = p_pwd;
connectionURL = p_mysqlURL;
//初始化连接池
connectorPool = (LinkedList<Connection>) Collections.synchronizedList(new LinkedList<Connection>());
for(int i=0;i<initPoolSize;i++){
Connection connection = null;
try {
connection = DriverManager.getConnection(connectionURL, userName, pwd);
} catch (SQLException e) {
System.err.println("mysql连接异常!");
e.printStackTrace();
}
connectorPool.addLast(connection);
}
//启动一个线程监测每个连接是否正常能够访问数据库
new Thread(()->{
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
for(int i=0;i<initPoolSize;i++){
Connection connection = connectorPool.get(i);
Statement statement = null;
try {
statement = connection.createStatement();
statement.execute("select count(*) from dual");
} catch (SQLException e) {
Connection newConnection = null;
try {
newConnection = DriverManager.getConnection(connectionURL, userName, pwd);
} catch (SQLException e2) {
System.err.println("mysql连接异常!");
e2.printStackTrace();
}
connectorPool.remove(i);
connectorPool.addLast(newConnection);
}
}
}
},"poolThread").start();
// 使用sql:select count(*) from dual; 监测与服务器的连接是否正常,以及测试速度
}
/**
* 获取数据库连接
* @return 数据库连接对象 Connection
*/
public static synchronized Connection getConnection(){
Connection last = connectorPool.getLast();
if(last == null){
System.err.println("连接池连接暂无,通过创建连接获取新接连");
Connection connection = null;
try {
connection = DriverManager.getConnection(connectionURL, userName, pwd);
} catch (SQLException e) {
System.err.println("mysql连接异常!");
e.printStackTrace();
}
return connection;
}
return last;
}
/**
* 查询之后的关闭操作
* @param connection 连接对象
*/
public static synchronized void close(Connection connection){
connectorPool.add(connection);
}
}
8.2 C3P0连接池-mysql版本使用
1.首先下载c3p0的jar包 需要:mchange-commons-java包和c3p0包
2.初始化连接池数据源对象:
ComboPooledDataSource dataSource = new ComboPooledDataSource();
3.使用C3P0
8.2.1 在静态代码块中初始化连接池(不推荐使用)
package com.huawei.sys.utils;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException;
/**
* C3P0连接池工具类
*/
public class MyC3P0Util {
// 连接池数据源对象
private static ComboPooledDataSource dataSource = new ComboPooledDataSource();
private MyC3P0Util(){
}
static{
//1. 注册驱动
try {
dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
} catch (PropertyVetoException e) {
System.err.println("驱动注册异常");
e.printStackTrace();
}
//2. 设置url 用户名 密码
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/javatest?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai");
dataSource.setUser("root");
dataSource.setPassword("cxk666");
}
/**
* C3P0连接池-获取连接的方法
* @return
*/
public static Connection getConnection(){
Connection connection = null;
try {
connection = dataSource.getConnection();
} catch (SQLException e) {
System.err.println("获取连接异常!");
e.printStackTrace();
}
return connection;
}
/**
* C3P0归还连接的方法
*/
public static void close(Connection con){
try {
con.close();// C3P0中并不会关闭,是将连接归还到连接池(代理模式:mchange-commons)
} catch (SQLException e) {
System.err.println("归还连接异常!");
e.printStackTrace();
}
}
}
8.2.2 使用properties配置文件配置C3P0连接池初始化(推荐使用)
优点: 配置和代码分离,且c3p0是自动完成配置不需要手动编写任何初始化代码
配置文件内容
注意: 配置文件名字必须叫:c3p0.properties ,要放在src目录下,不要放到包结构中
c3p0.driverClass=com.mysql.cj.jdbc.Driver
c3p0.jdbcUrl=jdbc:mysql://localhost:3306/javatest?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
c3p0.user=root
c3p0.password=cxk666
# 初始化连接池的大小
c3p0.initialPoolSize=3
# 最大连接时间
c3p0.maxIdleTime=60
# 最大连接池大小
c3p0.maxPoolSize=20
java工具类代码内容
package com.huawei.sys.utils;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException;
/**
* C3P0连接池工具类
*/
public class MyC3P0Util {
// 连接池数据源对象
private static ComboPooledDataSource dataSource = new ComboPooledDataSource();
private MyC3P0Util(){
}
//不再需要静态代码块了,当项目启动,数据源对象初始化时会自动找到配置文件初始化连接池
/**
* C3P0连接池-获取连接的方法
* @return
*/
public static Connection getConnection(){
Connection connection = null;
try {
connection = dataSource.getConnection();
} catch (SQLException e) {
System.err.println("获取连接异常!");
e.printStackTrace();
}
return connection;
}
/**
* C3P0归还连接的方法
*/
public static void close(Connection con){
try {
con.close();// C3P0中并不会关闭,是将连接归还到连接池(代理模式:mchange-commons)
} catch (SQLException e) {
System.err.println("归还连接异常!");
e.printStackTrace();
}
}
}
8.2.3 多数据源情况使用-xml配置文件
多数据源?
可能一个项目同时需要多种数据库配置灵活应变
注意:使用properties的配置方法和使用xml配置方法只能选择其中一个使用
使用xml配置:文件名必须叫:c3p0-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<!-- 默认使用的数据源配置 -->
<default-config>
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/javatest?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
</property>
<property name="user">root</property>
<property name="password">cxk666</property>
</default-config>
<!-- 多数据源-其他数据源的配置方法 -->
<!-- 其他数据源一定要指定name属性 -->
<named-config name="tengxunyun" >
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://XXXXXXX:3306/XXXX?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
</property>
<property name="user">XXXXX</property>
<property name="password">XXXX</property>
</named-config>
</c3p0-config>
多数据源初始化数据源对象时:
// 构造器中指定一个数据源的名称,对应的是xml文件中 named-config 标签的 name属性值
private static ComboPooledDataSource dataSource = new ComboPooledDataSource("tengxunyun");
使用连接池案例
public static void main(String[] args) throws SQLException {
//1. 调用编写的工具类通过C3P0获取连接
Connection connection = MyC3P0Util.getConnection();
//2. 创建操作对象查询数据
Statement statement = connection.createStatement();
ResultSet set = statement.executeQuery("select s_name from student");
while(set.next()){
String s_name = set.getString("s_name");
System.out.println(s_name);
}
//3. 归还连接(代理完成归还,并不是关闭连接)
connection.close();//归还连接
}