JDBC连接的基本操作
1、在idea的pom.xml文件中加入Maven依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
2、JDBC连接步骤
- 1)加载驱动
- 2)创建一个连接
- 3)创建Statement或PreparedStatement的对象
- 4)通过创建对象调用方法执行SQL语句
- 调用执行DQL(select)操作时,使用executeQuery方法,就会返回单个ResultSet对象
- 调用执行DML(insert,update,delete)操作时,使用executeUpdate方法,返回的是受影响的记录的条数
- 调用其他操作使用execute方法,返回的是布尔类型,表示执行的SQL语句有无返回值,true表示执行的SQL语句有返回值即有ResultSet对象,false表示执行的SQL语句没有返回值
- 5)如果是使用executeQuery方法,就会返回单个ResultSet对象,获得结果集,然后通过遍历ResultSet获取返回的记录
- 6)关闭连接
Statement示例代码
package MySQlJDBC;
import java.sql.*;
public class MySQLJDBCDemo {
public static void main(String[] args) throws Exception {
//1、加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2、创建连接
Connection conn = DriverManager.getConnection("jdbc:mysql://master:3306/db1?useSSL=false", "root", "123456");
//3、创建Statement
Statement st = conn.createStatement();
//4、通过Statement执行SQL
ResultSet rs = st.executeQuery("select *from student limit 10");
//5、遍历ResultSet获取返回的记录
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
String gender = rs.getString("gender");
String clazz = rs.getString("clazz");
System.out.println(id + "," + name + "," + age + "," + gender + "," + clazz);
}
//6、关闭连接
st.close();
conn.close();
}
}
3、JDBC执行SQL的两种方式(Statement与PreparedStatement)
- 使用Statement对象
使用范围:当执行相似SQL(结构相同,具体值不同)语句的次数比较少
优点:语法简单
缺点:采用硬编码效率低,安全性较差。直接使用变量拼接SQL会造成SQL注入问题
原理:硬编码,每次执行时相似SQL都会进行编译 - 预编译PreparedStatement
使用范围:当执行相似sql语句的次数比较多(例如用户登陆,对表频繁操作…)语句一样,只是具体的值不一样,被称为动态SQL
优点:语句只编译一次,减少编译次数。提高了安全性(阻止了SQL注入)
缺点: 执行非相似SQL语句时,速度较慢。
原理:相似SQL只编译一次,减少编译次数
PreparedStatement示例代码
package MySQlJDBC;
import java.sql.*;
public class MySQLJDBCDemo {
public static void main(String[] args) throws Exception {
//1、加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2、创建连接
Connection conn = DriverManager.getConnection("jdbc:mysql://master:3306/db1?useSSL=false", "root", "123456");
String clz="文科二班";
int age1=23;
//3、使用PreporeStatement避免SQL注入问题
PreparedStatement ps = conn.prepareStatement("select *from student where clazz=? and age>?");
//4、通过PreporeStatement执行SQL
//先设置参数,'?'从1开始编号
ps.setString(1,clz);
ps.setInt(2,age1);
//再执行sql
ResultSet rs = ps.executeQuery();
//5、遍历ResultSet获取返回的记录
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
String gender = rs.getString("gender");
String clazz = rs.getString("clazz");
System.out.println(id + "," + name + "," + age + "," + gender + "," + clazz);
}
//6、关闭连接
ps.close();
conn.close();
}
}
4、JDBC读取一个文件插入数据库中
-
1、读取数据,基本插入到数据库
步骤:先建表,然后通过IO流读取数据,在通过JDBC连接数据库,将数据插入到表中
a、在mysql中手动建表:
CREATE TABLE `student_like` ( `id` int(10) NOT NULL COMMENT '学生id', `name` varchar(10) DEFAULT NULL COMMENT '学生姓名', `age` int(2) DEFAULT NULL COMMENT '学生年龄', `gender` varchar(1) DEFAULT NULL COMMENT '学生性别', `clazz` varchar(5) DEFAULT NULL COMMENT '学生班级' ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='学生信息表'
或通过JDBC建表
package MySQlJDBC; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; public class MySQLJDBCDemo1 { public static void main(String[] args) throws Exception{ /* CREATE TABLE `student_like` ( `id` int(10) NOT NULL COMMENT '学生id', `name` varchar(10) DEFAULT NULL COMMENT '学生姓名', `age` int(2) DEFAULT NULL COMMENT '学生年龄', `gender` varchar(1) DEFAULT NULL COMMENT '学生性别', `clazz` varchar(5) DEFAULT NULL COMMENT '学生班级' ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='学生信息表' */ //1、加载驱动 Class.forName("com.mysql.jdbc.Driver"); //2、创建连接 Connection conn = DriverManager.getConnection("jdbc:mysql://master:3306/db3?useSSL=false","root","123456"); //3、创建PrepareStatement PreparedStatement ps = conn.prepareStatement("CREATE TABLE `student_like` (\n" + " `id` int(10) NOT NULL COMMENT '学生id',\n" + " `name` varchar(10) DEFAULT NULL COMMENT '学生姓名',\n" + " `age` int(2) DEFAULT NULL COMMENT '学生年龄',\n" + " `gender` varchar(1) DEFAULT NULL COMMENT '学生性别',\n" + " `clazz` varchar(5) DEFAULT NULL COMMENT '学生班级'\n" + " ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='学生信息表'"); //4、执行SQL ps.executeUpdate(); //5、关闭连接 ps.close(); conn.close(); } }
b、通过IO流读取文件,然后通过在通过JDBC连接数据库,将数据插入到表中
package MySQlJDBC; import java.io.BufferedReader; import java.io.FileReader; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; public class ReadAndWriteToMySQL { public static void main(String[] args) throws Exception { //读取student.txt文件 BufferedReader br = new BufferedReader(new FileReader("Java/data/students.txt"));//这里的路径是相对路径 //1、加载驱动 Class.forName("com.mysql.jdbc.Driver"); //2、创建连接 Connection conn = DriverManager.getConnection("jdbc:mysql://master:3306/db1?useSSL=false","root","123456"); //3、创建PrepareStatement PreparedStatement ps = conn.prepareStatement("insert into student_like values(?,?,?,?,?)"); String line; while ((line= br.readLine())!=null){ //line就是一行数据 //例如:1500100001,施笑槐,22,女,文科六班 String[] splits = line.split(","); String id = splits[0]; String name = splits[1]; String age = splits[2]; String gender = splits[3]; String clazz = splits[4]; //传递参数 ps.setInt(1,Integer.parseInt(id)); ps.setString(2,name); ps.setInt(3,Integer.parseInt(age)); ps.setString(4,gender); ps.setString(5,clazz); // //执行SQL // ps.executeUpdate(); //使用批量插入 ps.addBatch(); //先将每一条SQL语句先放入Batch中,最后批量处理的SQL语句 } //执行sql ps.executeBatch(); //关闭连接 ps.close(); conn.close(); } }
-
2、使用多线程、线程池、连接池批量插入数据到数据库
步骤:1)创建一个InsertTask线程类,实现Runnable,重写run方法,并在run方法中实现数据插入功能。
2)使用连接池创建多个连接
3)使用线程池实现多线程
InsertTask线程类示例代码
package MySQlJDBC; import java.sql.Connection; import java.sql.PreparedStatement; import java.util.ArrayList; public class InsertTask implements Runnable { Connection conn; ArrayList<String[]> linesArr; int i;//指示运行到哪个任务 public InsertTask(Connection conn, ArrayList<String[]> linesArr, int i) { this.conn = conn; this.linesArr = linesArr; this.i = i; } @Override public void run() { System.out.println("正在执行第" + i + "个任务"); try { //3、创建PrepareStatement PreparedStatement ps = conn.prepareStatement("insert into student_like values(?,?,?,?,?)"); for (String[] splits : linesArr) { //例如:1500100001,施笑槐,22,女,文科六班 String id = splits[0]; String name = splits[1]; String age = splits[2]; String gender = splits[3]; String clazz = splits[4]; //传递参数 ps.setInt(1, Integer.parseInt(id)); ps.setString(2, name); ps.setInt(3, Integer.parseInt(age)); ps.setString(4, gender); ps.setString(5, clazz); // //执行SQL // ps.executeUpdate(); //使用批量插入 ps.addBatch(); //将语句先放入Batch中 } //执行sql ps.executeBatch(); ps.close(); conn.close(); // 关闭连接池传入的连接并不会真正关闭,而是放回连接池 System.out.println("第" + i + "个任务执行完成"); }catch (Exception e){ System.out.println("发生了异常"+e); } } }
ReadAndWriteToMySQL3示例代码
package MySQlJDBC; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import java.io.BufferedReader; import java.io.FileReader; import java.sql.Connection; import java.util.ArrayList; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ReadAndWriteToMySQL3 { public static void main(String[] args) throws Exception { //读取student.txt文件 BufferedReader br = new BufferedReader(new FileReader("Java/data/students.txt")); String line; ArrayList<String[]> linesArr = new ArrayList<>(); while ((line = br.readLine()) != null) { // 避免重复读取数据,在第一次读的时候将每一行数据切分后放入ArrayList linesArr.add(line.split(",")); } br.close(); //将创建连接的代码放在循环外,避免创建多次连接 //使用线程池实现多线程 ExecutorService es = Executors.newFixedThreadPool(8); //使用连接池 // 配置 HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:mysql://master:3306/db1"); config.setUsername("root"); config.setPassword("123456"); config.addDataSourceProperty("connectionTimeout", "1000"); // 连接超时:1秒 config.addDataSourceProperty("idleTimeout", "60000"); // 空闲超时:60秒 config.addDataSourceProperty("maximumPoolSize", "10"); // 最大连接数:16 config.addDataSourceProperty("useSSL", "false"); // 关闭使用SSL连接 //创建连接池 HikariDataSource ds = new HikariDataSource(config); for (int i = 0; i < 100; i++) { //每次从连接池中获取一个连接 Connection conn = ds.getConnection(); System.out.println("第" + i + "次插入"); InsertTask task = new InsertTask(conn, linesArr, i); es.submit(task); } //销毁线程池 es.shutdown(); //销毁连接池 ds.close(); } }
5、JDBC连接池
1、连接池是什么?
连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用。 连接池用于提高在数据库上执行命令的性能。连接池本质上就是数据库连接的缓存。使用数据库连接时,如果池中有一个可用,它将使用该连接而不是重新建立另一个新的连接 ,用完后不是关闭它,而是将其放回池中。
2、为什么要用连接池?
- 资源重用:由于数据库连接得以重用,避免了频繁创建,释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增加了系统运行环境的平稳性。
- 更快的系统反应速度:数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于连接池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接避免了数据库连接初始化和释放过程的时间开销,从而减少了系统的响应时间
- 新的资源分配手段:对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接池的配置实现某一应用最大可用数据库连接数的限制避免某一应用独占所有的数据库资源.
- 统一的连接管理:避免数据库连接泄露在较为完善的数据库连接池实现中,可根据预先的占用超时设定,强制回收被占用连接,从而避免了常规数据库连接操作中可能出现的资源泄露。
3、HikariCP连接池的使用
Maven依赖
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>4.0.3</version>
</dependency>
连接池初始化
// 配置
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://master:3306/db1");
config.setUsername("root");
config.setPassword("123456");
config.addDataSourceProperty("connectionTimeout", "1000"); // 连接超时:1秒
config.addDataSourceProperty("idleTimeout", "60000"); // 空闲超时:60秒
config.addDataSourceProperty("maximumPoolSize", "16"); // 最大连接数:16
config.addDataSourceProperty("useSSL", "false"); // 关闭使用SSL连接
// 创建连接池实例
DataSource ds = new HikariDataSource(config);
//每次从连接池中获取一个连接
Connection conn = ds.getConnection();
MySQL查看连接池数量
show global status like "%Thread%";
#运行结果
+------------------------------------------+-------+
| Variable_name | Value |
+------------------------------------------+-------+
| Delayed_insert_threads | 0 |
| Performance_schema_thread_classes_lost | 0 |
| Performance_schema_thread_instances_lost | 0 |
| Slow_launch_threads | 0 |
| Threads_cached | 9 |
| Threads_connected | 4 |
| Threads_created | 14 |
| Threads_running | 1 |
+------------------------------------------+-------+
Treads_cached是缓冲池中的线程个数
Treads_connected这个是连接中的线程数
Threads_created表示创建过的线程数
使用连接池
try (Connection conn = ds.getConnection()) { // 通过ds获取连接
...
} // 在此“关闭”连接