升级版JDBC(扩展)

导包都省略

1.resources/driver.properties

driver.properties

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/kj10?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
username=root
password=zjj

2.com.kgc.kj10.dao/ ConnectionFactory、BaseDAO、UserinfosDAOImpl、IDataHandler
ConnectionFactory

// 利用简单工厂模式提升代码的重用性;
//封装注册数据库的驱动和获得与数据库的连接;
// 利用配置文件的方式减少硬编码,便于维护。
public class ConnectionFactory {
    private static ConnectionFactory factory;
    private static String DRIVER;
    private static String URL;
    private static String USERNAME;
    private static String PASSWORD;

    static {
        Properties prop = new Properties();
        //获取文件的在当前机器下的路径
        String path = null;
        try {
//            UrlEncode编码主要用于将字符串以URL编码,返回一个字符串;
//            https://www.bbsmax.com/A/pRdBeqXa5n/
//            UrlDecode解码主要对字符串进行URL解码,返回已解码的字符串;
            path = URLDecoder.decode(
                    ConnectionFactory.class.getResource("/driver.properties").getPath(), "UTF-8");
              //根目录下的绝对路径 utf-8中文
//            xxx.class.getResource用JAVA获取文件      配置文件信息的获取

//      加载(读取)文件  (以流的形式)
        prop.load(new FileInputStream(path));
//      获取文件中的数据
        DRIVER=prop.getProperty("driver");
        URL=prop.getProperty("url");
        USERNAME=prop.getProperty("username");
        PASSWORD=prop.getProperty("password");

        } catch (IOException e) {
            e.printStackTrace();
        }
//        System.out.println(path);
    }

    private ConnectionFactory(){}

    public static ConnectionFactory builder(){
        if (factory == null){
            factory = new ConnectionFactory();
        }
        return factory;
    }
    public Connection getConnection(){
    // 这个方法也不完美  可能会调用好多好多次连接 1000个人同时并发 
    //会有人连接不上 用jmeter测试 效果使用 连接池 多线程!
        Connection conn = null;
        try {
            Class.forName(DRIVER);
            conn = DriverManager
                    .getConnection(URL,USERNAME,PASSWORD);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }
// 类的单例模式只需要执行一次数据库连接,可以防止数据库的多次连接给服务器造成负担
    public static void main(String[] args) {
        for (int i=0;i<100;i++) {
            System.out.println(ConnectionFactory.builder().getConnection());
        }
    }
}

BaseDAO

public class BaseDAO {
    private static Connection connection;
    private static PreparedStatement pstat;
    private static ResultSet rs;

    public BaseDAO(){
        connection=ConnectionFactory.builder().getConnection();
    }

    public int update(String sql,Object [] params) {
        int count = 0;
        try {
            pstat = connection.prepareStatement(sql);
            for ( int i=0;i<params.length;i++ ) {
                pstat.setObject(i+1,params[i]);
            }
            count = pstat.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            closeAll();
        }
        return count;
    }

    public ResultSet query(String sql,Object [] params) {
        try {
            pstat = connection.prepareStatement(sql);
            for ( int i=0;i<params.length;i++ ) {
                pstat.setObject(i+1,params[i]);
            }
            rs = pstat.executeQuery();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return rs;
    }
    public static void closeAll(){
        try {
            if (rs!=null)rs.close();
            if (pstat!=null)pstat.close();
            if (connection!=null)connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
} 

IDataHandler

public interface IDataHandler<T> {
    int save(T t);//添值
    int delete(int id);//删除某段
    int update(T t);//修改值
    List<T> findAll();//查询所有
    T findById(int id);//查某一段
}

实体类省略Userinfos

UserinfosDAOImpl

public class UserinfosDAOImpl extends BaseDAO implements IDataHandler<Userinfos> {
    @Override
    public int save(Userinfos userinfos) { return 0; }

    @Override
    public int delete(int id) {return 0;}

    @Override
    public int update(Userinfos userinfos) {return 0;}

    @Override
    public List<Userinfos> findAll() {
        String sql="select * from userinfos";
        return ResultToArrayUtil.change(query(sql,new Object[]{}), Userinfos.class);
    }

    @Override
    public Userinfos findById(int id) { return null; }

    public Userinfos login(String username,String password) {
        String sql="select * from userinfos where username=? and password=?";
        List<Object> lst = ResultToArrayUtil.change(query(sql, new Object[]{username, password}), Userinfos.class);
        return lst.isEmpty()?null:(Userinfos) lst.get(0);
    }
}

里面有个查询可以用到反射
com.kgc.commons/ResultToArrayUtil
ResultToArrayUtil

public class ResultToArrayUtil {

    public static <T> List<T> change(ResultSet rs, Class cls) {
        List<T> lst = new ArrayList<T>();
        try {
            while (rs.next()) {
                Object obj = cls.newInstance();//类的对象 该方法就是就是创建一个新的对象 直接输出
                //获取类中的所有属性
                Field[] fields = cls.getDeclaredFields();
                for (Field field:fields) {
                    //将属性临时关闭私有权限
                    field.setAccessible(true);//让我们在用反射时访问私有变量
                    //获取属性的类型
                    String typeName=field.getType().getTypeName().toUpperCase();
                    //获得属性的名字
                    String fieldName=field.getName();
                    switch (typeName){
                        case "STRING":
                            field.set(obj,rs.getString(fieldName));//通过反射塞值  field.setString ==》set
                            break;
                        case "INT":
                        case "INTEGER":
                            field.set(obj,rs.getInt(fieldName));
                            break;
                        case "DOUBLE":
                            field.set(obj,rs.getDouble(fieldName));
                            break;
                        case "DATE":
                            field.set(obj,rs.getDate(fieldName));
                            break;
                    }
                }
                lst.add((T)obj);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return lst;
    }
}

seivice省略

针对于LoginServlet
可以直接这样写

 // dopost 是用来接收post方法的
public class LoginServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
    }
//  doget 是接收网页 用get方法时调用的
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    }
}

以上方法只针对人数很少的时候,
一旦人数多了,就会并发问题,所以要进行优化
可以用apache-jmeter-5.3测试

并发问题连接出问题==> 建连接池
1.创建1个容器ArrayList
2.开启N个连接(少量)
3.连接状态考虑。。。
4.分配连接(有没有空闲的连接)
4.1 如果有空闲连接直接给用户(改状态为不可用)
4.2 如果没有空闲连接
4.2.1检测容器是否满了
不满-------> 创1个新连接给用户
满--------->等
4.3不用的连接
4.3.1 用户主动销毁close
4.3.2 收回连接检测
没有问题–>修改状态
有问题—> close

com.njbdqn.kj10.dao/ StatuConnection

package com.njbdqn.kj10.dao;
import java.sql.Connection;
public class StatuConnection {
    private Connection conn;
//   connection是java中的一个类,在java.sql包下,用于获得数据库的连接
    private boolean isBusy = false;//空状态 判断 是否有人再用

    public Connection getConn() { return conn; }

    public void setConn(Connection conn) { this.conn = conn; }

    public boolean isBusy() { return isBusy; }

    public void setBusy(boolean busy) { isBusy = busy; }
}

com.njbdqn.kj10.dao/ DataLinked

package com.njbdqn.kj10.dao;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLDecoder;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
/**
 * @Description 数据库连接池
 * 1000人 容纳的 单线程
 */
public class DataLinked {
    private DataLinked(){
    }
    //1.用户获取连接  用户只要连接
    public static Connection getConnection(){
        return Pool.getConnectionFormFree().getConn();
    }
    //2.用户销毁连接
    public static void destory(Connection conn){ Pool.destory(conn); }
    private  static class Pool{
        //静态私有内部类
        private static List<StatuConnection> pool = new ArrayList<>();
        private static String DRIVER;
        private static String URL;
        private static String USERNAME;
        private static String PASSWORD;
        private static int INIT =3;
        private static String QUERYSTR;
        private static int MAXLINKED = 30;
        static {
            readDriver();
            //产生连接存放到连接池中 ,  只在程序运行的 第一次做
            for (int i = 0; i < INIT; i++) {
                pool.add(buildConnection());
            }
        }
        private static void readDriver(){
            Properties prop = new Properties();
            String path = null;
            try {
                path = URLDecoder.decode(
                        ConnectionFactory.class.getResource("/driver.properties").getPath(), "UTF-8");//根目录下的绝对路径 utf-8中文
                prop.load(new FileInputStream(path));
                DRIVER=prop.getProperty("driver");
                URL=prop.getProperty("url");
                USERNAME=prop.getProperty("username");
                PASSWORD=prop.getProperty("password");
                String init = prop.getProperty("init");
                if (init != null){
                    INIT = Integer.parseInt(init);
                }
                String maxlinked = prop.getProperty("maxlinked");
                if (maxlinked!=null){
                    MAXLINKED = Integer.parseInt(maxlinked);
                }
                QUERYSTR = prop.getProperty("querystr");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //1.创建连接1个 没有使用的状态连接
        private static StatuConnection buildConnection(){
            StatuConnection conn = new StatuConnection();
            Connection connection = null;
            try {
                Class.forName(DRIVER);
                connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
            } catch (Exception e) {
                e.printStackTrace();
            }
            conn.setConn(connection);
            return conn;
        }
        //2.获取连接
        //2.1 先检测是否空闲的连接
        public static StatuConnection getConnectionFormFree(){
            //循环集合
            StatuConnection conn = null;
            //搜索不忙的连接
            for (StatuConnection sc:pool) {
                if (!sc.isBusy()){
                    conn = sc;
                    sc.setBusy(true);
                    return sc;
                    //break;//解决6次连接 return sc; return 跳出多种循环
                }
            }
            //检测是否全部忙不忙
            if (conn == null){
                //检测pool中是否还能创建新连接
//                if (MAXLINKED ==pool.size()){
                if (pool.size()>=MAXLINKED){
                    //休息一会 不要太忙  递归太快了
//                   可以更快 就是连接用完就换下一个 不休息
                    try {
                        Thread.sleep(3000);//3秒
//                        500  成功了 但是速度慢 改快点 第2步
                        System.out.println("执行。。。。。。");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    return getConnectionFormFree();//递归
                }else {
                    conn = buildConnection();
                    conn.setBusy(true);
                    pool.add(conn);
                    return conn;
                }
            }else {
                return conn;
            }
        }
        //3.销毁连接
        public static void destory(Connection con){
            //判断连接是否有效
            ResultSet rs = null;
            try {
                PreparedStatement pstat = con.prepareStatement(QUERYSTR);
                rs = pstat.executeQuery();
                if (rs.next()){
                    for (StatuConnection sc: pool) {
                        if (sc.getConn() == con){
                            sc.setBusy(false);
                            break;
                        }
                    }
                }
            } catch (SQLException e) {
                //立即关闭连接
                for (StatuConnection sc : pool) {
                    if (sc.getConn() == con){
                        try {
                            pool.remove(sc);//销毁
                            sc.getConn().close();//强行关闭  不然占资源 跟连接池的连接关闭啊
                        } catch (SQLException ex) {
                            ex.printStackTrace();
                        }
                    }
                }
            }
        }
    }
//     1 2 3加三把锁 synchronized 只能一个人       10000人连接  但特别慢 不方便     要改成线程代码 最终版
}

com.njbdqn.kj10.dao/ BaseDAO
改动
private改成protected
protected static Connection connection;

//public BaseDAO()
{ connection=ConnectionFactory.builder().getConnection(); }
//改成如下
//升级版
    public BaseDAO(){ connection=DataLinked.getConnection(); }

com.njbdqn.kj10.dao.impl/UserinfosDAOImpl
改动:

public Userinfos login(String username,String password) {
    String sql="select * from userinfos where username=? and password=?";
    List<Object> lst = ResultToArrayUtil.change(query(sql, new Object[]{username, password}), Userinfos.class);
    //    升级版
    DataLinked.destory(connection);
    return lst.isEmpty()?null:(Userinfos) lst.get(0);
}

在src/main下创建一个包resources
driver.properties

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/kj10?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
username=root
password=zjj
init=5
querystr=select 1
#结果就是1 不能用*啊
maxlinked=10
#1000人
#10次连接  50次 速度慢 改大点 第1步

#可以更快 就是连接用完就换下一个 不休息

#数据库改模式 数据库是逐行扫描的  太慢了 限制位置查询 索引 可以变快
#20000人 同时并发 一起进入
#但是人数更多的 访问,还是会慢 并且爆红 测试

什么是jmeter?

Apache JMeter 是 Apache 组织开发的基于 Java 的压力测试工具。用于对软件做压力测试,它最初被设计用于 Web 应用测试,但后来扩展到其他测试领域。
发送请求到服务端,获取目标服务的统计信息,生成不同格式的报告。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值