当前位置:网站首页>详解JDBC的实现与优化(万字详解)

详解JDBC的实现与优化(万字详解)

2022-08-10 23:55:00 牛牛最爱喝兽奶

JDBC详解

在这里插入图片描述

简介

在这里插入图片描述

JDBC 指 Java 数据库连接,是一种标准Java应用编程接口( JAVA API),用来连接 Java 编程语言和广泛的数据库。从根本上来说,JDBC 是一种规范,它提供了一套完整的接口,允许便携式访问到底层数据库,因此可以用 Java 编写不同类型的可执行文件。
执行流程:
连接数据源,如:数据库。
为数据库传递查询和更新指令。
处理数据库响应并返回的结果。

所需的接口和类

1.Connection接口

Connection 对象的数据库能够提供信息描述其表、所支持的 SQL 语法、存储过程和此连接的功能等。默认情况下,Connection 对象处于自动提交模式下,这意味着它在执行每个语句后都会自动提交更改。如果禁用自动提交模式,为了提交更改,必须显式调用 commit 方法;否则无法保存数据库更改。
createStatement()方法:
创建一个 Statement 对象来将 SQL 语句发送到数据库。没有参数的 SQL 语句通常使用 Statement 对象执行。如果多次执行相同的 SQL 语句,使用 PreparedStatement 对象可能更有效。
getMetaData()方法:
获取 DatabaseMetaData 对象,该对象包含关于 Connection 对象连接到的数据库的元数据。元数据包括关于数据库的表、受支持的 SQL 语法、存储过程、此连接的功能等信息。
rollback(Savepoint savepoint) 方法:
取消在当前事务中进行的所有更改,并释放此 Connection 对象当前保存的所有数据库锁定。此方法应该只在已禁用自动提交模式时使用。
setAutoCommit(boolean autoCommit) 方法:
将此连接的自动提交模式设置为给定状态。如果连接处于自动提交模式下,则将执行其所有 SQL 语句,并将这些语句作为单独的事务提交。否则,其 SQL 语句将成组地进入通过调用 commit 方法或 rollback 方法终止的事务中。默认情况下,新的连接处于自动提交模式下。
Savepoint setSavepoint(String name)方法:
添加事务回滚的点,在当前事务中创建一个具有给定名称的保存点,并返回表示它的新 Savepoint 对象。

2. Statement接口

用于执行静态 SQL 语句并返回它所生成结果的对象。
在默认情况下,同一时间每个 Statement 对象在只能打开一个 ResultSet 对象。因此,如果读取一个 ResultSet 对象与读取另一个交叉,则这两个对象必须是由不同的 Statement 对象生成的。如果存在某个语句的打开的当前 ResultSet 对象,则 Statement 接口中的所有执行方法都会隐式关闭它。
executeUpdate(String sql) 方法:
执行给定 SQL 语句,该语句可能为 INSERT、UPDATE 或 DELETE 语句,或者不返回任何内容的 SQL 语句(如 SQL DDL 语句)。
executeQuery(String sql) 方法:
执行给定的 SQL 语句,该语句返回单个 ResultSet 对象。

3.ResultSet接口

表示数据库结果集的数据表,通常通过执行查询数据库的语句生成。
ResultSet 对象具有指向其当前数据行的指针。最初,指针被置于第一行之前。next 方法将指针移动到下一行;因为该方法在 ResultSet 对象中没有下一行时返回 false,所以可以在 while 循环中使用它来迭代结果集。
默认的 ResultSet 对象不可更新,仅有一个向前移动的指针。因此,只能迭代它一次,并且只能按从第一行到最后一行的顺序进行。可以生成可滚动和/或可更新的 ResultSet 对象。以下代码片段(其中 con 为有效的 Connection 对象)演示了如何生成可滚动且不受其他更新影响的、可更新的结果集。请参阅 ResultSet 字段以了解其他选项。

Statement stmt = con.createStatement(
                                      ResultSet.TYPE_SCROLL_INSENSITIVE,
                                      ResultSet.CONCUR_UPDATABLE);
       ResultSet rs = stmt.executeQuery("SELECT a, b FROM TABLE2");
       // rs will be scrollable, will not show changes made by others,
       // and will be updatable

next() 方法:
判断resultset的接受结果,采用循环遍历,查询结果!在知道数据库表里参数类型的情况下,采用resultset的getint方法。

Properties类(父类是hashtable)

Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。
一个属性列表可包含另一个属性列表作为它的“默认值”;如果未能在原有的属性列表中搜索到属性键,则搜索第二个属性列表。
因为 Properties 继承于 Hashtable,所以可对 Properties 对象应用 put 和 putAll 方法。但强烈反对使用这两个方法,因为它们允许调用方插入其键或值不是 Strings 的项。相反,应该使用 setProperty 方法。如果在“有危险”的 Properties 对象(即包含非 String 的键或值)上调用 store 或 save 方法,则该调用将失败。
load 和 store 方法按下面所指定的、简单的面向行的格式加载和存储属性。此格式使用 ISO 8859-1 字符编码。可以使用 Unicode 转义符来编写此编码中无法直接表示的字符;转义序列中只允许单个 ‘u’ 字符。可使用 native2ascii 工具对属性文件和其他字符编码进行相互转换。
作用:
Properties(Java.util.Properties),该类主要用于读取Java的配置文件,不同的编程语言有自己所支持的配置文件,配置文件中很多变量是经常改变的,为了方便用户的配置,能让用户够脱离程序本身去修改相关的变量设置。就像在Java中,其配置文件常为.properties文件,是以键值对的形式进行参数配置的。
getProperty(String key) 方法:
用指定的键在此属性列表中搜索属性。
setProperty(String key, String value) 方法:
调用 Hashtable 的方法 put。

DataSource类

该工厂用于提供到此 DataSource 对象表示的物理数据源的连接。作为 DriverManager 设施的替代项,DataSource 对象是获取连接的首选方法。实现 DataSource 接口的对象通常在基于 JavaTM Naming and Directory Interface (JNDI) API 的命名服务中注册。
DataSource 接口由驱动程序供应商实现。共有三种类型的实现:
1.基本实现 - 生成标准 Connection 对象
2.连接池实现 - 生成自动参与连接池的 Connection 对象。此实现与中间层连接池管理器一起使用。
3.分布式事务实现 - 生成一个 Connection 对象,该对象可用于分布式事务,并且几乎始终参与连接池。此实现与中间层事务管理器一起使用,并且几乎始终与连接池管理器一起使用。
DataSource 对象的属性在需要时可以修改。例如,如果将数据源移动到另一个服务器,则可更改与服务器相关的属性。其优点是,因为可以更改数据源的属性,所以任何访问该数据源的代码都无需更改。
通过 DataSource 对象访问的驱动程序不会向 DriverManager 注册。通过查找操作检索 DataSource 对象,然后使用该对象创建 Connection 对象。使用基本的实现,通过 DataSource 对象获取的连接与通过 DriverManager 设施获取的连接相同。
getConnection() 方法:
尝试建立与此 DataSource 对象表示的数据源的连接。

DriverManager类

管理一组 JDBC 驱动程序的基本服务。
注:DataSource 接口是 JDBC 2.0 API 中的新增内容,它提供了连接到数据源的另一种方法。使用 DataSource 对象是连接到数据源的首选方法。
getConnection(String url) 方法:
试图建立到给定数据库 URL 的连接。

PreparedStatement接口(Statement的子接口)

表示预编译的 SQL 语句的对象。
SQL 语句被预编译并且存储在 PreparedStatement 对象中。然后可以使用此对象高效地多次执行该语句。
注:用来设置 IN 参数值的 setter 方法(setShort、setString 等等)必须指定与输入参数的已定义 SQL 类型兼容的类型。例如,如果 IN 参数具有 SQL 类型 INTEGER,那么应该使用 setInt 方法。
如果需要任意参数类型转换,使用 setObject 方法时应该将目标 SQL 类型作为其参数的类型。
在以下设置参数的示例中,con 表示一个活动连接:

PreparedStatement pstmt = con.prepareStatement("UPDATE EMPLOYEES
           SET SALARY = ? WHERE ID = ?");
   pstmt.setBigDecimal(1, 153833.00)
   pstmt.setInt(2, 110592)

executeQuery() 方法:
在此 PreparedStatement 对象中执行 SQL 查询,并返回该查询生成的 ResultSet 对象。
getMetaData() 方法:
检索包含有关 ResultSet 对象的列消息的 ResultSetMetaData 对象,ResultSet 对象将在执行此 PreparedStatement 对象时返回。

基础版的JDBC

作为简单功能的实现,并不是通用的方法每次实现功能单一,没有实现功能统一。

package jdbc;
import java.sql.*;
public class Connect {
    
	public static void main(String [] args) throws Exception{
    
		//Class.forName("com.mysql.cj.jdbc.Driver");
		 //1.注册jdbc驱动
		  String driverName="com.mysql.cj.jdbc.Driver";
		//2.获取连接对象
		  String dbURL="jdbc:mysql://127.0.0.1:3306/mytest?useSSL=false&serverTimezone=UTC";
		  String userName="root";		
		  String userPwd="123456";//数据库密码(自己设置的)
	try{
    
		Class.forName(driverName);
		System.out.println("加载驱动成功!");
	}catch(Exception e){
    
		e.printStackTrace();
		System.out.println("加载驱动失败!");
	}
	try{
    
		Connection dbConn=DriverManager.getConnection(dbURL,userName,userPwd);
		System.out.println("连接数据库成功!");
	}catch(Exception e){
    
		e.printStackTrace();
		System.out.print("MySQL80连接失败!");
	}	
		Connection Conn=DriverManager.getConnection(dbURL,userName,userPwd);
		Statement stat=Conn.createStatement();
		String sql = "select * from tb1";
		ResultSet result = stat.executeQuery(sql);
		while(result.next()) {
    
			System.out.print("username:"+result.getString("username")+"|");
			System.out.println("age:"+result.getInt("age"));
			
		}
		result.close();
		stat.close();
		Conn.close();
	}

}

通用版本的JDBC(采用Statement)

在这里插入图片描述

1.JDBCUtils通用抽象类:

package mysql.jdbc;

import fs.反射.ReflectionUtils;
import org.apache.commons.dbcp.BasicDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

public abstract class JDBCUtils {
    
    private static String url = null;
    private static String name =null;
    private static String password = null;
    private Connection conn= null;
    private Statement st = null;
    private  PreparedStatement ps =null;
    private ResultSet rs = null;
    private ResultSetMetaData rsm =null;
    private static DataSource dataSource =null;

    static{
    
        Properties properties = new Properties();
        InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("db.properties");
       		
        try {
    
            properties.load(is);
           String driver  =properties.getProperty("driverClassName");		     			url = properties.getProperty("url");		
       		name = properties.getProperty("username");		
       		password = properties.getProperty("password");
        } catch (IOException e) {
    
            e.printStackTrace();
        } catch (Exception e) {
    
            e.printStackTrace();
        }

    }
    public static Connection getconnection() throws SQLException {
    //获取链接
        return DriverManager.getConnection(url,name,password);

    }
    //---------------statement方法
    public boolean update(String sql) {
    //数据库的修改
        int bRet =0;
        try {
    
            conn = getconnection();
            st = conn.createStatement();
            bRet = st.executeUpdate(sql);
            //System.out.println(bRet);
        } catch (SQLException e) {
    
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }
        return bRet!=0;

    }
    public static void jdbcClose(Connection conn,Statement st,ResultSet rs) {
    //jbdc数据库关闭
        try {
    
            rs.close();

            if(rs!=null){
    
                rs = null;
            }
        } catch (SQLException e) {
    
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally{
    
            try {
    
                st.close();
                if(st!=null){
    
                    st = null;
                }

            } catch (SQLException e) {
    
                // TODO Auto-generated catch block
                e.printStackTrace();
            }finally{
    
                try {
    
                    conn.close();
                    if(conn!=null){
    
                        conn = null;
                    }
                } catch (SQLException e) {
    
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
    public static void jdbcClose(Connection conn,Statement st) {
    
        try {
    
            st.close();
            if(st!=null){
    
                st = null;
            }
        } catch (SQLException e) {
    
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally{
    
            try {
    
                conn.close();
                if(conn!=null){
    
                    conn = null;
                }
            } catch (SQLException e) {
    
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    }
    public boolean delete(String sql) {
    //删除
        int bRet =0;
        try {
    conn = getconnection();
            st = conn.createStatement();
            bRet = st.executeUpdate(sql);
            //System.out.println(bRet);
        } catch (SQLException e) {
    
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }
        return bRet!=0;

    }
    public  boolean inster(String sql) throws SQLException {
    
        int i = 0;
        try {
    
            conn = getconnection();
            st = conn.createStatement();
            i = st.executeUpdate(sql);
        } catch (SQLException e) {
    
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }

        return i!=0;
    }
    //查询单条 多条
    public abstract Object reToObj(ResultSet rs);
    public abstract List<Object> rsToList(ResultSet rs);

    public Object queryOne(String sql) throws SQLException {
    
        Object obj =null;
        conn = getconnection();
        st = conn.createStatement();
        rs = st.executeQuery(sql);
        obj = reToObj(rs);
        return obj;
    }

    public List<Object> queryAll(String sql) throws SQLException {
    
        List<Object> list =null;
        conn = getconnection();
        st = conn.createStatement();
        rs = st.executeQuery(sql);
        list = rsToList(rs);
        return list;
    }

}

2.db.properties文件
方便统一规划,管理驱动、用户、密码、以及数据库表。

driverClassName = com.mysql.cj.jdbc.Driver
url = jdbc:mysql://127.0.0.1:3306/test1
username = root
password = 123456

3.User类
执行对象

package jdbc;
public class User {
    
	private int id;
	private String username;
	private String password;
	
	public User() {
    
		// TODO Auto-generated constructor stub
	}

	public User(int id, String username, String password) {
    
		super();
		this.id = id;
		this.username = username;
		this.password = password;
	}
	public int getId() {
    
		return id;
	}
	public String getUserName() {
    
		return username;
	}
	public String getPassWord() {
    
		return password;
	}
	public void setId(int id) {
    
		this.id = id;
		
	}
	public void setUsername(String userName) {
    
		this.username = userName;
		
	}
	public void setpassword(String passWord) {
    
		this.password = passWord;
		
	}
	@Override
	public String toString() {
    
		
		
		return "id:["+id+"]userName:["+username+"]passWord:["+password+"]";
	}

}

4.UserDao类
操作类对接数据库

package jdbc;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class UserDao extends JDBCUtils{
    
		User user = new User();
	@Override
	public Object reToObj(ResultSet rs) {
    
		try {
    
			if(rs.next()) {
    
				user.setId(rs.getInt("id"));
				user.setUsername(rs.getString("username"));
				user.setpassword(rs.getString("password"));
			}
		} catch (SQLException e) {
    
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
		
		return (Object)user;
	}

	@Override
	public List<Object> rsToList(ResultSet rs) {
    
		List<Object> list = new ArrayList<>();
		try {
    
			while(rs.next()) {
    
				User user = new User();
				user.setId(rs.getInt("id"));
				user.setUsername(rs.getString("username"));
				user.setpassword(rs.getString("password"));
				list.add(user);	
			}
		} catch (SQLException e) {
    
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
		return list;
	}

}

5.UserService接口
用户服务层

package jdbc;

import java.util.List;

public interface UserService {
    
	
	public User querUserName(String username);
	public List<Object> queryUserNameAll(String username);
	public boolean insert(User user);
	public boolean update(User user);
	public boolean delete(int id);

}

6.UserServiceImpl类
数据链路层作为用户数据库连接桥梁

package jdbc;

import java.sql.SQLException;
import java.util.List;

public class UserServiceImpl implements UserService {
    

	UserDao userdao = new UserDao();
	@Override
	public User querUserName(String username) {
    
		// TODO 自动生成的方法存根
		Object obj = null;
		User user =null;
		String sql = "select * from user where username ='"+username+"'";
		try {
    
			obj = userdao.queryOne(sql);
		} catch (SQLException e) {
    
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
		user = (User)obj;
		return user;
	}

	@Override
	public List<Object> queryUserNameAll(String username) {
    
		// TODO 自动生成的方法存根
		List<Object> list = null;
		String sql = "select * from user where username ='"+username+"'";
		try {
    
			list = userdao.queryAll(sql);
		} catch (SQLException e) {
    
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
		return list;
	}

	@Override
	public boolean insert(User user) {
    
		// TODO 自动生成的方法存根
		String sql = "insert into user values("+user.getId()+",'"+user.getUserName()+"',"+"'"+user.getPassWord()+"');";
		 //insert into user values(110,'xiaohua','123456');
		boolean boo=false;
		try {
    
			boo = userdao.inster(sql);
		} catch (SQLException e) {
    
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
		return boo;
	}

	@Override
	public boolean update(User user) {
    
		// TODO 自动生成的方法存根
		String sql = "update user set "+"username='"+user.getUserName()+"',password='"+user.getPassWord()+"'where id="+user.getId()+";";
		boolean boo=false;
		boo = userdao.update(sql);
		return boo;
	}

	@Override
	public boolean delete(int id) {
    
		// TODO 自动生成的方法存根
		String sql = "delete from user where id="+id+";";
		boolean boo=false;
		boo = userdao.delete(sql);
		return boo;
	}


}

7.TestDB类
测试用例

package jdbc;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

public class TestDB extends Object{
    
	public static void main(String[] args) throws  ExecutionException, InterruptedException, SQLException {
    
		UserServiceImpl userS = new UserServiceImpl();
		User user = userS.querUserName("xia");
		User u1 = new User(121,"hex","123456");
		User u2 = new User(121,"h","123456");
		System.out.println("插入"+userS.insert(u1)+"删除:"+userS.delete(0)+"修改:"+userS.update(u2));	 
		System.out.println(user);
}

结果:
数据库执行前数据有:
在这里插入图片描述
执行语句之后:
输出:
在这里插入图片描述
在这里插入图片描述
采用普通版本的Statement去执行提交sql语句,会存在一个sql语句注入的问题,这个问题是由数据库厂商所造成的一个问题。在为输入正常目标的语句下也可以查阅到结果。所以采用这种方法会存在一定的bug!

提升版的JDBC连接(PreparedStatement+dbcp)

接下来的会对以上问题进行优化,采用PreparedStatement,PreparedStatement是预编译的,对于批量处理可以大大提高效率. 也叫JDBC存储过程。加上数据库连接池dbcp,不要频繁的去对数据库的连接。采用数据库连接池的方式需要jar包的支持!DataSource取代了DriverManager去获取数据库连接
在这里插入图片描述
1.数据库连接池的创建
定义为静态代码块,只执行一次!

static{
    
        Properties properties = new Properties();
        InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("db.properties");
        try {
    
            properties.load(is);
            dataSource = BasicDataSourceFactory.createDataSource(properties);
        } catch (IOException e) {
    
            e.printStackTrace();
        } catch (Exception e) {
    
            e.printStackTrace();
        }

    }

2.获取连接

    public static Connection getconnection() throws SQLException {
    
        return dataSource.getConnection();

    }

3.基于PreparedStatement 的通用查询方法

public <T> T selectPre(Class<T> clazz,String sql,Object...args) throws SQLException, InstantiationException, IllegalAccessException {
    
        conn = getconnection();//获取连接
        ps = conn.prepareStatement(sql);//获取PrepareStatement对象,预处理
        T entity =null;//声明一个T类型的返回值对象
        for(int i=0;i<args.length;i++) {
    
            ps.setObject(i+1, args[i]);//添加查询相关条件
        }
        rs = ps.executeQuery();//提交查询条件,获取resultSet对象
        rsm = ps.getMetaData();//获取ResultSetMetaData,数据库表里的相关信息
        Map<String,Object> map = new HashMap();//创建HashMap类的map对象
        if(rs.next()) {
    //rs结果集遍历
            for(int i=0;i<rsm.getColumnCount();i++) {
    //getColumnCount获取表的列数
                String label = rsm.getColumnLabel(i+1);//获取列名
                Object value = rs.getObject(label);//根据列名去获取里面的数据
                map.put(label, value);//添加键值对
            }
        }
        if(map.size()>0) {
    //如果查询到结果
            entity = clazz.newInstance();//获取一个T类型实例
            for(Map.Entry<String, Object> entry:map.entrySet()) {
    //调取Map的内部类entry对象
                String field = entry.getKey();//获取
                Object value = entry.getValue();//获取值
                ReflectionUtils.setFieldValue(entity,field,value);//通过反射的方法去为对象赋值
            }
        }
        return entity;//返回对象
    }

4.基于PreparedStatement 的通用修改、删除、方法

    public  void update(String sql,Object...args) throws SQLException {
    
        conn = getconnection();
        ps = conn.prepareStatement(sql);
        for(int i=0;i<args.length;i++) {
    
            ps.setObject(i+1, args[i]);
        }
        int bRET = ps.executeUpdate();//修改的行
        System.out.println(bRET);


    }
原网站

版权声明
本文为[牛牛最爱喝兽奶]所创,转载请带上原文链接,感谢
https://blog.csdn.net/cout_s/article/details/119760499