• 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:子协议:子名称//主机名:端口/数据库名?属性名=属性值&…
        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) + " " + rs.getObject(2) + " " + rs.getObject(3) + " " + 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 {
            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") + " "
                        + rs.getObject("name") + " "
                        + rs.getObject("birthday") + " "
                        + 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() + " " + u.getName() + " " + u.getMoney() + " " + u.getBirthday());
        }
    }
  • 相关阅读:
    operator模块和functools模块
    函数注解
    用户定义的可调用类型、从定位参数到仅限关键字参数
    可调用对象
    nxos启动的初始化和https访问nx-api
    网络安全基础之网络协议与安全威胁
    华为AC中服务集命令解释配置
    转:图解ARP协议(四)代理ARP原理与实践(“善意的欺骗”)
    windows下python3 python2 共存下安装virtualenvwrapper
    关于网络安全学习的网站
  • 原文地址:https://www.cnblogs.com/jameslif/p/4080963.html
Copyright © 2020-2023  润新知