zoukankan      html  css  js  c++  java
  • 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());
        }
    }
  • 相关阅读:
    python-用闭包(装饰器)实现计算器的功能
    python 斐波那契数列
    python 递归方法
    python冒泡排序
    一步步学习ASP.NET MVC3 (6)——@helper,@functions
    一步步学习ASP.NET MVC3 (5)——View从Action中获得数据
    一步步学习ASP.NET MVC3 (4)——Razor(2)
    一步步学习ASP.NET MVC3 (3)——Razor(1)
    ASP.NET MVC Spring.NET NHibernate 整合
    ASP.NET MVC NHibernate 整合
  • 原文地址:https://www.cnblogs.com/laurarararararara/p/12067036.html
Copyright © 2011-2022 走看看