JDBC数据库连接三 Li.062

自定义JDBC框架 使用JDBC过程中有大量的重复代码, 核心功能仅仅执行一条SQL语句, 所以可以抽取一个JDBC模板类, 来封装一些方法(update query). 专门帮我们执行增删改查SQL语句. 将之前那些重复的操作, 都抽取到模板类中的方法里, 就能大简化使用步骤 源信息 DataeBaseMetaData: 数据库的源信息 java.sql.DataBaseMetaData 封装了整个数据库的综合信息 例如 String getDatabaseProductName(): 获取数据库产品的名称 例如int getDatabaseProductVersion: 获取数据库产品的版本号 ParameterMetaData: 参数源信息 java.sql.ParameterMetaData 封装的是预编译执行者对象中每个参数的类型和属性, 这个对象可以通过预编译执行者对象中的getParameterMetaData()方法来获取 int getParameterCount() 用于获取SQL语句中的参数个数 ResultSetMetaData: 结果集的源信息 java.sql.ResultSetMetaData: 封装的是结果集对象中列的类型和属性, 这个对象可以通过结果集对象中的getMetaData()方法来获取 int getColumnCount() 用于获取列的总数, String getColumnName(int i)用于获取列名 package com.test005; import com.lizicai.utils.DataSourceUtils; import javax.sql.DataSource; import java.io.IOException; import java.sql.*; public class JDBCTemplate { // 1. 定义参数变量(数据源, 连接对象, 执行者对象, 结果集对象 private DataSource dataSource; private Connection con; private PreparedStatement pst; private ResultSet rs; // 2. 通过有参构造为数据源赋值 public JDBCTemplate(DataSource dataSource){ this.dataSource = dataSource; } // 3. 定义update方法, 参数 sql语句 sql语句中的参数 public int update(String sql, Object... objs){ int result = 0; try { con = dataSource.getConnection(); pst = con.prepareStatement(sql); ParameterMetaData parameterMetaData = pst.getParameterMetaData(); int count = parameterMetaData.getParameterCount(); if( count != objs.length){ throw new RuntimeException("参数个数不匹配"); } for(int i=0; i< objs.length;i++){ pst.setObject(i+1,objs[i]); } result = pst.executeUpdate(); } catch (Exception e) { e.printStackTrace(); } finally { DataSourceUtils.close(con, pst); } return result; } } package com.test005; import com.lizicai.utils.DataSourceUtils; import org.junit.Test; public class JDBCTemplateTest1 { private JDBCTemplate template = new JDBCTemplate(DataSourceUtils.getDataSource()); @Test public void insert(){ String sql = "INSERT INTO student VALUES (?,?,?,?)"; Object[] params = {5,"周七",27,"1997-07-07"}; int result = template.update(sql, params); if(result != 0){ System.out.println("添加成功"); } else { System.out.println("添加失败"); } } @Test public void update(){ String sql = "UPDATE student SET age=? WHERE name=?"; Object[] params = {37,"周七"}; int result = template.update(sql, params); System.out.println(result); } @Test public void delete(){ String sql = "DELETE FROM student where name=?"; Object[] params = {"周七"}; int result = template.update(sql, params); System.out.println(result); } } 查询功能-框架编写 查询一条记录并封装对象的方法: queryForObject() ...

October 27, 2021&nbsp;·&nbsp;5 分钟&nbsp;·&nbsp;Lizicai

JDBC数据库连接二 Li.061

数据库连接池 数据库连接池 数据库连接池负责分配,管理和释放数据库连接,它允许应用程序重复使用一个现在的数据库连接,而不是再重新建立一个. 这项技术能明显提高对数据库操作的性能 DataSource接口概述 javax.sql.DataSource接口: 数据源(数据库连接池). Java官方提供的数据库连接池规范(接口) 如果想完成数据库连接池技术, 就必须实现DataSource接口 核心功能: 获取数据库连接对象: Connection getConnection() 自定义数据库连接池 定义一个类, 实现DataSource接口 定义一个容器, 用于保存多个Connection连接对象 定义静态代码块, 通过JDBC工具类获取10个连接保存到容器中 重写getConnection方法, 从容器中获取一个连接并返回 定义getSize方法, 用于获取窗口的大小并返回 package com.test001; import com.lizicai.utils.JDBCUtils; import javax.sql.DataSource; import java.io.PrintWriter; import java.sql.Connection; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.logging.Logger; public class MyDataSource implements DataSource { // 1. 准备窗口, 用于保存多个连接对象, 变成线程安全的 private static List<Connection> pool = Collections.synchronizedList(new ArrayList<>()); // 2. 定义代码块, 通过工具类获取10个连接对象 static { for(int i=0;i<10;i++){ Connection con = JDBCUtils.getConnection(); pool.add(con); } } //3 重写getConnection() , 用于获取一个连接对象 @Override public Connection getConnection() throws SQLException { if( pool.size()> 0){ Connection con = pool.remove(0); return con; } else { throw new RuntimeException("连接数量已用尽"); } } // 4. 定义getSize方法, 获取连接池窗口的大小 public int getSize(){ return pool.size(); } } package com.test001; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class MyDataSourceTest { public static void main(String[] args) throws SQLException { MyDataSource pool = new MyDataSource(); System.out.println(pool.getSize()); Connection con = pool.getConnection(); String sql = "SELECT * FROM student"; PreparedStatement pst = con.prepareStatement(sql); ResultSet rs = pst.executeQuery(); while (rs.next()){ System.out.println(rs.getInt("sid")+"\t"+rs.getString("NAME") +"\t"+rs.getInt("age")+"\t"+rs.getDate("birthday")); } rs.close(); pst.close(); con.close(); System.out.println(pool.getSize()); } } 归还方式 归还数据库连接的方式 继承方式 装饰设计模式 我在适配器设计模式 动态 代理方式 归还方式 - 继承方式 继承方式归还数据库连接的思想。 通过打印连接对象,发现 DriverManager 获取的连接实现类是 JDBC4Connection 那我们就可以自定义一个类,继承JDBC4Connection这个类,重写closeQ 方法,完成连接对象的归还 继承方式归还数据库连接的实现步骤。 定义一个类,继承JDBC4Connection。 定义 Connection 连接对象和连接池容器对象的成员变量。 通过有参构造方法完成对成员变量的赋值。 重写cose 方法,将连接对象添加到池中。 继承方式日还数据库连接存任的问题。 通过查看 JDBC 工具类获取连接的方法发现:我们星然白定义了一个子类,完成了归还连接的操作。但是 DriverManager 获取的还是JDBC4Connection这个对象,并不是我们的子类对象,而我们又不能整体去修改驱动包中类的功能,所继承这种方式行不通! 归还连接 - 装饰设计模式 装饰设计模式日还数据库连接的思想。 我们可以自定义一个类,实现 Connection接口。这样就具备了和 JDBC4Connection相同的行为了 重写close()方法,完成连接的归还。其余的功能还调用 mysql驱动包实现类原有的方法即可 装饰设计模式归还数据库连接的实现步骤。 定义一个类,实现 Connection 接口 定义 Connection 连接对象和连接池容器对象的成员变量 通过有参构造方法完成对成员变量的赋值 重写close()方法,将连接对象添加到池中 剩余方法,只需要调用mysql驱动包的连接对象完成即可 在自定义连接池中,将获取的连接对象通过自定义连接对象进行包装 装饰设计模式归还数据库连接存在的问题。 实现 Connection接口后,有大量的方法需要在自定义类中进行重写 package com.test002; import java.sql.*; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.concurrent.Executor; /** * 1. 定义一个类, 实现Connection接口 * 2. 定义连接对象和连接池容器对象的成员变量 * 3. 通过有参构造方法为成员变量赋值 * 4. 重写close()方法, 完成归还连接 * 5. 剩余方法, 还是调用原有的连接对象中的功能即可 */ public class MyConnection2 implements Connection { private Connection con; private List<Connection> pool; public MyConnection2(Connection con, List<Connection> pool) { this.con = con; this.pool = pool; } @Override public void close() throws SQLException { pool.add(con); } } public class MyDataSource implements DataSource { //3 重写getConnection() , 用于获取一个连接对象 @Override public Connection getConnection() throws SQLException { if( pool.size()> 0){ Connection con = pool.remove(0); MyConnection2 myCon = new MyConnection2(con,pool); return myCon; } else { throw new RuntimeException("连接数量已用尽"); } } } 归还连接 - 适配器设计模式 适配器设计模式归还数据库连接的思想。 我们可以提供一个适配器类,实现 Connection 接口,将所有方法进行实现(除了close方法) 自定义连接类只需要继承这个适配器类,重写需要改进的close0 方法即可 适配器设计模式归还数据库连接的实现步骤。 定义一个适配器类, 实现 Connection 接口。 定义 Connection 连接对象的成员变量。 通过有参构造方法完成对成员变量的赋值。 重写所有方法(除了close), 调用mysq驱动包的连接对象完成即可。 定义一个连接类,继承适配器类。 定义 Connection 连接对象和连接池容器对象的成员变量,并通过有参构造进行赋值。 重写close()方法,完成归还连接。 在自定义连接池中,将获取的连接对象通过自定义连接对象进行包装。 适配器设计模式归还数据库连接存在的问题。 自定义连接类虽然很简洁, 但我在楼下器还是自己编写的, 也比较麻烦 public abstract class MyAdapter implements Connection { private Connection con; public MyAdapter(Connection con){ this.con = con; } //省略其他实现的方法 } package com.test002; import java.sql.Connection; import java.sql.SQLException; import java.util.List; public class MyConnection3 extends MyAdapter{ private Connection con; private List<Connection> pool; public MyConnection3(Connection con, List<Connection> pool) { super(con); this.con = con; this.pool = pool; } @Override public void close() throws SQLException { pool.add(con); } } 动态代理 动态代理: 在不改变目标对象方法的情况下对方法进行增强 组成 被代理对象: 真实的对象 代理对象: 内存的一个对象 要求 代理对象必须和被代理对象实现相同的接口 实现 Proxy.newProxyInstance() package com.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class Test { public static void main(String[] args) { Student stu = new Student(); stu.eat("饭"); stu.study(); /** * 要求: 在不改Student类中任何代码前提下, 通过study方法输出一句话 * 类加载器: 和被代理对象使用相同的类加载器 * 接口类型Class数组: 和被代理对象使用相同接口 * 代理规则: 完成代理增加的功能 */ StudentInterface proxyStu = (StudentInterface) Proxy.newProxyInstance(stu.getClass().getClassLoader(), new Class[]{StudentInterface.class}, new InvocationHandler() { /** * 执行student类中所有方法都会经过invoke方法 * 对method方法进行判断 * 如果是study, 则对其增强 * 如果不是, 还调用学生对象原的功能即可 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getName().equals("study")){ System.out.println("学快点"); return null; } else { return method.invoke(stu, args); } } }); proxyStu.eat("米饭"); proxyStu.study(); } } 归还连接 - 动态代理方式 动态代理方式归还数据库连接的思想。 我们可以通过 Proxy 来完成对 Connection 实现类对象的代理 代理过程中判断如果执行的是 close 方法,就将连接归还池中。如果是其他方法则调用连接对象原来 的功能即可 动态代理方式归还数据库连接的实现步骤。 定义一个类,实现 DataSource接口 定义一个容器,用于保存多个Connection连接对象 定义静态代码块,通过JDBC工具类获取 10 个连接保存到容器中 重马getConnection 方法,从容器中获取一个连接 通过 Proxy 代理,如果是close 方法,就将连接归还池中。如果是其他方法则调用原有功能 定义 getsize 方法,用于获取容器的大小并返回 动态代理方式归还数据库连接存在的问题。 我们自己写的连接池技术不够完善,功能也不够强大 package com.test001; import com.lizicai.utils.JDBCUtils; import com.test002.MyConnection2; import com.test002.MyConnection3; import javax.sql.DataSource; import java.io.PrintWriter; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.sql.Connection; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.logging.Logger; public class MyDataSource implements DataSource { // 1. 准备窗口, 用于保存多个连接对象, 更改List为线程安全的 private static List<Connection> pool = Collections.synchronizedList(new ArrayList<>()); // 2. 定义代码块, 通过工具类获取10个连接对象 static { for(int i=0;i<10;i++){ Connection con = JDBCUtils.getConnection(); pool.add(con); } } // 动态代理归还Connection @Override public Connection getConnection() throws SQLException { if( pool.size()> 0){ Connection con = pool.remove(0); Connection proxyCon =(Connection) Proxy.newProxyInstance(con.getClass().getClassLoader(), new Class[]{Connection.class}, new InvocationHandler() { /** 执行Connection 实现类连接对象所有的方法都会经过invoke * 如果是close方法, 归还连接 * 如果不是, 直接执行连接对象原有的功能即可 * */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getName().equals("close")){ pool.add(con); return null; } else { return method.invoke(con, args); } } }); return proxyCon; } else { throw new RuntimeException("连接数量已用尽"); } } // 4. 定义getSize方法, 获取连接池窗口的大小 public int getSize(){ return pool.size(); } @Override public Connection getConnection(String username, String password) throws SQLException { return null; } @Override public <T> T unwrap(Class<T> iface) throws SQLException { return null; } @Override public boolean isWrapperFor(Class<?> iface) throws SQLException { return false; } @Override public PrintWriter getLogWriter() throws SQLException { return null; } @Override public void setLogWriter(PrintWriter out) throws SQLException { } @Override public void setLoginTimeout(int seconds) throws SQLException { } @Override public int getLoginTimeout() throws SQLException { return 0; } @Override public Logger getParentLogger() throws SQLFeatureNotSupportedException { return null; } } 使用C3P0数据库连接池的使用步骤 导入Jar包, c3p0-0.9.5.5.jar和mchange-commons-java-0.2.19.jar 导入配置文件到src目录下 创建C3P0连接池对象 获取数据库连接进行使用 C3P0下载, 开头/c3p0-bin/c3p0-0.9.5.5/c3p0-0.9.5.5.bin.zip ...

October 26, 2021&nbsp;·&nbsp;7 分钟&nbsp;·&nbsp;Lizicai

JDBC数据库连接一 Li.060

JDBC 的概念 JDBC的概念 JDBC(Java DataBase Connectivity Java数据库连接)是一种用于执行SQL语句的Java API, 可以为多种关系型数据库提供统一访问,它是由一组用Java语文编写的类和接口组成的 JDBC的本质 其实就是Java官方提供的一套规范(接口). 用于帮助开发人员快速实现不同关系型数据库的连接. JDBC 的快速入门程序 导入Jar包 注册驱动 获取数据库连接 获取执行者对象 执行SQL语句并返回结果 处理结果 释放资源 JDBC包下载 package com.lizicai.jdbc; import java.sql.*; public class JDBCDemo1 { public static void main(String[] args) throws ClassNotFoundException, SQLException { // 导入Jar包, 已导入 // 注册驱动, 也可以省略掉 Class.forName("org.mariadb.jdbc.Driver"); // 获取数据库连接 Connection connection = DriverManager.getConnection ("jdbc:mariadb://192.168.0.100:3306/db10", "root", "rootPassword"); // 获取执行者对象 Statement statement = connection.createStatement(); // 执行SQL语句并返回结果 String sql = "SELECT * FROM city"; ResultSet rs = statement.executeQuery(sql); // 处理结果 while (rs.next()){ System.out.println(rs.getInt("id")+","+rs.getString("NAME")+","+rs.getInt("VERSION")); } // 释放资源 connection.close(); statement.close(); } } DriverManager DriverManger驱动管理对象 注册驱动 注册给定的驱动程序: static void registerDriver(Driver driver) 写代码使用: Class.forName(“org.mariadb.jdbc.Driver”) 在org.mariadb.jdbc.Driver类中存在静态代码块 static { try { DriverManager.registerDriver(new Driver(), new DeRegister()); } catch (SQLException var1) { throw new RuntimeException("Could not register driver", var1); } } 不需要通过DriverManager调用静态方法registerDriver(),因为只要Driver类被使用,则会执行其静态代码块完成注册驱动 ...

October 24, 2021&nbsp;·&nbsp;7 分钟&nbsp;·&nbsp;Lizicai