JDBC

JDBC:java database connectivity java数据库连接   官方(sun公司)定义了操作所有关系型数据库的规则(mysql、oracle...),即接口。
提供的组件
DriverManager:这个类管理一系列的数据库驱动程序,用来匹配不同的数据库源的协议
Connection:此接口具有接触数据库的所有方法,该连接对象通信的上下文。
Statement:使用创建于这个接口的对象将数据提交到数据库
ResultSet:这个对象从数据库中获取得到的结果
SQLException:这个类处理数据库执行过程中的任何错误
JDBC数据库连接池的目的:
减少频繁的创建/销毁连接,因为一次数据库连接的开销是很大的,要经过一下几个步骤:
1.加载驱动
2.获得一个Connection
3.通过TCP连接数据库
4.发送sql语句
5.执行sql,返回结果
6.关闭TCP连接
7.释放Connection
 
1.JDBC连接池测试数据库的连接:
import java.sql.*;

public class JDBCDemo {
    public static void main(String[] args) {
        //加载数据库的驱动 mysql: com.mysql.jdbc.Driver
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        /**
         * 获取数据库的连接 DriverManager
         * URL格式:
         * jdbc:数据库子协议(mysql/oracle/sql sever)  ://ip:port/database_name
         * 数据库的连接URL:   jdbc:mysql://localhost:3306/exercise    exercise 是库名  3306数据库端口号
         *
         * 用户名:root
         * 密码:123456
         */
        //获取数据库的连接 DriverManager
        try {
            /**
             * getConnection源码
             * * @param   url a database url of the form
             *  * @return a connection to the URL
             *  返回一个URL
             *  所以必须捕获异常,防止url为空
             *  * @exception SQLException if a database access error occurs or the url is
             *      * {@code null}
             *      * @throws SQLTimeoutException  when the driver has determined that the
             *      * timeout value specified by the {@code setLoginTimeout} method
             *      * has been exceeded and has at least tried to cancel the
             *      * current database connection attempt
             */
            Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/exercise", "root", "123456");



            //获取Statement的实例:通过connect实例获取
            /**
             * createStatement
             *  Creates a <code>Statement</code> object for sending SQL statements to the database.
             *      把sql语句给数据库
             */
            Statement statement = connection.createStatement();
            /**
             * 执行SQL:1.创建基本的statement对象
             * Statement statement = connection.createStatement();
             */

             //执行SQL
            String sql = "select * from student";

            //将SQL提交给MySQL数据库
          
            /**
             * executeQuery
             * * Executes the given SQL statement, which returns a single  <code>ResultSet</code> object.
             *  执行sql语句
             */
            ResultSet resultSet = statement.executeQuery(sql);


            //对结果集的处理
            System.out.println("SID:Sname:Ssex:Sage");
            while (resultSet.next()) {
                //boolean next() 判断返回结果集中是否还有数据
                //指定属性名
                String sid = resultSet.getString("SID");
                String sname = resultSet.getString("Sname");
                String ssex = resultSet.getString("Ssex");
                String sage = resultSet.getString("Sage");
                System.out.println(sid+":"+sname+":"+ssex+":"+sage);
            }

            //关闭资源
            statement.close();
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

 

Connection:  1.获取执行sql的对象   2.管理事务:开启setAutoCommit(true)、提交(commit)、回滚(rollback)

int executeUpdate:返回的是影响的行数(可以通过返回的行数判断是否执行成功),执行表相关的方法(insert 、update、 delete )  执行库相关的方法(create、alter 、drop)

ResultSet executeQuery:返回的是结果集,执行select语句

ResultSet:next判断是否还有下一行   getXxx:获取数据:getString、getInt

insert语句的练习:

 注意要有释放资源的语句:

updata和delete的语句练习:

 getXxx的参数问题:

查询的各个方法的应用:

 

 

 练习:登陆,判断是否登陆成功

 账号密码对应的数据表为:

 

 

package JdbcExercise;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
//2020/1/30
public class JDBCExer {
        public static void main(String[] args) {
           boolean flag=login("hui", "123");
            if(flag){
                System.out.println("登陆成功!!");
            }else {
                System.out.println("登陆失败!");
            }
        }
    public static boolean login(String username, String password){
        if (username == null || password == null) {
            return false;
        }
        Connection connection = null;
        Statement statement=null;
        try {
            connection = JdbcUtils.getConnection();
            String sql = "select * from user where username= '"+username+"' and password='"+password+"' ";
            //String sql1="select * from user";
            statement = connection.createStatement();
            ResultSet resultSet = statement.executeQuery(sql);
            return resultSet.next();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.close(statement,connection);
        }
        return false;
    }
}

 但是,如果将密码写成这样,也会登陆成功,即Sql注入问题

 

注:如果单引号没有拼接正确会报这样的异常:

com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException:

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''' at line 1

上述问题采用预编译之后:

JDBC中SQL注入问题

sql注入性问题:通过用户输入的参数拼接SQL时,通过改变SQL语义来访问数据库获取结果的方式称之为SQL注入攻击

SQL注入问题如何解决:采用预编译机制的PreparedStatement,在SQL上将参数通过占位符'?'来代替参数,参数另外通过setString形式将参数设置PreparedStatement对象会分别将参数和SQL交给MySQL 服务器,执行之前会进行预编译,在预编译阶段会检测SQL是否正确,正确则将编译结果直接提交给SQL服务器执行,编译失败则直接返回,不在执行PreparedStatement主要的优势如下:

1、防止SQL注入

2、使用预编译机制,执行效率是高于Statement

3、sql语句中参数值是通过?形式来替代参数,在使用PreparedStatement方法set?对应的值,相对于SQL直接拼接更加优雅

PreparedStatement和Statement区别:
1、语法不同:PreparedStatement可以使用预编译的SQL,
Statement使用静态SQL
2、效率不同:PreparedStatement执行效率高于Statement
3、安全性不同:PreparedStatement可以防止SQL注入  Statement不能防止SQL注入

注:主要区别  Statement对象: 创建语句参数不需要sql语句createStatement()  查询需要sql语句  executeOuery(sql);
                        PreStatement对象:创建含参数sql语句preparedStatement(sql)  查询不需要sql语句:execuOuery();

 2.预编译:

注:executeQuery()  查询函数,用于select语句,返回结果集

import java.sql.*;

public class JDBCDemo {
    public static void main(String[] args) {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        try {
            Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/exercise", "root", "123456");
            String sql1="select * from student where Ssex=?";
            PreparedStatement statement1=connection.prepareStatement(sql1);
            //分别传到服务器
            statement1.setString(1,"n"); //查找Sex=“n“的多行信息,1的意思是第一个参数因为只有一个Ssex属性
            ResultSet resultSet = statement1.executeQuery();

            //对结果集的处理
            System.out.println("SID:Sname:Ssex:Sage");
            while (resultSet.next()) {
                //boolean next() 判断返回结果集中是否还有数据
                //指定属性名
                String sid = resultSet.getString("SID");
                String sname = resultSet.getString("Sname");
                String ssex = resultSet.getString("Ssex");
                String sage = resultSet.getString("Sage");
                System.out.println(sid+":"+sname+":"+ssex+":"+sage);
            }


            //关闭资源
            statement1.close();
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

 

 3.插入的预编译

注:插入语句后,返回的是影响的行数,所以返回int类型

int resultSet=statement1.executeUpdate();  在preStatement() 函数中已经执行过了,所以在executeUpdate()中不用再加入sql;

加了之后会出现这样的错误(改了半天sql语句,结果...)

 

import java.sql.*;

public class PreStaExercise {
    public static void main(String[] args) {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        try {
            Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/exercise", "root", "123456");
            String sql="insert into  student(Sid,Sname,Sage,Ssex) values (?,?,?,?) ";
            PreparedStatement statement1=con.prepareStatement(sql);
            statement1.setInt(1,7);
            statement1.setString(2,"hui");
            statement1.setInt(3,22);
            statement1.setString(4,"w");
int resultSet=statement1.executeUpdate(); System.out.println(resultSet);
statement1.close(); con.close(); }
catch (SQLException e) { e.printStackTrace(); } } }

添加后的student表:

 

因为试了很多遍,而且因为Sid作为主键不能重复。所以加入两行数据时,不能有相同的Sid,否则会报如下错。

 com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '5' for key 'PRIMARY'

 

 练习1:定义一个方法,查询student表的数据将其封装为对象,然后装载集合,返回。

(将数据库查到的数据定义成一个类,放在一个List集合,此时的类类相当于javaBean)

1.定义student类  2.定义方法 public List<Student> findAll()   3.实现方法 select *from student

import domain.Student;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 2020/1/30
 */
public class JDBCDemo2 {
    public static void main(String[] args) throws ClassNotFoundException {
        List<Student> list=findAll();
        System.out.println(list);
        System.out.println(list.size());
    }
    public static List<Student> findAll() throws ClassNotFoundException {
        List<Student> list=null;
        Class.forName("com.mysql.jdbc.Driver");
        try {
            list = new ArrayList();
            Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/exercise", "root", "123456");
            String sql1 = "select * from student";
            Statement statement = connection.createStatement();
            ResultSet resultSet = statement.executeQuery(sql1);
            Student student = null;
            while (resultSet.next()) {
                int sid = resultSet.getInt(1);
                String sname = resultSet.getString(2);
                int age = resultSet.getInt(3);
                String ssex = resultSet.getString(4);//创建student对象
                student = new Student();
                student.setSid(sid);
                student.setSname(sname);
                student.setSage(age);
                student.setSsex(ssex);
                list.add(student);
            }
            statement.close();
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return list;
    }
}

有多数重复代码,所以将jdbc工具类封装起来,可以将固定的路径与表放在配置文件中,然后该类加载配置文件:

package JdbcExercise;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/**
 * 2020/1/30
 * 抽取jdbc常用的放在工具类里面
 */
public class JdbcUtils {
    private static String url;
    private static String user;
    private static String password;
    private static String driver;
    /**
     * 静态代码块负责读取配置文件获取数据库连接的各种参数,只读取一次
     * Properties集合类来获取配置文件
     */
    static {
        try {
            Properties p=new Properties();
            //方式一:加载文件(可能会因路径写错而报异常:FileNotFoundException)
           // p.load(new FileReader("src/jdbc.properties"));

            //方式二:获取src路径下的文件,标准方式-->获取ClassLoader类加载器
            /**
             * 步骤:先通过字节码文件获取class它的类加载器;sun.misc.Launcher$AppClassLoader@18b4aac2
             * 通过该加载器获取它的文件名称资源,得到一个URL统一资源定位符;
file:/C:/Users/laurarararararara/IdeaProjects/SqlTest/out/production/SqlTest/jdbc.properties * 再通过getPath方法获取该文件的路径,得到一个String类型的地址;
/C:/Users/laurarararararara/IdeaProjects/SqlTest/out/production/SqlTest/jdbc.properties * load方法加载该地址。
*/ ClassLoader classLoader = JdbcUtils.class.getClassLoader(); System.out.println(classLoader); URL resource = classLoader.getResource("jdbc.properties"); System.out.println(resource); String path = resource.getPath(); System.out.println(path); p.load(new FileReader(path)); //获取属性赋值 url=p.getProperty("url"); user=p.getProperty("user"); password=p.getProperty("password"); driver=p.getProperty("driver"); } catch (IOException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url,user,password); } public static void close(Statement statement,Connection connection) { if(statement!=null){ try { statement.close(); } catch (SQLException e) { e.printStackTrace(); } } if(connection!=null){ try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } }

测试的Demo类:

 练习2:Jdbc的事务操作

使用connection对象来管理事务  

开启事务:connection.setAutoCommit(false);   在执行sql前开启事务

提交事务:commit( );     当所有sql都执行完提交事务

回滚事务:rollback( );    在catch中回滚事务

正常情况下的代码操作,完成后,money会相应改变

 

 

可能出现的问题:

 

事务处理后:

 

 

package JdbcExercise;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class JdbcAffairs {
    public static void main(String[] args) {
        Connection connection=null;
        try {
            connection = JdbcUtils.getConnection();
            //开启事务
            connection.setAutoCommit(false);
            String sql1="update account set money= money-? where name=?";
            String sql2="update account set money= money+? where name=?";
            PreparedStatement preparedStatement1 = connection.prepareStatement(sql1);
            PreparedStatement preparedStatement2 = connection.prepareStatement(sql2);
            preparedStatement1.setInt(1,500);
            preparedStatement1.setString(2,"a");
            preparedStatement2.setInt(1,500);
            preparedStatement2.setString(2,"b");
            preparedStatement1.executeUpdate();
            //手动制造异常
            int i=3/0;
            preparedStatement2.executeUpdate();
            //提交事务
            connection.commit();
            JdbcUtils.close(preparedStatement1,connection);
            JdbcUtils.close(preparedStatement2,null);
        } catch (Exception e) {
            //如果出现任何异常要用catch语句抓,所以回滚操作放在catch;同时应注意异常的抓应该为大的Exception
            try {
                connection.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        }
    }
}

补充: Spring  JDBC:Spring框架对JDBC的简单封装

 queryForMap():查询结果封装为map集合

queryForList():查询结果封装为list集合(将每一条记录封装为一个map集合,再装载到list集合中)

queryForObject():查询结果封装为对象

query():查询结果封装为javaBean对象

导入包错误异常:

 

map集合只能放一条记录,列名为key,值为value:

 

 query方法的两种参数:

 

 queryForObject方法:

 

 

package JdbcTemplate;
import org.junit.Test;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import utils.DruidUtils;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

/**
 * 2020/2/7 JdbcTemplate对象的方法
 * 不需要close
 */
public class TemplateDemo1 {
    static JdbcTemplate template =new JdbcTemplate(DruidUtils.getDs());;
    public static void main(String[] args) {
        test3();
        System.out.println();
        test();
        test1();
        System.out.println();
        test2();
    }
    public static void test3(){
        //1.导入jar包as libraries  2.创建JdbcTemplate对象   3.调用方法
        String sql = "update account set money=1200 where id=?";
        int count = template.update(sql, 3);
        System.out.println(count);
//        //注意该方法结果集的长度只能是一条,将列名作为key,值作为value,封装为map集合
//        String sql2 = "select name,money from account where id=? or id=?";
//        Map<String, Object> stringObjectMap = template.queryForMap(sql2, 1, 2);
//        System.out.println(stringObjectMap);
        //普通的list查询一条记录,可以设置泛型将每一条记录放在一个map集合,然后把该集合放在list集合
        String sql3 = "select * from account";
        List<Map<String,Object>> list = template.queryForList(sql3);
        for (Map<String,Object> stringObjectMap1:list) {
            System.out.print(stringObjectMap1+" ");
        }
    }
    //没有简化,实现了RowMapper接口还是跟之前相同
    public static void test(){
        String sql4 = "select * from account";
        List<Account> list1 = template.query(sql4, new RowMapper<Account>() {
            @Override
            public Account mapRow(ResultSet resultSet, int i) throws SQLException {
                Account account=new Account();
                int id = resultSet.getInt(1);
                String name=resultSet.getString(2);
                int money = resultSet.getInt(3);
                account.setId(id);
                account.setName(name);
                account.setMoney(money);
                return account;
            }
        });
        for (Account account:list1) {
            System.out.print(account+"   ");
        }
        System.out.println();
    }
    //用BeanPropertyRowMapper对象,构造函数中加载类的字节码文件;可以实现到javaBean的自动封装
    public static void test1() {
        String sql4 = "select * from account";
        List<Account> list = template.query(sql4, new BeanPropertyRowMapper<Account>(Account.class));
        for (Account account:list) {
            System.out.print(account+"   ");
        }
    }

    public static void test2() {
        //返回查询记录总数;queryForObject用于聚合函数的查询
        String sql4 = "select count(money) from account";
        //返回的是long型
        Integer integer = template.queryForObject(sql4, Integer.class);
        System.out.println(integer);
        //返回记录Account对象的形式
        String sql5="select *from account where money=?";
        Account account=template.queryForObject(sql5,new BeanPropertyRowMapper<Account>(Account.class),1200);
        System.out.println(account.toString());
    }
}

 

 

posted @ 2019-12-19 12:19  acehm  阅读(286)  评论(0编辑  收藏  举报