JDBC(Java DataBase Connectivity) : Java数据库连接,是Java语言操作数据库的一种技术。
JDBC是sun公司定义的一套操作所有关系型数据库的规则(接口),具体的实现类由各个数据库厂商来实现,Java开发人员只需要面向JDBC接口编程,不需要理会实现类中的方法。
JDBC优点:使用同一套代码可以快速切换不同的数据库。
public class JdbcQuickly {
public static void main(String[] args) throws SQLException {
//1.注册驱动
DriverManager.registerDriver(new Driver());
//2.获得连接
String url="jdbc:mysql://localhost:3306/database1";
String username = "root";
String password = "root";
Connection connection = DriverManager.getConnection(url, username, password);
//3.获得语句执行者对象(帮我们执行sql语句的)
Statement statement = connection.createStatement();
//4.执行sql
String sql = "INSERT INTO tab_user VALUES(NULL , 'jack' , '1234' , 1000);";
//5.处理返回结果
int count = statement.executeUpdate(sql);
System.out.println("影响记录行数为:" + count);
//6.释放资源
statement.close();
connection.close();
}
}
注册驱动需要引入jar包,作用是告知java程序到底使用哪个数据库。
// 另外一种注册方式
Class.forName("com.mysql.jdbc.Driver");
作用:获取连接对象,连接对象提供createStatement()
方法创建操作数据库对象。
String url = "jdbc:mysql://localhost:3306/database1";
协议 主机地址 端口号 数据库名称
String username = "root"; 用户名
String password = "root"; 密码
Connection connection = DriverManager.getConnection(url, username, password);
public void testSelect() throws Exception {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获得连接
String url ="jdbc:mysql:///day04";
String username="root";
String password="root";
Connection connection = DriverManager.getConnection(url, username, password);
//3.获得语句执行者对象(帮我们执行sql语句的)
Statement statement = connection.createStatement();
/**
* 4.执行sql语句
* ResultSet 结果集对象 包含着当前 sql语句执行的结果内容
*/
String sql = "select username,id,password,money from tab_user";
ResultSet resultSet = statement.executeQuery(sql);
/**
* 5.处理结果
* next() 指针向下移动
* resultSet.get数据类型的方法(列的位置) 很少用 数据库的sql语句是会变化的
* int id = resultSet.getInt(1);
* String dbUsername = resultSet.getString(2);
* resultSet.get数据类型的方法(列名) 跟最后的展示顺序无关
*/
while (resultSet.next()){ //判断下一行有没有数据 如果有 指针向下移动
//指针指向的当前行数据
int id = resultSet.getInt("id");
String dbUsername = resultSet.getString("username");
System.out.println(id +"@@"+dbUsername);
}
//6.释放资源
resultSet.close();
statement.close();
connection.close();
}
常见错误:
无论是循环前还是循环后都不允许操作ResultSet集合!
connection.setAutoCommit( false )
connection.commit()
;connection.rollback()
;工具类:将程序反复出现的代码按照功能进行封装,方便使用
package com.itheima.utils;
import java.sql.*;
/**
* 工具类: 抽取重复代码
* 1.准备一个方法 连接对象
* 2.准备一个方法 释放资源对象
*/
public class JdbcUtils {
/**
* 1.准备一个方法 连接对象
*/
public static Connection getConnection() throws Exception {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获得连接
String url ="jdbc:mysql:///test";
String username="root";
String password="root";
Connection connection = DriverManager.getConnection(url, username, password);
return connection;
}
/**
* 2.准备一个方法 释放资源对象
*/
public static void close(Statement statement , Connection connection){
try {
if(statement!=null){
statement.close();
}
if(connection!= null){
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void close(ResultSet resultSet , Statement statement , Connection connection){
try {
if(resultSet!=null){
resultSet.close();
}
close(statement , connection);//调用已经写好的close
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//Servlet
@WebServlet(name = "LoginServlet" , urlPatterns = "/login")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
//1.获得数据
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println(username + "@@" + password);
//2.调用service进行登录
UserService userService = new UserServiceImpl();
//返回值必须是user对象 而不是01
User user = userService.login(username , password);
//3.响应数据
if(user == null){
response.sendRedirect(request.getContextPath() + "/fail.jsp");
}else{
//成功需要将user放入session
request.getSession().setAttribute("user" , user);
response.sendRedirect(request.getContextPath() + "/success.jsp");
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("未知异常错误,请检查程序");
}
}
}
//service
public class UserServiceImpl implements UserService {
/**
* 登录方法
*/
@Override
public User login(String username, String password) throws Exception {
UserDao userDao = new UserDaoImpl();
User user = userDao.login(username,password);
return user;
}
}
//dao
public class UserDaoImpl implements UserDao {
/**
* 根据用户名密码查询
* @param username
* @param password
* @return
*/
@Override
public User login(String username, String password) throws Exception {
//1.注册驱动 2.获得连接
Connection connection = JdbcUtils.getConnection();
//3.创建语句执行者对象
Statement statement = connection.createStatement();
//4.执行sql
String sql = "select * from tab_user where username ='"+username+"' and password='"+password+"'";
ResultSet resultSet = statement.executeQuery(sql);
//5.处理结果
User user = null;
while (resultSet.next()){//判断有没有下一行数据
//构建user对象
user = new User(resultSet.getInt("id"), resultSet.getString("username") , resultSet.getString("password"), resultSet.getInt("money"));
break;//测试程序写上
}
//6.释放资源
JdbcUtils.close(resultSet,statement,connection);
return user;
}
}
注入问题:在拼接sql语句时,造成了语义的变化,改变了内部原来的语法
预编译执行原理:
PreparedStatement优势:
使用PreparedStatement完成CRUD
public class DML_CURD {
@Test
public void insert() throws Exception {
//1.注册驱动
//2.获得连接
Connection connection = JdbcUtils.getConnection();
String sql = "insert into tab_user values(null,?,?,?)"; //?表示占位
/**
* 3.获得预编译对象 同时发送sql
* PreparedStatement 提供方法
* set数据类型(位置,值)
* executeUpdate() 执行DML 传入sql就报错
* executeQuery() 执行DQL 传入sql就报错
* Statement
* executeUpdate(sql) 执行DML
* executeQuery(sql) 执行DQL
*/
PreparedStatement pst = connection.prepareStatement(sql);//发送sql语句
pst.setString(1, "tom");
pst.setString(2 , "2222");
pst.setInt(3, 1000);
//记住 通知数据库执行 不需要传入 sql
int count = pst.executeUpdate();
//5.处理返回结果
System.out.println(count);
//6.释放资源
JdbcUtils.close(pst,connection);
}
@Test
public void update() throws Exception {
//1.注册驱动
//2.获得连接
Connection connection = JdbcUtils.getConnection();
String sql = " update tab_user set username=? , password=? ,money = ? where id = ?"; //?表示占位
/**
* 3.获得预编译对象 同时发送sql
*/
PreparedStatement pst = connection.prepareStatement(sql);//发送sql语句
pst.setString(1, "jerry");
pst.setString(2 , "4444");
pst.setInt(3, 5000);
pst.setInt(4, 4);
//记住 通知数据库执行 不需要传入 sql
int count = pst.executeUpdate();
//5.处理返回结果
System.out.println(count);
//6.释放资源
JdbcUtils.close(pst,connection);
}
@Test
public void delete() throws Exception {
//1.注册驱动
//2.获得连接
Connection connection = JdbcUtils.getConnection();
String sql = " delete from tab_user where id = ? "; //?表示占位
/**
* 3.获得预编译对象 同时发送sql
*/
PreparedStatement pst = connection.prepareStatement(sql);//发送sql语句
pst.setInt(1, 5);
//记住 通知数据库执行 不需要传入 sql
int count = pst.executeUpdate();
//5.处理返回结果
System.out.println(count);
//6.释放资源
JdbcUtils.close(pst,connection);
}
}
public class DQL_CURD {
/**
* 测试查询
*/
@Test
public void testSelect() throws Exception {
//1.获得连接
Connection connection = JdbcUtils.getConnection();
//2.获得预编译对象
String sql = "select username,id,password,money from tab_user where id > ? ";
PreparedStatement pst = connection.prepareStatement(sql);
pst.setInt(1, 1 );
/**
* 4.执行sql语句
*/
ResultSet resultSet = pst.executeQuery();
/**
* 5.处理结果
*/
while (resultSet.next()){ //判断下一行有没有数据 如果有 指针向下移动
//指针指向的当前行数据
int id = resultSet.getInt("id");
String dbUsername = resultSet.getString("username");
System.out.println(id +"@@"+dbUsername);
}
JdbcUtils.close(resultSet , pst,connection);
}
}
注意:
pst.executeUpdate() 表示pst本类的方法
pst.executeUpdate(sql ) 表示父类的方法
public class UserDaoImpl implements UserDao {
/**
* 根据用户名密码查询
* @param username
* @param password
* @return
*/
@Override
public User login(String username, String password) throws Exception {
//1.注册驱动 2.获得连接
Connection connection = JdbcUtils.getConnection();
//3.创建预编译对象
String sql = "select * from tab_user where username =? and password=?";
PreparedStatement pst = connection.prepareStatement(sql);
//设置参数
pst.setString(1 , username);
pst.setString(2 , password);
//4.执行sql
ResultSet resultSet = pst.executeQuery();
//5.处理结果
User user = null;
while (resultSet.next()){//判断有没有下一行数据
//构建user对象
user = new User(resultSet.getInt("id"), resultSet.getString("username") , resultSet.getString("password"), resultSet.getInt("money"));
break;//测试程序写上
}
//6.释放资源
JdbcUtils.close(resultSet,pst,connection);
return user;
}
}
上文中CURD代码问题在于: 每一次操作都要新创建连接,并且需要释放连接,而程序中反复的创建对象和关闭对象时非常浪费资源的。
连接池思想:
提前定义连接放入容器中,每次操作完后不释放资源 , 放回容器中即可。
连接池是有规范的 , 在jdk中已经提供了一个接口 DataSource
(数据源-连接池),要是想自定义连接池,则必须实现这个接口。
import com.itheima.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.LinkedList;
/**
* 自定义的连接池
* 目的:
* 1.以后对外提供连接方法
* 2.提供回收连接的方法
* 此连接池需要初始化 List : LinkedList ArrayList
*/
public class MyPool {
//定义了连接池
private static LinkedList<Connection > pool = new LinkedList<>();
static{//静态代码块初始化池
try {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
for(int i = 0 ; i < 10 ; i ++){
//2.获得连接
Connection connection = DriverManager.getConnection("jdbc:mysql:///test", "root", "root");
//放入池
pool.add(connection);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获得连接
* @return
*/
public static Connection getConnection(){
return pool.removeFirst();
}
/**
* 回收连接
*/
public static void close(Connection connection){
pool.add(connection);
}
}
//测试类
public class TestDemo {
public static void main(String[] args) {
for(int i = 0 ; i < 15 ; i ++){
//应该要自动扩容 , 等待机制
//从池中获得连接
Connection connection = MyPool.getConnection();
System.out.println( (i+1) + "@@" + connection );
//找到循环中的某一个连接 放回去 (必须放回去)
if(i==5){
MyPool.close(connection);
}
}
}
}
DBCP C3P0:已淘汰, 历史产品
HikariCP:日本产品(快) 作为了流行技术springboot默认数据源(DataSource)
Druid(德鲁伊):阿里产品, 提供很多其他组件,特点是稳定 ,速度有一定的保证
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidPooledConnection;
import com.mysql.jdbc.Driver;
import java.sql.SQLException;
/**
* 使用的任何技术其实都与导入的包中大部分跟接口名称或者技术有关系
* Druid : 连接池
* 数据源: DataSource
*
*/
public class DruidTestDemo1 {
public static void main(String[] args) throws SQLException {
//1.创建连接池
DruidDataSource dataSource = new DruidDataSource();
//设置参数 基本四项(必须设置)
dataSource.setDriverClassName("com.mysql.jdbc.Driver");//驱动
dataSource.setUrl("jdbc:mysql:///test");//连接
dataSource.setUsername("root");//用户名
dataSource.setPassword("root");//密码
//其他的可选项
dataSource.setMaxActive(10); //最大激活数
dataSource.setInitialSize(3); //初始个数
dataSource.setMaxWait(3000); //最大等待时间 单位毫秒
//2.获得连接
for(int i= 0 ; i< 10 ; i++){
DruidPooledConnection connection = dataSource.getConnection();
System.out.println( (i+1) +"@@" + connection);
}
}
}
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* Druid : 连接池
* 数据源: DataSource
* 标准版本
*/
public class DruidTestDemo3 {
public static void main(String[] args) throws Exception {
//读取配置文件 将配置文件交给工厂即可
//3.1加载流
//当前类.class.getClassLoader().getResourcesAsStream("配置文件的名称")
//当前类.class.getClassLoader() 获得类加载器对象 : 1.加载资源到内存 2.保证资源只被加载一次 双亲委派机制
InputStream is = DruidTestDemo3.class.getClassLoader().getResourceAsStream("druiddb.properties"); // * 10 内存只有一个对象
//FileInputStream is2 = new FileInputStream("路径"); * 10 读取十次文件 内存有十个对象
//3.2 创建Properties对象
Properties properties = new Properties();
//3.3 通过Properties 直接加载流对象
properties.load(is);
//1.创建连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
//2.获得连接
for(int i= 0 ; i< 10 ; i++){
Connection connection = dataSource.getConnection();
System.out.println( (i+1) +"@@" + connection);
}
}
}
配置文件
#所以的key不允许发生变化,所有的key来自于set方法 去掉set首字母小写
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///test
username=root
password=root
initialSize=3
maxActive=10
maxWait=2000
工具类
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
/**
* 1.构建数据源
* 2.需要提供从数据源中获得连接的方法
*/
public class DruidUtils {//类体不允许写代码的
//1.准备数据源
private static DataSource dataSource = null;
static { //方法体书写代码
try {
//3.1加载流
InputStream is = DruidTestDemo3.class.getClassLoader().getResourceAsStream("druiddb.properties"); // * 10 内存只有一个对象
//3.2 创建properties对象
Properties properties = new Properties();
//3.3 读取流
properties.load(is);
//4.创建数据源
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 2.需要提供从数据源中获得连接的方法
*/
public static Connection getConnection() throws SQLException {
//从数据源返回对象
return dataSource.getConnection();
}
}
测试代码
import java.sql.Connection;
import java.sql.SQLException;
public class TestDemo {
public static void main(String[] args) throws SQLException {
for(int i = 0 ; i < 10 ; i ++){
Connection connection = DruidUtils.getConnection();
System.out.println(i+1 + "@@" + connection);
if(i==5){//将第五个返回给连接池
connection.close();
// connection.close() 释放资源
// 任何数据源对close方法都有重写(增强) 归还连接
}
}
}
}