案例-登录功能

所用的技术: jdbc mysql 接口 实现类 三层架构 web -- service -- dao

创建工程: web工程

所需要的jar包: mysql的驱动包

数据库准备

CREATE TABLE `t_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(25) DEFAULT NULL,
  `passworld` varchar(25) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

实体对象

public class User {
    private int id;
    private String username;
    private String passworld;
    private int age;
    //提供有参无参构造方法
    //提供get set 方法
    //提供 toString 方法
}

dao层

public interface UserDao {
    /**
     * 登录方法
     * @param user
     * @return
     */
    public User login(User user);
}
/**

 * @Desc  dao层实现类
 */
public class UserDaoImpl implements UserDao {
    @Override
    public User login(User u) {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        //如果你的数据是本地的,并且端口是默认3306,是可以省略
        String url = "jdbc:mysql:///test";
        String user ="root";
        String pwd ="root";
        Connection conn = null;
        Statement stmt = null;
        try {
            conn = DriverManager.getConnection(url, user, pwd);
            stmt =  conn.createStatement();

            String sql ="select * from t_user where username = '"+u.getUsername()+"' and passworld = '"+u.getPassworld()+"' ";
            ResultSet rs = stmt.executeQuery(sql);

            //登录,只有两个结果,查到,查不到
            if(rs.next()){
                String username = rs.getString("username");
                String passworld = rs.getString("passworld");

                //如果有数据封装并返回
                User user1 = new User();
                user1.setUsername(username);
                user1.setPassworld(passworld);

                return user1;
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }//TODO关闭连接
        //如果没有查到用户,整体返回null
        return null;
    }
}

dao单元测试

/**
 * 测试dao层代码是否正常使用
 */
@Test
public void login() {
    //创建dao层
    UserDao dao = new UserDaoImpl();

    User user = new User();
    user.setUsername("dfdfdf");
    user.setPassworld("123");

    //调用dao方法
    User u = dao.login(user);
    if(u!=null){
        System.out.println("登录成功");
    }else{
        System.out.println("登录失败");
    }
}

service层

public interface UserService {
    /**
     * 登录
     * @param user
     * @return
     */
    public boolean login(User user);
}
/**

 * @Desc  业务层实现类
 */
public class UserServiceImpl implements UserService {
    //创建dao对象
    private UserDao userDao = new UserDaoImpl();

    @Override
    public boolean login(User user) {

        //调用dao层方法
        User res = userDao.login(user);
        if(res != null){
            //不能null表示dao查到数据,登录成功
            return true;
        }
        //否则登录失败
        return false;
    }
}

测试service

@Test
public void login() {
    //创建对象
    UserService service = new UserServiceImpl();

    User user = new User();
    user.setUsername("rose");
    user.setPassworld("123");

    boolean login = service.login(user);
    if(login){
        System.out.println("登录成功");
    }else{
        System.out.println("登录失败");
    }
}

web层

/**

 * @Desc web层获取用户提交过来的数据,但我们还没学到,这里做一个测试当作web层
 */
public class UserWeb {
    public static void main(String[] args) {
        //创建对象
        UserService service = new UserServiceImpl();

        User user = new User();
        user.setUsername("jack");
        user.setPassworld("123");

        boolean login = service.login(user);
        if(login){
            System.out.println("登录成功");
        }else{
            System.out.println("登录失败");
        }
    }
}

sql注入问题

-- 原sql
select * from t_user where username = "jack" and passworld = '123'

-- 什么是sql注入: 通过字符串拼接,篡改sql,这就是sql注入
select * from t_user where username = ''or '1'='1' and passworld = ''or'1'='1'
select * from t_user where 1 = 1

解决sql注入问题的思路

-- 解决sql注入问题?
-- 出现的sql注入原因:字符串拼接,篡改sql
-- 解决思想,就让sql进行拼接
select * from t_user where username = ? and passworld = ?

3.PreparedStatement解决sql注入

预编译语句 对象;

编译sql语句;

再填充参数;

1,执行效率:Statement 采取直接编译 SQL 语句的方式,扔给数据库去执行,而 PreparedStatement 则先将 SQL 语句预编译一遍,再填充参数,这样效率会高一些。SQL 语句被预编译并且存储在 PreparedStatement 对象中,其后可以使用该对象高效地多次执行该语句。

2,代码可读性:Statement 中 SQL 语句中需要 Java 中的变量,加就得进行字符串的运算,还需要考虑一些引号、单引号的问题,参数变量越多,代码就越难看,而且会被单引号、双引号搞疯掉;
而 PreparedStatement,则不需要这样,参数可以采用“?”占位符代替,接下来再进行参数的填充,
这样利于代码的可读性,并且符合面向对象的思想。

3,安全性:Statement 由于可能需要采取字符串与变量的拼接,很容易进行 SQL 注入攻击,而 PreparedStatement 由于是预编译,再填充参数的,不存在 SQL 注入问题。
/**

 * @Desc  dao层实现类
 */
public class UserDaoImpl implements UserDao {
    @Override
    public User login(User u) {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        //如果你的数据是本地的,并且端口是默认3306,是可以省略
        String url = "jdbc:mysql:///test";
        String user ="root";
        String pwd ="root";
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            conn = DriverManager.getConnection(url, user, pwd);
            /*stmt =  conn.createStatement();
            String sql ="select * from t_user where username = '"+u.getUsername()+"' and passworld = '"+u.getPassworld()+"' ";
            ResultSet rs = stmt.executeQuery(sql);*/

            //预处理,提前把sql进行处理,可以防止sql注入问题
            String sql ="select * from t_user where username = ? and passworld = ?";
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1,u.getUsername());
            pstmt.setString(2,u.getPassworld());

            //执行,再发送
            rs = pstmt.executeQuery();

            //登录,只有两个结果,查到,查不到
            if(rs.next()){
                String username = rs.getString("username");
                String passworld = rs.getString("passworld");

                //如果有数据封装并返回
                User user1 = new User();
                user1.setUsername(username);
                user1.setPassworld(passworld);

                return user1;
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }//TODO关闭连接

        //如果没有查到用户,整体返回null
        return null;
    }
}

PreparedStatement进行了预编译处理,当下次执行相同的sql,sql就不会再编译了,比Statement的效率高。

Statement每执行一次sql进行编译一次

jdbc批处理-了解

业务场景:当需要向数据库中发送一批sql时,就可以用jdbc的批处理机制,以提升执行效率,避免向数据库一条条的发送

statement批处理

/**
     * statement批处理
     */
    @Test
    public void test1(){
        Connection conn = null;
        Statement statement = null;
        try {
            conn =  JDBCUtil.getConnection();
            statement = conn.createStatement();

            for(int i = 1; i<= 10000; i++){

                String sql ="insert into t_user (id,username, passworld,age) values(null,'"+i+"', '123', 28)";
                statement.addBatch(sql);
                //一次性向数据库中添加了3000条
                if(i % 3000 == 0 ){
                    //执行批次
                    statement.executeBatch();
                    //清空批次
                    statement.clearBatch();
                }
            }
            //执行批次
            statement.executeBatch();
            //清空批次
            statement.clearBatch();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JDBCUtil.close(statement,conn);
        }

    }

PreparedStatement批处理

/**
     * PreparedStatement
     * @throws SQLException
     */
    @Test
    public void test2() throws SQLException {

        Connection conn = JDBCUtil.getConnection();
        String sql ="insert into t_user (username, passworld,age) values(?, ?, ?)";
        PreparedStatement pstmt = conn.prepareStatement(sql);

        for (int i = 1; i <=10000 ; i++) {
            pstmt.setString(1,"u"+i);
            pstmt.setString(2,"p"+i);
            pstmt.setInt(3,i);

            pstmt.addBatch();

            if(i % 3000 == 0){
                pstmt.executeBatch();
                pstmt.clearBatch();
            }
        }

        pstmt.executeBatch();
        pstmt.clearBatch();

        JDBCUtil.close(pstmt,conn);
    }

扩展帮助:

一、问题描述

在使用idea编写java程序时,需要Scanner从控制台输入数据,但发现无法输入。

二、解决方法

这个需要根据自己使用IDEA的位数来进行修改,保险起见可以两个都修改。 64位: 将idea安装路径bin目录下的idea64.exe.vmoptions文件加一行参数: -Deditable.java.test.console=true 32位: 将idea安装路径bin目录下的idea.exe.vmoptions文件加一行参数: -Deditable.java.test.console=true