JDBC学习笔记——增删改查
1、数据库准备
要用JDBC操作数据库,第一步当然是建立数据表:
1
2
3
4
5
6
|
CREATE TABLE `user` ( `id` int ( 11 ) NOT NULL AUTO_INCREMENT PRIMARY KEY, `name` varchar( 45 ) DEFAULT NULL, `birthday` date DEFAULT NULL, `money` double DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; |
2、JDBC连接数据库的基本步骤
JDBC连接数据库包含以下几个基本步骤:1、注册驱动 ;2、建立连接(Connection);3、创建SQL语句(Statement);4、执行语句;5、处理执行结果(ResultSet);6、释放资源。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public static void test() throws SQLException{ // 1.注册驱动 Class.forName( "com.mysql.jdbc.Driver" ); // 2.建立连接 url格式 - JDBC:子协议:子名称//主机名:端口/数据库名?属性名=属性值&… // 3.创建语句 Statement st = conn.createStatement(); // 4.执行语句 ResultSet rs = st.executeQuery( "select * from user" ); // 5.处理结果 while (rs.next()) { System.out.println(rs.getObject( 1 ) + "\t" + rs.getObject( 2 ) + "\t" + rs.getObject( 3 ) + "\t" + rs.getObject( 4 )); } // 6.释放资源 rs.close(); st.close(); conn.close(); } |
3、简单的增删改查
第二节的代码有一个问题,如果我们在执行代码时抛出异常,那么Connection就无法关闭了,所以我们应该把关闭资源操作放入finally中,这样就无论如何都会关闭这些数据库连接资源。同时我们还会扩展程序功能,上面的例子只是展示了一个查询操作,接下来将会展示最常用的增、删、改、查四个操作。首先介绍一个JdbcUtils类,该类会封装数据库连接步骤中的第一步、第二步及第六步操作,分别是注册驱动,建立连接及释放资源操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
public final class JdbcUtils { static { try { Class.forName( "com.mysql.jdbc.Driver" ); } catch (ClassNotFoundException e) { throw new ExceptionInInitializerError(e); } } private JdbcUtils() { } public static Connection getConnection() throws SQLException { } public static void free(ResultSet rs, Statement st, Connection conn) { try { if (rs != null ) rs.close(); } catch (SQLException e) { e.printStackTrace(); } finally { try { if (st != null ) st.close(); } catch (SQLException e) { e.printStackTrace(); } finally { if (conn != null ) try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } } |
可以看到,这个类的构造函数是一个私有构造函数,所以我们将无法创建这个类的实例。在静态初始化域,我们进行了注册驱动操作,静态初始化域只会在类加载的时候执行一次,这样可以保证只要加载了这个类,我们会且仅会注册一次驱动。然后getConnection()方法封装了建立连接操作,free(rs, st, conn)方法封装了释放资源操作。接下来可以看看如何使用JdbcUtils类进行增、删、改、查操作:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
//增加操作 void create() throws SQLException { Connection conn = null ; Statement st = null ; ResultSet rs = null ; try { conn = JdbcUtils.getConnection(); st = conn.createStatement(); int i = st .executeUpdate( "insert into user(name,birthday, money) values ('name1', '1987-01-01', 400) " ); System.out.println( "i=" + i); } finally { JdbcUtils.free(rs, st, conn); } } //删除操作 void delete() throws SQLException { Connection conn = null ; Statement st = null ; ResultSet rs = null ; try { conn = JdbcUtils.getConnection(); st = conn.createStatement(); int i = st.executeUpdate( "delete from user where id>4" ); System.out.println( "i=" + i); } finally { JdbcUtils.free(rs, st, conn); } } //修改操作 void update() throws SQLException { Connection conn = null ; Statement st = null ; ResultSet rs = null ; try { conn = JdbcUtils.getConnection(); st = conn.createStatement(); int i = st.executeUpdate( "update user set money=money+10 " ); System.out.println( "i=" + i); } finally { JdbcUtils.free(rs, st, conn); } } //查询操作 void read() throws SQLException { Connection conn = null ; Statement st = null ; ResultSet rs = null ; try { conn = JdbcUtils.getConnection(); st = conn.createStatement(); rs = st.executeQuery( "select id, name, money, birthday from user" ); while (rs.next()) { System.out.println(rs.getObject( "id" ) + "\t" + rs.getObject( "name" ) + "\t" + rs.getObject( "birthday" ) + "\t" + rs.getObject( "money" )); } } finally { JdbcUtils.free(rs, st, conn); } } |
4、面向对象封装增删改查
第三节的例子只是为了展示如何使用JDBC进行增删改查操作,在项目中真正使用时,我们是不会像上面的例子这样简单使用的,Java是面向对象的,所以我们一般会使用面向对象的思想对操作进行封装。首先,其实对于数据表每一条数据,我们都可以认为它是一个对象实例,例如此例中我们定义的数据表User有id,name,birthday和money四个属性,对应的我们可以创建User类如下:
1
2
3
4
5
6
7
8
|
public class User { private int id; private String name; private Date birthday; private float money; //getters and setters } |
按照"面向接口编程而非面向实现编程"的原则,我们可以定义数据表操作的接口如下:
1
2
3
4
5
6
|
public interface UserDao { public void addUser(User user); public User getUser( int userId); public void update(User user); public void delete(User user); } |
然后我们使用JDBC方式实现这个接口如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
public class UserDaoJdbcImpl implements UserDao { public void addUser(User user) { Connection conn = null ; PreparedStatement ps = null ; ResultSet rs = null ; try { conn = JdbcUtils.getConnection(); String sql = "insert into user(name,birthday, money) values (?,?,?) " ; ps = conn.prepareStatement(sql); ps.setString( 1 , user.getName()); ps.setDate( 2 , new java.sql.Date(user.getBirthday().getTime())); ps.setFloat( 3 , user.getMoney()); ps.executeUpdate(); } catch (SQLException e) { throw new RuntimeException(e.getMessage(), e); } finally { JdbcUtils.free(rs, ps, conn); } } public void delete(User user) { Connection conn = null ; Statement st = null ; ResultSet rs = null ; try { conn = JdbcUtils.getConnection(); st = conn.createStatement(); String sql = "delete from user where id=" + user.getId(); st.executeUpdate(sql); } catch (SQLException e) { throw new RuntimeException(e.getMessage(), e); } finally { JdbcUtils.free(rs, st, conn); } } public User getUser( int userId) { Connection conn = null ; PreparedStatement ps = null ; ResultSet rs = null ; User user = null ; try { conn = JdbcUtils.getConnection(); String sql = "select id, name, money, birthday from user where id=?" ; ps = conn.prepareStatement(sql); ps.setInt( 1 , userId); rs = ps.executeQuery(); while (rs.next()) { user = new User(); user.setId(rs.getInt( "id" )); user.setName(rs.getString( "name" )); user.setMoney(rs.getFloat( "money" )); user.setBirthday(rs.getDate( "birthday" )); } } catch (SQLException e) { throw new RuntimeException(e.getMessage(), e); } finally { JdbcUtils.free(rs, ps, conn); } return user; } public void update(User user) { Connection conn = null ; PreparedStatement ps = null ; ResultSet rs = null ; try { conn = JdbcUtils.getConnection(); String sql = "update user set name=?, birthday=?, money=? where id=? " ; ps = conn.prepareStatement(sql); ps.setString( 1 , user.getName()); ps.setDate( 2 , new java.sql.Date(user.getBirthday().getTime())); ps.setFloat( 3 , user.getMoney()); ps.setInt( 4 , user.getId()); ps.executeUpdate(); } catch (SQLException e) { throw new RuntimeException(e.getMessage(), e); } finally { JdbcUtils.free(rs, ps, conn); } } } |
可以看到,真正核心的代码其实和第二节的代码很相像,但是按照这种风格写的代码扩展性更好,如果哪一天我们不打算使用JDBC,而改用Hibernate连接数据库,使用接口编程只需修改实现,不需要修改其他部分,大大减小了修改难度。
5、传入sql执行
需要说明的是,上面的代码使用了PreparedStatement对象,PrepareStatement是预编译的Statement对象,它在创建的时候就会把sql的大体框架搭建起来,把一些变量用占位符表示,使用时,我们再设置这些占位符的值。PrepareStatement最大的特点是可以防止sql注入,更安全,所以再需要拼接用户输入的场景,推荐使用PrepareStatement。
第四节代码的编码风格类似Hibernate,Hibernate的很多操作都是需要传入对象的,但是这种传递对象的方式灵活性不高,例如update()方法,我们把User对象上的所有属性都更新了,但是可能我们只想更新birthday一个属性,更新其他属性有点多余,所以更好的方法应该是传入sql语句,而不是一个User对象。再仔细观察,我们发现,其实我们最终只是调用了Statement上的两个方法,分别是executeUpdate和executeQuery两个方法。所以我们可以把上面的增删改查修改为如下形式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
public class UserDaoUtils { private UserDaoUtils(){ } static User executeQuery(String sql, Object[] params) throws SQLException { Connection conn = null ; PreparedStatement ps = null ; ResultSet rs = null ; User user = null ; try { conn = JdbcUtils.getConnection(); ps = conn.prepareStatement(sql); for ( int i = 1 ; i <= params.length; i++) { ps.setObject(i, params[i - 1 ]); } rs = ps.executeQuery(); while (rs.next()) { user = new User(); user.setId(rs.getInt( "id" )); user.setBirthday(rs.getDate( "birthday" )); user.setMoney(rs.getFloat( "money" )); user.setName(rs.getString( "name" )); } } finally { JdbcUtils.free(rs, ps, conn); } return user; } static int executeUpdate(String sql, Object[] params) throws SQLException { Connection conn = null ; PreparedStatement ps = null ; int rs = 0 ; try { conn = JdbcUtils.getConnection(); ps = conn.prepareStatement(sql); for ( int i = 1 ; i <= params.length; i++) { ps.setObject(i, params[i - 1 ]); } rs = ps.executeUpdate(); } finally { JdbcUtils.free( null , ps, conn); } return rs; } } public class UserDaoJdbcImpl2 implements UserDao{ @Override public void addUser(User user) { try { UserDaoUtils.executeUpdate( "insert into user(name,birthday, money) values (?,?,?)" , new Object[]{user.getName(), user.getBirthday(), user.getMoney()}); } catch (SQLException e) { e.printStackTrace(); } } @Override public User getUser( int userId) { User user = null ; try { user = UserDaoUtils.executeQuery( "select id, name, money, birthday from user where id=?" , new Object[]{userId}); } catch (SQLException e) { e.printStackTrace(); } return user; } @Override public void update(User user) { try { UserDaoUtils.executeUpdate( "update user set name=?, birthday=?, money=? where id=?" , new Object[]{user.getName(), user.getBirthday(), user.getMoney(), user.getId()}); } catch (SQLException e) { e.printStackTrace(); } } @Override public void delete(User user) { try { UserDaoUtils.executeUpdate( "delete from user where id=?" , new Object[]{user.getId()}); } catch (SQLException e) { e.printStackTrace(); } } } |
首先我们定义了一个UserDaoUtils对象,该对象包含两个方法,分别是executeQuery()和executeUpdate()方法,这两个方法均包含两个参数,分别是sql语句及sql语句的参数。然后我们定义了UserDaoJdbcImpl2类,该类使用UserDaoUtils实现了UserDao接口,相较于UserDaoJdbcImpl简化了很多。
6、利用结果集元数据封装对象
上面的UserDaoJdbcImpl2和UserDaoUtils的代码都已经很简洁了,但是有个问题,如果我们想封装其他对象的JDBC操作,那么我们将不得不重新定义一对Utils和Impl,这个其实是重复劳动,那么我们有没有什么方法可以避免这些重复劳动呢?Impl对象是必须定义的,因为我们需要实现不同的对象,如果想少定义一些对象,那么就只能不定义Utils对象。查看UserUtils的exectueQuery()和executeUpdate()方法,发现只有executeQuery()方法是与User对象耦合的,而且耦合部分只有封装结果集的部分,我们可以把这一部分代码抽象成一个接口,让调用方传入,这样就可以避免这部分耦合,所以定义接口如下:
1
2
3
|
public interface RowMapper { public Object mapRow(ResultSet rs) throws SQLException; } |
然后我们修改第四节的UserDaoUtils对象如下,并重命名为MyJdbcTemplate:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
public class MyJdbcTemplate { private MyJdbcTemplate(){} public static Object executeQuery(String sql, Object[] args, RowMapper rowMapper) { Connection conn = null ; PreparedStatement ps = null ; ResultSet rs = null ; try { conn = JdbcUtils.getConnection(); ps = conn.prepareStatement(sql); for ( int i = 0 ; i < args.length; i++) ps.setObject(i + 1 , args[i]); rs = ps.executeQuery(); Object obj = null ; if (rs.next()) { obj = rowMapper.mapRow(rs); } return obj; } catch (SQLException e) { throw new RuntimeException(e.getMessage(), e); } finally { JdbcUtils.free(rs, ps, conn); } } public static int executeUpdate(String sql, Object[] params) throws SQLException { Connection conn = null ; PreparedStatement ps = null ; int rs = 0 ; try { conn = JdbcUtils.getConnection(); ps = conn.prepareStatement(sql); for ( int i = 1 ; i <= params.length; i++) { ps.setObject(i, params[i - 1 ]); } rs = ps.executeUpdate(); } finally { JdbcUtils.free( null , ps, conn); } return rs; } } |
可以看到,现在我们的executeQuery()方法已经与User对象解耦了,所以整个对象都已经与User对象解耦,是一个通用方法,我们可以使用该对象实现UserDao接口如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
public class UserDaoJdbcImpl3 implements UserDao { @Override public void addUser(User user) { try { MyJdbcTemplate.executeUpdate( "insert into user(name,birthday, money) values (?,?,?)" , new Object[] { user.getName(), user.getBirthday(), user.getMoney() }); } catch (SQLException e) { e.printStackTrace(); } } @Override public User getUser( int userId) { User user = null ; try { user = (User) MyJdbcTemplate.executeQuery( "select id, name, money, birthday from user where id=?" , new Object[] { userId }, new RowMapper() { @Override public Object mapRow(ResultSet rs) throws SQLException { User user = new User(); user.setId(rs.getInt( "id" )); user.setName(rs.getString( "name" )); user.setMoney(rs.getFloat( "money" )); user.setBirthday(rs.getDate( "birthday" )); return user; } }); } catch (Exception e) { e.printStackTrace(); } return user; } @Override public void update(User user) { try { MyJdbcTemplate.executeUpdate( "update user set name=?, birthday=?, money=? where id=?" , new Object[] { user.getName(), user.getBirthday(), user.getMoney(), user.getId() }); } catch (SQLException e) { e.printStackTrace(); } } @Override public void delete(User user) { try { MyJdbcTemplate.executeUpdate( "delete from user where id=?" , new Object[] { user.getId() }); } catch (SQLException e) { e.printStackTrace(); } } } |
UserDaoJdbcImpl3的实现与第四节UserDaoJdbcImpl2的实现十分相似,只有getUser()方法与UserDaoJdbcImpl2不同,在UserDaoJdbcImpl3总,我们不仅要传递sql语句以及sql参数,我们还需要传递RowMapper对象,该对象能够帮助我们把查询结果封装成一个User对象。
7、用配置文件实现与具体类的解耦
我们一直在讲UserDao的不同实现,但是却一直没讲如何使用这些实现,要使用这些方法首先应该创建对象,最简单的创建方法应该是像下面这样:
1
|
UserDao userDao = new UserDaoJdbcImpl(); |
但是这种把实现硬编码进代码中不是很优雅,如果我们想修改实现,就必须重新编译代码,更好的我们使用配置文件定义实现类,创建时读取配置文件决定应该使用哪个实现。配置文件的格式使用Java Properties格式,配置文件的内容如下:
1
|
userDaoClass=cn.test.UserDaoJdbcImpl3 |
我们将使用工厂模式创建一个DaoFactory对象,该对象有一个createUserDao()方法,该方法将读返回一个UserDao接口的实现,方法的实现,我们可以选择每次都创建一个全新的返回,也可以选择第一次创建然后,缓存起来,之后就直接返回缓存对象的方法,在这里我们选择第二种,该对象的实现如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
public class DaoFactory { private static UserDao userDao = null ; private static DaoFactory instance = new DaoFactory(); private DaoFactory() { } public static DaoFactory getInstance() { return instance; } public UserDao createUserDao() { if (userDao == null ) { try { Properties prop = new Properties(); InputStream inStream = DaoFactory. class .getClassLoader() .getResourceAsStream( "daoconfig.properties" ); prop.load(inStream); String userDaoClass = prop.getProperty( "userDaoClass" ); Class<?> clazz = Class.forName(userDaoClass); userDao = (UserDao) clazz.newInstance(); } catch (Throwable e) { throw new ExceptionInInitializerError(e); } } return userDao; } } |
最后,编写一个UserDaoTest类,对上面代码进行简单测试:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public class UserDaoTest { public static void main(String[] args) { UserDao userDao = DaoFactory.getInstance().createUserDao(); User user = new User(); user.setBirthday( new Date()); user.setMoney( 234242 ); user.setName( "xxxx" ); userDao.addUser(user); User u = userDao.getUser( 1 ); System.out.println(u.getId() + "\t" + u.getName() + "\t" + u.getMoney() + "\t" + u.getBirthday()); } } |
JDBC学习笔记——增删改查的更多相关文章
- MySQL学习笔记——增删改查
有关数据库的DML操作 -insert into -delete.truncate -update -select -条件查询 -查询排序 -聚合函数 -分组查询 DROP.TRUNCATE.DELE ...
- mvc模式jsp+servel+jdbc oracle基本增删改查demo
mvc模式jsp+servel+jdbc oracle基本增删改查demo 下载地址
- jdbc编程学习之增删改查(2)
一,enum类型的使用 在SQL中没有布尔类型的数据,我们都使用过布尔类型,当属性的值只用两种情况时.例如性别等.那在数据库对这些属性的值个数比较少时我们应该使用什么数据类型呢?SQL给我们提供了枚举 ...
- eclipse控制台下实现jdbc简单的增删改查测试
1.现在MySQL中创建一个表 2.首先创建一个类 //导入的包 import java.sql.Connection;import java.sql.DriverManager;import jav ...
- jdbc 数据的增删改查的Statement Resultset PreparedStatement
完成数据库的连接,就马上要对数据库进行增删改查操作了:先来了解一下Statement 通过JDBC插入数据 (这里提供一个查找和插入方法) Statement:用于执行sql语句的对象: *1.通过C ...
- JDBC连接数据库及增删改查操作
什么是JDBC?Java语言访问数据库的一种规范,是一套APIJDBC (Java Database Connectivity) API,即Java数据库编程接口,是一组标准的Java语言中的接口和类 ...
- JDBC实现简单增删改查
JDBC全称为:Java Data Base Connectivity (java数据库连接),主要用于java与数据库的链接. 整个链接过程如下图: 1.数据库驱动:Driver 加载mysql驱动 ...
- jsp+Servlet+JavaBean+JDBC+MySQL项目增删改查
1简单的Mvc,分层建包. java resources src/mian/java (1)dao 包 JDBC连接类,连接数据库.增删改查方法,其他的方法. (2)model包 实体类,数据库字段, ...
- 使用jdbc对数据库增删改查(Mysql为例)
一.statement对象介绍 Statement对象的executeUpdate方法,用于向数据库发送增.删.改的sql语句,executeUpdate执行完后,将会返回一个整数. Statemen ...
随机推荐
- (五)RabbitMQ消息队列-安装amqp扩展并订阅/发布Demo(PHP版)
原文:(五)RabbitMQ消息队列-安装amqp扩展并订阅/发布Demo(PHP版) 本文将介绍在PHP中如何使用RabbitMQ来实现消息的订阅和发布.我使用的系统依然是Centos7,为了方便, ...
- 远离“精神乞丐”(IBM的前CEO郭士纳把员工分为四种类型)
语音丨吴伯凡 乞丐与其说是一种身份, 不如说是一种精神状态, 习惯性索取且心安理得, 习惯性寻求安慰,习惯性抱怨, 与之截然对立的, 是“操之在我”(Proactive)的精神, 乞丐型员工是公司内部 ...
- 应用 Valgrind 发现 Linux 程序的内存问题及交叉编译for arm
Valgrind 概述 体系结构 Valgrind是一套Linux下,开放源代码(GPL V2)的仿真调试工具的集合.Valgrind由内核(core)以及基于内核的其他调试工具组成.内核类似于一个框 ...
- html5 背景音乐 js控制播放 暂停
<html> <head> <title> 测试页面 </title> <script src="jquery.min.js" ...
- GLPI-开源资产管理软件
https://github.com/glpi-project/glpi/releases/tag/9.2.3 http://glpi-project.org/downloads/ 开源资产管理软件- ...
- php中foreach源码分析(编译原理)
php中foreach源码分析(编译原理) 一.总结 编译原理(lex and yacc)的知识 二.php中foreach源码分析 foreach是PHP中很常用的一个用作数组循环的控制语句.因为它 ...
- Request对象和Response对象详解
Request 1.获取请求的基本信息 1>获取请求的url和uri 2>获取url后面的请求参数部分的字符串 3>获取请求方式 4>获取主机名,IP地址 5>获取 Co ...
- [Typescript] Sorting arrays in TypeScript
In this lesson we cover all the details of how to sort a list of items using TypeScript. We also pre ...
- Xcode7 不能使用http网络请求问题解决
最近使用Xcode 7.0 写代码,发送网路请求提示: App Transport Security has blocked a cleartext HTTP (http://) resource ...
- iOS BCD编码
inline static NSData* encodeBCD(NSString *value){ //NSString *value = @"123456"; NSMutable ...