文章目录
一、JDBC下载与导入
二、获取连接
三、PreparedStatement类操作数据库
四、操作BLOB类型字段(图片视频等)
五、Batch批量插入
一、JDBC下载与导入
1、jar包下载
👇👇👇划至文末有mysql-connector-java-8.0.27.jar的阿里云盘链接🔗,已经只留下有用的jar包文件了,下载即可用。
2、导入(IDEA)
①在工程下新建一个文件夹(可以命名为libs)
②将下载的jar包复制粘贴到该文件夹里面,然后右键该文件夹选择"Add as Library"
二、获取连接
1、创建配置文件jdbc.properties
配置信息如下:
user=用户(root) password=数据库密码 url=jdbc:mysql://localhost:3306/test?useUnicode=true&character=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true driverClass=com.mysql.cj.jdbc.Driver
说明:
使用配置文件的好处:实现了代码和数据的分离,如果需要修改配置信息,直接在配置文件中修改,不需要深入代码;如果修改了配置信息,省去重新编译的过程。
2、使用DriverManager获取连接
import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.util.Properties; /** * @Author: Yeman * @Date: 2022-01-11-21:51 * @Description: */ public class jdbcLinked { public static void main(String[] args) throws Exception { //1、加载配置文件 InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties"); Properties pros = new Properties(); pros.load(is); //2、读取配置信息 String user = pros.getProperty("user"); String password = pros.getProperty("password"); String url = pros.getProperty("url"); String driverClass = pros.getProperty("driverClass"); //3、加载驱动 Class.forName(driverClass); //4、获取连接 Connection conn = DriverManager.getConnection(url, user, password); System.out.println(conn); } }
三、PreparedStatement类操作数据库
1、PreparedStatement介绍
①通过调用获取连接时生成的 Connection 对象的 preparedStatement(String sql) 方法获取 PreparedStatement 对象。
②PreparedStatement 接口是 Statement 的子接口,它表示一条预编译过的 SQL 语句。
③PreparedStatement 对象所代表的 SQL 语句中的参数用问号(?)来表示,调用PreparedStatement 对象的 setXxx() 方法来设置这些参数。setXxx() 方法有两个参数,第一个参数是要设置的 SQL 语句中的参数的索引(从 1 开始),第二个是设置的 SQL 语句中的参数的值。
2、PreparedStatement vs Statement
①PreparedStatement 可以防止 SQL 注入问题,且提高代码的可读性和可维护性。
②PreparedStatement 能最大可能提高性能:
DBServer会对预编译语句提供性能优化。因为预编译语句有可能被重复调用,所以语句在被DBServer的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中就会得到执行。在statement语句中,即使是相同操作但因为数据内容不一样,所以整个语句本身不能匹配,没有缓存语句的意义,事实是没有数据库会对普通语句编译后的执行代码缓存。这样每执行一次都要对传入的语句编译一次。(语法检查,语义检查,翻译成二进制命令,缓存)
③PreparedStatement 便于操作BLOB类型字段(图片视频等文件数据)。
④PreparedStatement 更适于批量操作。
3、Java与SQL对应数据类型转换表
4、使用PreparedStatement实现通用增、删、改操作
这里所说的通用是针对于同一数据库下的不同表或同一张表的不同DML。
①工具类(包括获取连接,通用增删改,关闭连接)
package utils; import java.io.InputStream; import java.sql.*; import java.util.Properties; /** * @Author: Yeman * @Date: 2022-01-12-14:34 * @Description:操作数据库的工具类 */ public class jdbcUtils { /** * @Description 获取数据库连接 * @Param [] * @return java.sql.Connection **/ public static Connection getSqlConnection() throws Exception{ //1、加载配置文件 InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties"); Properties pros = new Properties(); pros.load(is); //2、读取配置信息 String user = pros.getProperty("user"); String password = pros.getProperty("password"); String url = pros.getProperty("url"); String driverClass = pros.getProperty("driverClass"); //3、加载驱动 Class.forName(driverClass); //4、获取连接 Connection conn = DriverManager.getConnection(url, user, password); return conn; } /** * @Description 关闭连接和Statement资源 * @Param [conn, ps] * @return void **/ public static void closeResource(Connection conn, Statement ps){ try { if (ps != null) ps.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } //重载 public static void closeResource(Connection conn, Statement ps, ResultSet res){ try { if (ps != null) ps.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (res != null) res.close(); } catch (SQLException e) { e.printStackTrace(); } } /** * @Description 通用增删改操作 * @Param [sql, args] * @return void **/ public static int updateDate(String sql, Object... args) { Connection conn = null; PreparedStatement ps = null; try { //获取数据库连接 conn = getSqlConnection(); //预编译sql语句,获取PreparedStatement实例 ps = conn.prepareStatement(sql); //填充占位符 for (int i = 0; i < args.length; i++) { ps.setObject(i + 1, args[i]); } //执行操作 return ps.executeUpdate(); //返回操作的字段数,没有则为0 } catch (Exception e) { e.printStackTrace(); }finally { //关闭资源 closeResource(conn, ps); } return 0; } }
②测试类
package jdbc; import utils.jdbcUtils; /** * @Author: Yeman * @Date: 2022-01-12-20:51 * @Description: */ public class comUpdate { public static void main(String[] args) { String sql = "update `order` set order_name = ? where order_id < ?"; int bb = jdbcUtils.updateDate(sql, "BB", 4); System.out.println(bb); } }
5、使用PreparedStatement实现通用查询操作
这里所说的通用是针对同一数据库下的不同表或者同一张表的不同查询字段数。而要实现不同表的通用,则首先需要创建相应的不同Java类,即依据ORM思想。
/* * ORM编程思想(object relational mapping) * 一个数据表对应一个Java类 * 表中的一条记录对应Java类的一个对象,一个字段对应Java类的一个属性 */
例如,我这里的数据库中有一张customers表,因此我需要创建一个customers类。
package javabean; import java.sql.Date; /** * @Author: Yeman * @Date: 2022-01-12-21:08 * @Description: customers表对应的Java类 */ public class Customers { private int id; private String name; private String email; private Date birth; public Customers() { } public Customers(int id, String name, String email, Date birth) { this.id = id; this.name = name; this.email = email; this.birth = birth; } public int getId() { return id; } public String getName() { return name; } public String getEmail() { return email; } public Date getBirth() { return birth; } @Override public String toString() { return "Customers{" + "id=" + id + ", name='" + name + '\'' + ", email='" + email + '\'' + ", birth=" + birth + '}'; } }
①工具类(连接与关闭),即调用上面的Utils
②通用查询类
import java.lang.reflect.Field; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.util.ArrayList; import java.util.List; /** * @Author: Yeman * @Date: 2022-01-13-20:45 * @Description: */ public class Query { public static <T> List<T> Query(Class<T> clazz, String sql, Object... args) { ArrayList<T> ts = new ArrayList<>(); Connection conn = null; PreparedStatement ps = null; ResultSet res = null; try { //获取连接 conn = jdbcUtils.getSqlConnection(); //预编译sql语句,获取PreparedStatement实例 ps = conn.prepareStatement(sql); //填充占位符 for (int i = 0; i < args.length; i++) { ps.setObject(i + 1, args[i]); } //获取结果集 res = ps.executeQuery(); //获取结果集元数据 ResultSetMetaData metaData = res.getMetaData(); //获取列数 int columnCount = metaData.getColumnCount(); //处理结果集数据 while (res.next()){ T t = clazz.newInstance(); for (int i = 0; i < columnCount; i++) { Object value = res.getObject(i + 1); //获取列值 String columnLabel = metaData.getColumnLabel(i + 1); //获取列的别名 //通过反射实现 Field field = clazz.getDeclaredField(columnLabel); field.setAccessible(true); field.set(t,value); } ts.add(t); } return ts; } catch (Exception e) { e.printStackTrace(); }finally { //关闭资源 jdbcUtils.closeResource(conn,ps,res); } return null; } }
四、操作BLOB类型字段(图片视频等)
1、MySQL BLOB类型
MySQL中,BLOB是一个二进制大型对象,是一个可以存储大量数据的容器,它能容纳不同大小的数据。插入BLOB类型的数据必须使用PreparedStatement,因为BLOB类型的数据无法使用字符串拼接写的。
MySQL的四种BLOB类型(除了在存储的最大信息量上不同外,他们是等同的)
实际使用中根据需要存入的数据大小定义不同的BLOB类型。但需要注意的是,如果存储的文件过大,数据库的性能会下降。
如果在指定了相关的Blob类型以后,还报错:xxx too large,那么需要在mysql的安装目录下,打开my.ini文件最后加上 max_allowed_packet=16M 。修改my.ini文件之后,需要重新启动mysql服务才能生效。(找不到my.ini文件或者无法修改可以留言或者私信)
2、向数据表中增(改)大数据类型
package jdbc; import utils.jdbcUtils; import java.io.File; import java.io.FileInputStream; import java.sql.Connection; import java.sql.PreparedStatement; /** * @Author: Yeman * @Date: 2022-01-13-22:13 * @Description: */ public class Blob01 { public static void main(String[] args) throws Exception { //获取连接 Connection conn = jdbcUtils.getSqlConnection(); //预编译sql语句,获取PreparedStatement实例 String sql = "insert into customers(name,email,birth,photo) values(?,?,?,?)"; PreparedStatement ps = conn.prepareStatement(sql); //填充占位符 ps.setObject(1,"叶绿体"); ps.setObject(2,"yelvti@163.com"); ps.setObject(3,"2000-01-01"); //即以流的形式读取图片文件后再添加到数据库中(增和改类似) FileInputStream photo = new FileInputStream(new File("a.jpg")); ps.setObject(4,photo); //执行 ps.execute(); //关闭连接 jdbcUtils.closeResource(conn,ps); } }
3、从数据库表中读取大数据类型
package jdbc; import javabean.Customers; import utils.jdbcUtils; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.sql.*; /** * @Author: Yeman * @Date: 2022-01-13-22:34 * @Description:这里以customer表为例,该表中有一photo图片类型数据 */ public class Blob02 { public static void main(String[] args) { Connection conn = null; PreparedStatement ps = null; ResultSet res = null; InputStream is = null; FileOutputStream os = null; try { conn = jdbcUtils.getSqlConnection(); String sql = "select id,name,email,birth,photo from customers where id = ?"; ps = conn.prepareStatement(sql); ps.setObject(1,16); res = ps.executeQuery(); while (res.next()){ int id = res.getInt("id"); String name = res.getString("name"); String email = res.getString("email"); Date birth = res.getDate("birth"); Customers customers = new Customers(id, name, email, birth); System.out.println(customers); //以二进制流的形式读取下来写到本地 Blob photo = res.getBlob("photo"); is = photo.getBinaryStream(); os = new FileOutputStream("zhuyin.jpg"); byte[] buffer = new byte[1024]; int len = 0; while ((len = is.read(buffer)) != -1){ os.write(buffer,0,len); } } } catch (Exception e) { e.printStackTrace(); }finally { try { if (is != null) is.close(); } catch (IOException e) { e.printStackTrace(); } try { if (os != null) os.close(); } catch (IOException e) { e.printStackTrace(); } jdbcUtils.closeResource(conn,ps,res); } } }
五、Batch批量插入
1、批量执行SQL语句
当需要成批插入或者更新记录时,可以采用Java的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理。通常比单独提交处理具有更高的效率。
JDBC 的批量处理语句包括下面三个方法:
①addBatch(String):添加需要批量处理的SQL语句或参数;
②executeBatch():执行批量处理语句;
③clearBatch():清空缓存的数据。
2、高效的批量插入
mysql服务器默认是关闭批量处理的,我们需要通过一个参数,让mysql开启批处理的支持。即将 ?rewriteBatchedStatements=true 补充在配置文件jdbc.properties的 url 的参数位置。
举例:向数据表goods中插入20000条数据
package jdbc; import utils.jdbcUtils; import java.sql.Connection; import java.sql.PreparedStatement; /** * @Author: Yeman * @Date: 2022-01-14-12:09 * @Description: 批量操作 */ public class goods { public static void main(String[] args) { Connection conn = null; PreparedStatement ps = null; try { long begin = System.currentTimeMillis(); //获取连接 conn = jdbcUtils.getSqlConnection(); //设置为不可自动提交 conn.setAutoCommit(false); //预编译sql语句,获取PreparedStatement实例 String sql = "insert into goods(name) value(?)"; ps = conn.prepareStatement(sql); //填充占位符,使用Batch积攒要执行的sql for (int i = 1; i <= 1000000; i++) { ps.setString(1, "name" + i); //攒sql ps.addBatch(); //执行(这里设置为每5000条执行一次) if (i % 5000 == 0){ ps.executeBatch(); //清空 ps.clearBatch(); } } //使其提交 conn.commit(); long end = System.currentTimeMillis(); System.out.println("花费的时间为:" + (end - begin)); } catch (Exception e) { e.printStackTrace(); }finally { //关闭连接 jdbcUtils.closeResource(conn,ps); } } }