javaweb基础----JDBC、servlet(二)

发布时间:2024年01月10日

一、连接数据库

在昨天的基础上,现在我想来实现前后端以及数据库的连接。

CSDNicon-default.png?t=N7T8https://mp.csdn.net/mp_blog/creation/editor/135388510?spm=1001.2101.3001.4503在cn2包下创建2个类,一个登录界面login,还有一个实现登录过程的loginServlet。

login:

public class login {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名");
        String username = sc.nextLine();
        System.out.println("请输入密码");
        String password = sc.nextLine();
        boolean flag = loginServlet.selectByUsAndPa(username, password);
        if (flag) {
            System.out.println("登录成功");
        } else {
            System.out.println("用户名或密码错误");
        }
    }
}

这个登录界面很简单,Scanner接收用户名密码,通过loginServlet中的静态方法selectByUsAndPa判断是否登录成功。

loginServlet:

public class loginServlet {
        public static boolean  selectByUsAndPa(String username,String password) {
            Connection conn = null;
            Statement stat = null;
            ResultSet rs = null;
            try {
                Class.forName("com.mysql.jdbc.Driver");
                conn = DriverManager.getConnection
                        ("jdbc:mysql:///mydb_21?user=root&password=********");
                stat = conn.createStatement();
                rs = stat.executeQuery("SELECT * FROM user2 WHERE username='"
                        + username + "' AND PASSWORD='" + password + "'");
                if (rs.next()) return true;
                else return false;
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException();
            } finally {
                if (conn != null) {
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    } finally {
                        conn = null;
                    }
                    if (stat != null) {
                        try {
                            stat.close();
                        } catch (SQLException e) {
                            e.printStackTrace();
                        } finally {
                            stat = null;
                        }
                        if (rs != null) {
                            try {
                                rs.close();
                            } catch (SQLException e) {
                                e.printStackTrace();
                            } finally {
                                rs = null;
                            }
                        }
                    }

                }
            }
        }
}

先建立一个新的数据库user2:

CREATE TABLE user2 (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50),
PASSWORD VARCHAR(50)
);

整体格式与昨天所讲的基础别无二至,在查询的时候SQL语句用的是? ? SELECT * FROM user2 WHERE username = 'zs' AND PASSWORD = '123';

rs = stat.executeQuery("SELECT * FROM user2 WHERE username='"
        + username + "' AND PASSWORD='" + password + "'");

注意书写格式。

                if (rs.next()) return true;
                else return false;

?这段语句有个坑要注意!判断是否查询到,不能写rs != null? ? 因为rs本质上是个地址,不管查没查到地址都不会是空而是个具体的地址值,所以不管输入什么最终都会得到查询成功的结果。因此这里要用游标,如果有数据就说明查到了该位置是要查询的结果。

? ? ? ?

二、连接池

在写代码的时候我意识到一个问题,我多次重复了一段相同的操作-----创建连接,关闭连接:

            Connection conn = null;
            Statement stat = null;
            ResultSet rs = null;

            conn.close();
            stat.close();
            rs.close();

频繁地这样操作,开关连接,会造成内存的不足。

类似List的扩容操作,如果容量一满就开辟一个新的空间,这样会造成系统资源的浪费,我们可以初始时开辟一定大小的空间,每次要扩容时再开辟一定大小,避免频繁扩容。

这里也是一样,为了避免频繁开关连接,初始时建立一个连接池,给其中分配一定数量的连接,当连接池中连接都用完再扩充连接池,避免多次扩充造成浪费。

首先建立内存池,存放链接。用List集合存放,并初始化5个链接放入连接池。

public class MyPool implements DataSource {
    //存放链接
    private static List<Connection> list = new ArrayList<>();
    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
            //初始化5个连接
            for (int i = 0; i < 5; i++) {
                Properties info = new Properties();
                info.setProperty("user","root");
                info.setProperty("password","1194006164qwer");
                Connection conn = DriverManager.getConnection("jdbc:mysql:///",info);
                list.add(conn);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

因为MyPool类继承的是DataSource这个父类,因此需要在MyPool中实现父类的所有方法。

DataSource 是自 JDK 1.4 提供的一个标准接口,用于获取访问物理数据库的 Connection 对象。

?但是并不是DataSource中所有的继承方法都需要实现,实现需要的几个就可以了:

主要实现这2个方法:getConnection() , returnConncetion

    public Connection getConnection() throws SQLException {
        if(list.size() <= 0){
            for (int i = 0; i < 3; i++) {
                Connection conn = DriverManager.getConnection
                        ("jdbc:mysql:///mydb_21?user=root&password=********");
                list.add(conn);
            }
        }
        return list.remove(0);
    }

    public void returnConnection(Connection conn) {
        if(conn != null) list.add(conn);
    }

当连接池中没有链接时,也就是list.size() <= 0 ,往连接池中放入3个链接。

如果连接池中有链接,直接return返回,使外部拿到这个链接,注意返回类型,是Connection类型。要用remove方法,而不能用get方法。因为get(0)是一直返回第0个链接,而remove(0)会返回后将该位置的链接删除,后一个链接移动到该位置,如此便可return所有链接。

连接池的创建和获取链接、归还链接写完后,我们写个测试类来测试一下连接池的功能:

public class test01 {
    public static MyPool myPool = new MyPool();
    public void test01(){
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = myPool.getConnection();
            ps.executeQuery("select* from user2 where id < ?");
            ps.setString(1,"3");
            rs = ps.executeQuery();
            while (rs.next()){
                String name = rs.getString("username");
                System.out.println(name);
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            myPool.returnConnection(conn);
        }
    }
}

1、创建MyPool对象,执行getConnection方法获取连接池中的链接。

2、用SQL查询语句将查询结果放入rs对象中,通过游标得到打印出来。

这里用的是PreparedStatement类型的对象,有别于Statement:

Statement每次执行sql语句,数据库都要执行sql语句的编译,最好用于仅执行一次查询并返回结果的情形,效率高于PreparedStatement。但存在sql注入风险

PreparedStatement是预编译执行的。在执行可变参数的一条SQL时,PreparedStatement要比Statement的效率高,因为DBMS预编译一条SQL当然会比多次编译一条SQL的效率高。安全性更好,有效防止SQL注入的问题。对于多次重复执行的语句,使用PreparedStatement效率会更高一点。执行SQL语句是可以带参数的,并支持批量执行SQL。
总的来说,Statement是适合一条语句查询,存在SQL注入风险;PreparedStatement适合可变参数的SQL语句和多次重复执行的SQL语句,能有效防止SQL注入问题。

在实际项目中我们不需要自己实现连接池,可以采用现成的连接池 (如cp30连接池)。

import com.mchange.v2.c3p0.ComboPooledDataSource;

import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class Test02 {
    //c3p0连接池
    private static ComboPooledDataSource source=new ComboPooledDataSource();

    public static void main(String[] args) {
        Connection conn=null;
        PreparedStatement ps=null;
        ResultSet rs=null;

        try {
            conn=source.getConnection();
            ps=conn.prepareStatement("select * from user2 where id<?");
            ps.setInt(1,3);
            rs=ps.executeQuery();
            while (rs.next()){
                String username = rs.getString("username");
                System.out.println(username);
            }
        } catch (Exception throwables) {
            throwables.printStackTrace();
        }finally {
            if(conn!=null){
                source.close();
            }
        }
    }
}

文章来源:https://blog.csdn.net/SAKURAjinx/article/details/135406606
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。