JDBC学习笔记——增删改查

2019年12月05日 阅读数:151
这篇文章主要向大家介绍JDBC学习笔记——增删改查,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

JDBC学习笔记——增删改查

一、数据库准备                                                                                  html

      要用JDBC操做数据库,第一步固然是创建数据表:java

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;

二、JDBC链接数据库的基本步骤                                                             mysql

      JDBC链接数据库包含如下几个基本步骤:一、注册驱动 ;二、创建链接(Connection);三、建立SQL语句(Statement);四、执行语句;五、处理执行结果(ResultSet);六、释放资源。sql

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:子协议:子名称//主机名:端口/数据库名?属性名=属性值&…
     Connection conn = DriverManager.getConnection( "jdbc:mysql://localhost:3306/jdbc" "root" "" );
 
     // 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();

三、简单的增删改查                                                                         数据库

      第二节的代码有一个问题,若是咱们在执行代码时抛出异常,那么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 {
         return  DriverManager.getConnection( "jdbc:mysql://localhost:3306/jdbc" "root" "" );
     }
 
     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);
     }
}  

四、面向对象封装增删改查                                                                安全

      第三节的例子只是为了展现如何使用JDBC进行增删改查操做,在项目中真正使用时,咱们是不会像上面的例子这样简单使用的,Java是面向对象的,因此咱们通常会使用面向对象的思想对操做进行封装。首先,其实对于数据表每一条数据,咱们均可以认为它是一个对象实例,例如此例中咱们定义的数据表User有id,name,birthday和money四个属性,对应的咱们能够建立User类以下:app

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链接数据库,使用接口编程只需修改实现,不须要修改其余部分,大大减少了修改难度。

五、传入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简化了不少。

六、利用结果集元数据封装对象                                                           

      上面的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 =