说明
因为是个人复习java的总结,所以结构稍显杂乱,有些语句过于口语化.
这部分内容准确地说不算是java的内容,而是为了JDBC进行的数据库复习
JDBC
其实就是一个用于连接数据库的jar包.本质上说是一个连接数据库的接口,其中有不同数据库的实现类.能够实现任意数据库的使用.
连接数据库的基本语句
import java.sql.*;
public class MysqlTest {
public static void main(String[] args) {
try {
//导入jar包,反射中有提到
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
//创建数据库的连接
Connection connection = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/warehouse?serverTimezone=UTC"
,"root"
," ");
//sql语句
String sql = "SELECT * FROM product";
//获取数据库对象的statement
Statement statement = connection.createStatement();
//执行sql语句
ResultSet result = statement.executeQuery(sql);
//对结果集遍历输出
while (result.next()){
String number = result.getString(1);
String name = result.getString(2);
Date madeTime = result.getDate(3);
double price = result.getDouble(4);
System.out.println(number+name+madeTime+price);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
分析其中用到的类
-
DriverManager
其实就是驱动管理类,也就是我们到底使用的是什么数据库的驱动.我们通过第一句的Class.forName()
加载了数据库具体的Driver类,但是其实加载Driver类的时候其中有静态代码块调用了DriverManager的registerDriver()
方法,让驱动管理类知道调用的是什么驱动类.也就是说DriverManager其实是一个公用的类,而不同的是Driver驱动类.
其实mysql5之后的jar包可以省略加载Driver类,会自动加载,但是建议书写
-
Connection
数据库连接类,其实就是通过驱动管理类使用getConnection()方法获取指定的数据库连接,其中参数:
url: 指定连接的路径,基本语法为:jdbc:mysql://ip地址:端口号/数据库名称[?编码格式&&时区设置等 可选]
user: 用户名
password: 密码
也可以将这些信息组合成一句作为参数,但是不太好阅读,另外url如果是本机的默认地址还可以直接跳过不书写.
-
Statement
执行sql的类,其中定义了执行sql语句的方法像:
Boolean execute(String sql )
执行任意sql语句
int executeUpdate(String sql )
执行DML和DDL语句(DML,DDL在上一篇数据库复习中有解释)
ResultSet executeQuery(String sql)
执行DQL语句
另外还有事务管理的相关语句
setAutoCommit(Boolean autoComic)
开启事务,因为默认是自动提交,程序中需要设置其不提交从而达到开启事务的效果
commit()
提交事务
rollBack()
回滚事务
-
ResultSet
结果集类,其实就是用来封装查询到的数据库结果.
其中获取内容实际上是通过移动光标+获取数据的方式实现的.
也就是对于结果集,一开始有一个光标指向的是字段名,如果想要获取数据,就需要使用next()将光标向下移动,然后再使用getXXX()的方式获取指定指定字段的数据.具体方法就需要根据字段的数据类型来选择,其中传入的参数可以数字代表第几个字段,也可以传入字段名来表示获取的数据属于什么字段.
-
PrepareStatement
其实区别于Statement,这个就是预编译的执行SQL语句的类,也就是会现在数据库底层编译好需要执行的语句,只等待传入被占位符占据的数据.这种方式主要针对一些操作频繁的命令,因为底层编译好了,频繁使用时效率比较高
具体使用大概如下所示:
String sql = “SELECT * FROM product WHERE number = ?;”
statement = connection.prepareStatement(sql);
statement.setString(1,”1”)
statement.executeQuery();
PrepareStatement和Statement的比较
其实在编程中一般都是使用PrepareStatement,因为对于数据库的操作基本都是增删改查,操作都比较频繁,那么效率肯定比较高.像之后会写的jsp中,都是使用这种方式.
另外Statement其实有一个很严重的问题,就是在解析语句的时候肯定会添加我们要求输入的内容,比如说登陆的时候.问题就处在这里,在获取输入之后就肯定会和sql语句拼接成一个字符串,那就意味着我们需要对传入的数据进行处理之后才能拼接字符串.
因为万一输入的数据是代码,而我们只是简单地将其和字符串进行凭借,就会导致sql查询的内容出错,导致安全问题.
虽然说对输入的内容去个符号也是可以的,但是比较麻烦.
JDBC简化案例
从使用中可以看出,连接数据库的操作实际上挺繁琐的,而且连接数据库这个操作肯定需要经常进行,所以就可以思考将这个连接的操作给封装到一个类中,而我们只要调用这个类就可以反复进行数据库连接.那再思考,发现可能我们需要连接其他数据库进行操作,那又得改源代码,太麻烦.然后又想到之前使用properties来实现读取配置文件.这下豁然开朗,一个封装之后可以复用的代码.
JDBC工具类
import java.io.FileReader;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
public class JDBCUtils {
private static String url;
private static String user;
private static String password;
static {
try {
//获取properties对象
Properties properties = new Properties();
//通过ClassLoader找到在src目录下的JDBC.properties的绝对路径,不需要自己去写路径.
//Classloader会从当前程序的运行根目录开始寻找,然后返回一个绝对地址,所以只要放在src中就能找到
ClassLoader classLoader = JDBCUtils.class.getClassLoader();
String path = classLoader.getResource("JDBC.properties").getPath();
FileReader fileReader = new FileReader(path);
properties.load(fileReader);
//获取配置文件中的信息
url = properties.getProperty("url");
user = properties.getProperty("user");
password = properties.getProperty("password");
String driver = properties.getProperty("driver");
//加载驱动管理
Class.forName(driver);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
//获取连接
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url,user,password);
}
}
测试类
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class MysqlTest {
public static void main(String[] args) {
try(
Connection connection =JDBCUtils.getConnection();
Statement statement = connection.createStatement();
){
List<Emp> list = findAll(statement);
System.out.println(list);
}catch (SQLException sqlException){
sqlException.printStackTrace();
}
}
//将数据查询到list中
public static List<Emp> findAll(Statement statement) throws SQLException {
//查询的sql语句
String sql = "SELECT * FROM product";
//查询并获得结果集
ResultSet result = statement.executeQuery(sql);
List<Emp> list = new ArrayList<>();
Emp emp = null;
//读取结果集
while (result.next()){
emp = new Emp();
String number = result.getString("number");
String name = result.getString(2);
Date madeTime = result.getDate(3);
double price = result.getDouble(4);
//设置自定义类型的属性
emp.setMadeTime(madeTime);
emp.setName(name);
emp.setPrice(price);
emp.setNumber(number);
//将每条数据加到集合中
list.add(emp);
}
return list;
}
}
自定义类型类
import java.util.Date;
/*
用于存放指定类型数据的类,在使用的时候具体改变这个类
*/
public class Emp {
private String number;
private String name;
private double price;
private Date madeTime;
/*
set,get,toString重写
*/
public void setNumber(String number) {
this.number = number;
}
public void setName(String name) {
this.name = name;
}
public void setPrice(double price) {
this.price = price;
}
public void setMadeTime(Date madeTime) {
this.madeTime = madeTime;
}
public String getNumber() {
return number;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
public Date getMadeTime() {
return madeTime;
}
public String toString() {
return "Emp{" +
"number='" + number + '\'' +
", name='" + name + '\'' +
", price=" + price +
", madeTime=" + madeTime +
'}';
}
}
细节上还存在一些小问题,但是大概就是这种形式,将前面的内容结合起来,加深了解.
这里其实有一个问题没有解决,properties中的FileReader没有释放,需要手动释放吗,还是说等垃圾回收.
事务控制
其实在代码中采用事务控制主要是为了防止异常的出现,如果没有采用事务,那么出现异常就会导致原本不应该生效的操作生效.
所以事务的rollback()
一般都是在catch到异常的时候进行的,然后在finally中commit().
数据库连接池
其实就是一个存放数据库连接的容器,就跟线程池差不多,都是为了能够高效地需要连接地对象提供连接,而不用每次都重新创建连接,线程更高.
数据库连接池使用的其实是api中的DtaSource接口,但是这个接口java没有去实现,而是留给了使用的人实现.其实有很多的厂商提供了开源的数据库连接池像C3P0(比较老),Druid(阿里实现,性能有保证)
其实使用数据库连接池和直接创建连接调用方法上区别不是很大,都是通过getConnection()
方法获取连接,然后使用close()
归还连接,只是底层的源码对于连接的操作不同.
Druid
安装去官网看一下就知道了,很简单.
使用Druid的时候别忘了mysql的jar包
使用语句如下,只写到获取连接,后面的操作没什么区别
首先需要配置一个properties文件,因为数据库连接池中有很多属性可以配置,具体可以去查这里只是简单写了几个
properties文件
driverClassName = com.mysql.cj.jdbc.Driver
url = jdbc:mysql://localhost:3306/warehouse?serverTimezone=UTC
username = root
password = 94230nowhy
#初始化连接数量
initialSize = 5
#最大连接数
maxActive = 10
#最大等待时间
maxWait = 300
测试类
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;
public class Test {
public static void main(String[] args) throws Exception {
Properties properties = new Properties();
//加载配置文件
InputStream inputStream = Test.class.getResourceAsStream("druid.properties");
properties.load(inputStream);
//通过DruidDataSourceFactory获取连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
//从连接池中获取一个连接
Connection connection = dataSource.getConnection();
//归还连接
System.out.println(connection);
//这个是工具类的测试
Connection connection1 = JDBCUtils.getConnection();
System.out.println(connection1);
}
}
工具类,也就是上面使用过的封装的连接
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
public class JDBCUtils {
private static DataSource dataSource;
static {
Properties properties = new Properties();
try {
properties.load(JDBCUtils.class.getResourceAsStream("druid.properties"));
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
public static DataSource getDataSource() {
return dataSource;
}
}
细节上有很多问题,但是作为案例使用以下数据库连接池.
Spring JDBC
是Spring框架中对于JDBC的封装,Spring会在之后学习中记录.
Spring给封装之后的JDBC提供了很多的方法,能够方便的使用.简单地使用语句如下:
JdbcTemplate template = new JdbcTemplate(datasource);
String sql = “update emp set salary = 10000 where id = 1001”;
//执行DML语句
template.update(sql);
//将查询到的结果封装成一个Map,采用预编译的形式只需要加个参数,需要注意结果只能一个
String sql1 = “select * from emp where id = ?”;
template.queryForMap(sql,1);
//实际上都是和map一起使用,也就是List<Map<String,Object>>类型的list,Map存的是横向一条数据,list是纵向很多数据的集合.
String sql2 = “select * from emp”;
template.queryForList(sql2,1);
//结果封装为JavaBean对象
template.query(sql1,1)
//一般用于聚合函数的查询
String sql3 = “select count(id) from emp”;
template.queryForObject(sql3)
这样就执行成功了,并且不需要释放资源,会自动释放
然后datasource的获取结合上面的数据库连接池的工具类,那么就可以简化操作,甚至可以把连接写在静态代码块中.
如有错误欢迎读者批评指正!!