(四)04-使用JDBC重写博客项目

bug修复

前面我们的项目中,Blog类和User类的id属性设置为long类型,实际上应该设置为Long类型会更好。因为:

  1. Java中Long 类型建立的时候如果没赋值,会默认给一个null.而long类型建立的时候如果没有赋值,则是0L。而id属性是数据库自动去填充值的,不应该有默认值,我们可以通过是否为null来判断是否存在记录。
  2. Long提供了很多API, 比如查询、赋空值等简便, 可以利用Long本身的优化;如你使用Long.valueOf时 可以利用缓存的-128至127之间的值; 还有从使用hibernate、el表达式等都是包装类型,减少了装箱/拆箱;

我们还得修改模型类的getId、setId的返回类型。

同理,模型类比如购物车Cart,它的字段页应该是Integer而不是int。相应的我们要修改那些传入参数long参数对应的修改为Long才行。一般模型类中的方法参数最好都是封装类型而不是基本类型。

model类里面去掉id = count++;

因为id自增交由数据库去完成,所以我们不必使用java代码去实现。所以我们要去修改User模型类和Blog模型类。

User模型类需要添加新的字段description

相应的修改用户仓库初始化代码和用户模型类构造器

数据库通用操作封装到DBHleper工具类

package com.jspblog.utils;

import com.jspblog.model.Blog;
import com.jspblog.model.Project;
import com.jspblog.model.User;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

public class DBHelper {

    public static String driver = "com.mysql.jdbc.Driver";
    public static String connectionString = "jdbc:mysql://127.0.0.1:3306/jspblog?useSSL=false";
    public static String username = "root";
    public static String password = "123456";

    // 1、加载MYSQL驱动,这里MySQL的JDBC驱动类是com.mysql.jdbc.Driver,要求类路径中包含相应的Driver类
    static {
        try {
            Class.forName(driver).newInstance();
        } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    // 2、连接到MYSQL,通过DriverManger来创建Connection对象,获取数据库连接
    public static Connection getConnection() throws Exception {
        return DriverManager.getConnection(connectionString, username, password);
    }

    // 3、创建PreparedStatement用以执行SQL语句

    // 插入操作,我们希望获得插入记录后的编号,也就是返回值是id
    public static Long create(String sql) {

        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;

        try {
            //2,连接到MYSQL
            connection = getConnection();
            //3,PreparedStatement用以执行SQL语句
            statement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            statement.executeUpdate();
            //4,ResultSet用于接收SQL语句执行后的结果,更新之后我们只需要获取更新记录的id属性即可,而不是整个记录对象
            resultSet = statement.getGeneratedKeys();
            //5、遍历并解析结果
            if (resultSet.next()) {
                return resultSet.getLong(1);
            }
            return null;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            //6,清理数据库连接相关的所有资源
            clear(resultSet, statement, connection);
        }
    }

    // 删除操作,用于remove动作
    // 由于删除之后就不存在就该记录了,所以不需要遍历结果页不用返回id,直接返回数字0表示删除成功
    public static int delete(String sql) {
        Connection connection = null;
        PreparedStatement statement = null;

        try {
            connection = DBHelper.getConnection();
            statement = connection.prepareStatement(sql);
            return statement.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        } finally {
            clear(null, statement, connection);
        }
    }

    //查询单条记录,查询就应该返回对象,而不像插入那样只要返回id。由于查询的可能是不同对象,所以使用泛型
    public static Blog queryBlog(String sql) {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;

        try {
            connection = DBHelper.getConnection();
            statement = connection.prepareStatement(sql);
            resultSet = statement.executeQuery();
            if (resultSet.first()) {
                int userId = resultSet.getInt("author");
                User user = queryUser("select * from user where id="+userId);
                Blog blog = new Blog(resultSet.getString("title"),resultSet.getString("content"),resultSet.getDate("createdTime"),user);
                return blog;
            }
            return null;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            clear(null, statement, connection);
        }
    }

    //查询单条记录,查询就应该返回对象,而不像插入那样只要返回id。由于查询的可能是不同对象,所以使用泛型
    public static User queryUser(String sql) {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;

        try {
            connection = DBHelper.getConnection();
            statement = connection.prepareStatement(sql);
            resultSet = statement.executeQuery();
            if (resultSet.first()) {
                User user = new User(resultSet.getString("email"),resultSet.getString("username"),resultSet.getString("password"));
                return user;
            }
            return null;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            clear(null, statement, connection);
        }
    }

    //查询单条记录,查询就应该返回对象,而不像插入那样只要返回id。由于查询的可能是不同对象,所以使用泛型
    public static Project queryProject(String sql) {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;

        try {
            connection = DBHelper.getConnection();
            statement = connection.prepareStatement(sql);
            resultSet = statement.executeQuery();
            if (resultSet.first()) {
                Project project = new Project(resultSet.getString("name"),resultSet.getString("description"),resultSet.getString("url"),resultSet.getString("logo"));
                return project;
            }
            return null;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            clear(null, statement, connection);
        }
    }

    //查询多条记录,与查询单条记录不同在于是遍历结果集放入数组中。由于查询的可能是不同对象,所以使用泛型
    @SuppressWarnings("unchecked")
    public static List<Blog> queryBlogList(String sql) {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;

        try {
            List<Blog> list = new ArrayList<Blog>();
            connection = DBHelper.getConnection();
            statement = connection.prepareStatement(sql);
            resultSet = statement.executeQuery();

            while(resultSet.next()) {
                int userId = resultSet.getInt("author");
                User user = queryUser("select * from user where id="+userId);
                Blog blog = new Blog(resultSet.getString("title"),resultSet.getString("content"),resultSet.getDate("createdTime"),user);
                list.add(blog);
            }
            return list;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            clear(null, statement, connection);
        }
    }

    //查询多条记录,与查询单条记录不同在于是遍历结果集放入数组中。由于查询的可能是不同对象,所以使用泛型
    @SuppressWarnings("unchecked")
    public static List<Project> queryProjectList(String sql) {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;

        try {
            List<Project> list = new ArrayList<Project>();
            connection = DBHelper.getConnection();
            statement = connection.prepareStatement(sql);
            resultSet = statement.executeQuery();

            while(resultSet.next()) {
                Project project = new Project(resultSet.getString("name"),resultSet.getString("description"),resultSet.getString("url"),resultSet.getString("logo"));
                list.add(project);
            }
            return list;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            clear(null, statement, connection);
        }
    }

    //查询多条记录,与查询单条记录不同在于是遍历结果集放入数组中。由于查询的可能是不同对象,所以使用泛型
    @SuppressWarnings("unchecked")
    public static List<User> queryUserList(String sql) {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;

        try {
            List<User> list = new ArrayList<User>();
            connection = DBHelper.getConnection();
            statement = connection.prepareStatement(sql);
            resultSet = statement.executeQuery();

            while(resultSet.next()) {
                User user = new User(resultSet.getString("email"),resultSet.getString("username"),resultSet.getString("password"),resultSet.getString("description"));
                list.add(user);
            }
            return list;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            clear(null, statement, connection);
        }
    }

    // 计数
    //技术我们一般传入sql语句是select count(*)这样的语句,返回结果只有一条记录,页只有一个字段。该字段存的就是count的记录数目
    public static int queryCount(String sql) {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;

        try {
            connection = DBHelper.getConnection();
            statement = connection.prepareStatement(sql);
            resultSet = statement.executeQuery();
            //由于结果肯定只有一条记录,所以我们取出first结果即可
            if (resultSet.first()) {
                return resultSet.getInt(1);  //获取结果集中第一列的值,count语句返回的本来就只有一个字段
            }
            return 0;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        } finally {
            clear(null, statement, connection);
        }
    }

    //6、清理数据库连接相关的所有资源
    private static void clear(ResultSet resultSet, Statement statement, Connection connection) {
        try {
            if (resultSet != null) {
                resultSet.close();
            }

            if (statement != null) {
                statement.close();
            }

            if (connection != null) {
                connection.close();
            }
        } catch (SQLException ignored) {

        }
    }
}

Statement.RETURN_GENERATED_KEYS用于获取自动生成的键值,这里是自增的id号。这里也就是我们不必给数据库对象的id属性赋值,在执行sql语句的时候,添加该代码会自动往记录的id自动填入一个自动增长的数字。
getGeneratedKeys();用于获取主键,也就是上面我们执行插入语句时,自动填充的id字段的值。

新建数据库表project

执行语句如下:

CREATE TABLE `project` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `description` longtext NOT NULL,
  `url` varchar(255) NOT NULL,
  `logo` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

保持原Servlet不变,修改模型类

比如我们ProjectRepository类中的getAll方法:

public List<Project> getAll() {
    return projects;
}

应该修改为:

public List<Project> getAll() {
        return DBHelper.queryProjectList("select * from project");
}

因为Servlet获取数据是去调用模型类中的方法而已,而获取数据修改为从数据库中读取也是把模型类中获取的数据的方法体修改为调用DBHelper执行sql语句而已。在Servlet中调用的模型类方法不变,变更的是模型类方法的实现,所以自然不需要修改Servlet文件。

而且private static List<Project> projects = new ArrayList<>();属性也可以去掉了。因为之前没有数据库,所以使用一个数组属性来保存多个对象,比如我们要获取所有项目信息就去访问这个数组属性。现在有了数据库之后,要获取所有项目信息只要调用getAll()方法,得到的返回值就是一个包含所有项目信息的数组,我们直接使用方法的返回值就行,就不需要该数组属性了。

其他方法比如User、Blog等相关的多个类也做这样的修改,模型类中获取数据都改成使用DBHelper类从数据库获取数据,去掉数组属性。

修改jsp文件里面引入类的包名,由mvc改为jspblog

把模型类中的数据初始化代码删除掉,通过Navicate在数据库中录入数据

把在WEB-INF/lib目录里面放入数据库驱动的jar包mysql-connector-java-5.1.40-bin.jar

必须完成上面全部的修改后,项目才能运行。

使用JDBC先完成创建博客、博客列表、博客详情三个功能与数据库交互

到此,就完成了博客项目使用JDBC与数据库交互的全部改写,测试通过。

提交到git仓库

创建.gitignore文件,由于我们之前使用oschina自动创建的.gitignore文件,所以修改该文件后还是无法生效,应该执行下面操作清除一下缓存cache,才能使修改后的.gitignore 生效。

git rm -r --cached .  #清除缓存  
git add . #重新trace file  
git commit -m "update .gitignore,(四)04-使用JDBC重写博客项目" #提交和注释  
 git push git@git.oschina.net:michael-first/jdbcblog.git master --force

为了以后提交方便,我们添加如下代码,以后提交的时候就使用jdbcblog替换代码仓库的地址,很方便

git remote add jdbcblog git@git.oschina.net:michael-first/jdbcblog.git
git push jdbcblog master

仓库: https://git.oschina.net/michael-first/jdbcblog

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容