关于JDBC的学习(1)
JDBC的简介
JDBC (Java DataBase Connectivity)是由Sun公司为简化java程序访问数据库而制定的一套面向对象的应用程序的接口。它规定了一套访问数据库的API,该API对应的类和接口都位于JDK的java.sql包中。
ps:目前我所知道的数据库有:MySQL, Oracle, SQLServer,DB2等。
JDBC和驱动的关系
我的理解:JDBC是将操作数据库的应用方法建立了一个统一的规范,即接口,但是不同的数据库,增删改查以及其他操作的代码都是不太一样的,所以各大数据库厂商,根据Java的JDBC接口编写一套专门的实现类----驱动程序。
类似于我们电脑上的USB接口,当我们插入键盘,鼠标以及U盘等,我们的电脑是可以识别出来并提供相应的操作,当然识别这些设备的驱动都是在我们电脑中的。
所以我们是在面向JDBC(接口)编程对数据库操作的。如下图:
所以当我们使用不同的数据时,就要选用不同的驱动软件,并且还要根据数据库的版本来选择相应的驱动。
JDBC的体系接口是:1、面向应用程序的API 2、面向数据的API 。
JDBC的编程步骤
直接上图:
对上述解释一下为什么使用PrepareStatement 类,而不使用Statement类以下两处优点:
(1)可以解决Statement的拼串问题。
(2)可以解决SQL注入的问题。
SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。
下面我们根据步骤开始操作:
1. 导入Jar包
首先下载所需要驱动包,这里提供一个连接是专门下载MySQL的驱动,在工程下边创建一个lib的文件,将驱动文件赋值粘贴进去
https://downloads.mysql.com/archives/c-j/
然后右击Jar包,如下图:
那些报错的地方是因为我把这个库给删除又重新导入的,另外Junit4 是一个测试包,在方法上添加一个@Test注解,就可以直接运行啦,不用再写main方法了。
2、加载并注册驱动和创建Connection连接
这里提供了3种方法,分别是迭代的过程,其实可以迭代五次,这里是关键的三次:
@Test
public void connection01() {
Connection conn = null;
try {
Driver driver = new com.mysql.cj.jdbc.Driver();
String url = "jdbc:mysql://192.168.1.10:3306/javaweb";
Properties info = new Properties();
info.setProperty("user", "javaweb");
info.setProperty("password", "123456");
conn = driver.connect(url, info);
} catch (SQLException e) {
e.printStackTrace();
}
System.out.println(conn);
}
定义的url:
这里我把我的MySQL装到了虚拟机中,所以我的主机号是我的ip,如果将mysql装到本机的是localhost。
Properties类:它是读取配置文件的类。在Java语言中,配置文件的后缀为.properties. 在这里我用他设置了用户名和密码。切记:多查一些API文档。
@Test
public void connection02() throws Exception {
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://192.168.1.10:3306/javaweb";
Properties info = new Properties();
info.setProperty("user", "javaweb");
info.setProperty("password", "123456");
Connection conn = DriverManager.getConnection(url, info);
System.out.println(conn);
}
我们通过观察Driver类中的静态代码可发现,当我们加载Driver类的时候,就会执行这一个静态代码块,他就会自动的注册Driver类,所以我们可以只用Class.forName(“com.mysql.cj.jdbc.Driver”)
附一下Driver类中的static:
最后一种方法就是对以上两种方法的优化迭代的,优化了上面的账号密码以及平台的可移植性的问题。
因为Java代码中出现了第三方库,和账号密码都写在java文件中,修改和可移植性差,就有了一下最后一种方法,就是利用Java反射机制,以及读取配置文件。
public void connection03() throws Exception {
InputStream in = Connetion_JDBC.class.getClassLoader()
.getResourceAsStream("jdbc.properties");
Properties pos = new Properties();
pos.load(in);
String user = pos.getProperty("user");
String password = pos.getProperty("password");
String url = pos.getProperty("url");
String driverClass = pos.getProperty("classname");
Class.forName(driverClass);
Connection conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);
}
3、创建PreparedStatement对象,执行SQL语句,处理结果集
在SQL语句中我们可以刻意 的分为两类:一类是增删改,另一类是查询。
-
增删改
1、增删改的操作是不返回结果集的,只返回一个成功或不成功的状态值或者改动的行数,所以我们不用结果集进行接收数据库返回的结果,这样反而更加简单。
2、当我们建立连接之后,会返回一个Connection 接口的对象(conn),我们利用Connection接口中的prepareStatement(String sql) 方法创建一个 PreparedStatement对象,用于将参数化的SQL语句发送到数据库。
3、因为在Java中不能直接运行SQL语句,所以我们将SQL语句写入到String 类型的字符串中,让preparedStatement去预编译SQL语句。
#另外preparedStatement 可以用占位符去替换要传入的变量,这样可以优化Statement的拼串问题,减少我们写SQL语句的难度,以及出错率(马虎是很可怕的)
@Test
public void update() {
Connection conn = null;
PreparedStatement ps = null;
try {
Driver driver = new Driver();
Properties info = new Properties();
info.setProperty("user", "javaweb");
info.setProperty("password", "971005");
String url = "jdbc:mysql://192.168.1.10:3306/javaweb";
conn = driver.connect(url, info);
System.out.println(conn);
String sql = "INSERT INTO dept(id, job ) VALUES (?, ?)";
ps = conn.prepareStatement(sql);
ps.setInt(1, 3);
ps.setString(2, "数据库工程师");
ps.execute();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
我们上面讲到的插入数据的代码是可以通用的,更换一下sql语句,就可以变成更新和删除的语句,到最后我把他们封装成了一个工具类,这样就可以更加方便的调用了。
- 查询
查询操作比较特殊的地方是,我们要将查询到的结果返回给我们的客户端。
为了提高代码的简洁性和复用性,我将连接和关闭的操作都封装到了工具类中,这样就可以直接调用静态方法,建立连接。
@Test
public void query() {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtils.connectionMysql();
String sql = "select id, job from dept where id = ?";
ps = conn.prepareStatement(sql);
ps.setObject(1, 3);
rs = ps.executeQuery();
ResultSetMetaData rsmd = (ResultSetMetaData) rs.getMetaData();
int columnCount = rsmd.getColumnCount();
if(rs.next()) {
int i = rs.getInt(1);
String job = rs.getString(2);
Dept result = new Dept(i,job, null, 0);
System.out.println(result);
}
} catch (Exception e) {
e.printStackTrace();
}
JDBCUtils.closeRes(conn, ps, rs);
}
以上是更新和查询数据的两个功能实现代码,但是这仅仅是只是实现了特定的更新和查询操作,因为这两个功能实现的只是针对于这一个表的操作,我们要实现的是针对每个表都能进行操纵,以增加代码的复用性,故我们转变设计的思路,将SQL语句以及必要的信息通过参数传递给方法。下面是实现的代码:
public static int update(String sql, Object ...args) {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JDBCUtils.connectionMysql();
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeRes(conn, ps, null);
}
return 0;
}
public static <T> T query(Class<T> clazz, String sql, Object ...args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
Dept dept = null;
try {
conn = JDBCUtils.connectionMysql();
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
rs = ps.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
if (rs.next()) {
T t = clazz.newInstance();
for (int j = 0; j < rsmd.getColumnCount(); j++) {
String name = rsmd.getColumnLabel(j + 1);
Object objectValue = rs.getObject(j + 1);
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
field.set(t, objectValue);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeRes(conn, ps, rs);
}
return null;
}
以上就是更新和查询的两个静态方法,当我们需要查询和更新的时候就可以调用这两个方法了。现在考虑一个新问题,如果要查询的结果是多条记录怎么办,我上边的查询只能查找一条记录,不能查找多条记录,所以我们要将查找的多条记录放到容器里面:
以下是实现方法:
public static <T> List<T> query(Class<T> clazz, String sql, Object...args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
ArrayList<T> list = new ArrayList<T>();
try {
conn = JDBCUtils.connectionMysql();
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i ++) {
ps.setObject(i + 1, args[i]);
}
rs = ps.executeQuery();
java.sql.ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
list = new ArrayList<T>();
while ( rs.next()) {
T t = clazz.newInstance();
for (int i = 0; i < columnCount; i ++) {
String columnLabel = rsmd.getColumnLabel(i + 1);
Object objectValues = rs.getObject(i + 1);
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t, objectValues);
}
list.add(t);
}
return list;
} catch (Exception e) {
e.printStackTrace();
} finally {
closeRes(conn, ps, rs);
}
return null;
}
总结
经过学习,JDBC的第一部分已经学习完了,这只是Java在操作数据库的一些基本实现,在真实的项目中,JDBC已经被封装到框架里面去了,但是我们要了解这些底层的实现原理。
主要体现了两种思想和两种技术:
1、面向接口的编程思想,在我们的Connection 以及其他的接口中是利用的多态原理,使接口接收了一些实现这些接口的对象,接口成为这些对象的载体,我们对接口的操作,实际上都是在这些对象上进行操作。
2、ORM思想(对象关系映射),观察我们的查询操作,我们将查询到的结果存储起来,那我们选用的存储的载体十分重要,我们查询的每一个表返回的列数和返回值都是不一样的,这样我们统一处理就会很麻烦,所以采用ORM思想,将一个表映射为一个类,将一行数据映射为一个对象,将一个字段映射为类的一个属性,故当我们查询每一个表时,附带每个表的javabean类,这样接受数据以及存储和查看,都变得十分有条理。
3、JDBC的结果集的元数据,因为结果集并不提供查询列数的方法,所以我们只能使用ResultSetMetaData这个类来获得一些我们必要的信息,网上一些人说这个类的性能不是很好,而且我学习的比较差,所以日后学到在进行总结。
4(*)、反射技术,当我们无法确定属性的名称,以及其他需要操作属性的时候,这个时候我们就要考虑一下利用反射,我理解的反射是:在java中有一种数据类型Class是专门存储类和接口的数据类型,类中有属性、构造方法、方法等,既然我们类实例出来对象,那么我们用来存储类的对象,就可以实例化该类存储的类的对象,那么我们也可以用Class来访问和处理这个实例化的对象的功能。(反射学的不扎实,嘻嘻)
主要学习到的是思考问题的方法,就是我们从先去实现一个功能为出发点,然后将其缺点用更好的方法取代,去迭代的一种方式,直到将其实现为一个工具类的方法(没那么绝对)。
第一次写博客记录一下,加油,另附上一些java的源代码。
代码链接
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)