暴力理解JDBC,多敲多记

-- 创建自定义方法
create function getLength(str varchar(20)) returns int
    begin
       return length(str);
    end
-- 选择方法
select getLength('zzz');
-- 删除方法
drop function getLength;

行转列

多行转一行多列

-- name分组
select * from test_9 group by name;
-- id和课程不要,只要姓名和分数
select name,1 as java, 1 as mysql form test_9 group by name;
-- 引入数据
select name,
    case courese
       when 'java' then score
    end
    as java,
    case course
       when 'mysql' then score
    end
    as mysql from test_9 group by name;

因为分组,数据被压缩,如果不使用组函数,只能访问最上面的语句,如果最上面是java,就没有办法获取mysql成绩;

-- 使用聚合函数把多行数据压缩到一行
select name ,
    min(
       case course
       when 'java' then score
       end
   )as java,
    max(
       case course
       when 'mysql' then score
       end
   )as mysql
from test_9 group by name;

多行转一行一列

concat(,'拼接符',),拼接,多行数据只会拼接一行
group_concat(,'拼接符',)拼接,函数将多行压扁成一行
select name,
    concat(course,'=',score)as '各科成绩'
from test_9 group by name;
select name,
    group_concat(course,'=',score separator '|')as '各科成绩'
from test_9 group by name;
select name,
    group_concat(course,'=',score order by course asc separator '|') as '各科成绩'
from test_9 group by name;

DQL-连接查询:

笛卡尔积,又叫笛卡尔乘积,多表查询中,连接的where限定条件,不能少于表的个数-1,否则会发生笛卡尔积,这个限定条件并不是随便的限定条件,二是用于维护映射两个表的条件,比如说外键;
笛卡尔乘积是一个很消耗内存的运算,它产生的新表,行数是原来行数的乘积,列则是原来列之和,所以我们需要对其进行优化;
优化一:使用等值连接条件,比如说where id=t.id;
优化二:使用inner join而不使用left join;
优化三:使用记录数更少的表当左表;
数据库在通过连接多张表返回记录的时候,会生成一张中间的临时表,然后把临时表返回给用户;
对于left join,不管on后面跟什么条件,左表的数据全部查出来,因此想要过滤,必须把条件放在where后面;
对于inner join来说,满足on后面的条件表的数据才能查出来,起到过滤条件,也可以把条件放在where后面;
on是在生成临时表时会执行的条件,需要和连接查询一起使用,而where是在生成临时表后再进行过滤,这时已经没有join的含义了;
多表查询中,左表称为主表,先生成左表,然后再生成右表,两个表中不符合on条件的数据都不要,inner join可以直接写join,不写inner;

select * from student s inner join teacher t on s.teacher_id=t.id inner join teacher t on s.teacher_id=t.id;
select * from student s left join outer join teacher t on s.teacher_id=t.id;

left join on左连接是left outer join的简写,使用left join和left outer join是一样效果,以左表为基准,左表数据全由,右表中不符合条件的数据就不要了;
right join是以右表为基准,右边中数据都有,左表中不符合条件的没有,就在指定列上用null替代,但是视图生成的时候,还是会生成左表数据,所以我们优先用inner join和left join;
外键列比较适合做限定条件,但是外键是为了保障不能随便修改数据,是数据完整性的一种约束,而表连接是因为一张表字段无法满足需求,它们两者之间没有关系;
模拟Oracle中的full join:
通过上面可以了解到inner筛选的是两个表都符合条件的数据;
left是筛选右表中符合条件的数据,左表完全保留;
right则是和left大致相反;
可要是两个表符合不符合都有呢?mysql中取消了full join连接,但是我们可以哦那个union模拟一下;
on和group by同时出现的时候on需要写在前面;

select * from student s
    left join teacher t on s.teacher_id=t.id
union
select * from student s
    right join teacher t on s.teacher_id=t.id;

备份SQL

下面这部分是关于使用Navicat工具的,右键我们需要保存的表,会有一个转存SQL文件的选项,里面有仅结构(只创建表或者数据库语句,没有数据)和结构和数据(就是创建语句和数据都有),这样我们的sql文件就生成了;
然后在navicat中右键点击数据库,点击后面的运行SQL文件,选择我们要运行的文件即可生成我们的表;
除了使用native,我们还可以使用CMD数据导入和导出;
从mysql文件夹的bin地址栏进入cmd中,mysqldump -u 用户名 -p 密码 需要导出的数据库>导出文件的保存位置;
导入操作,同样是进入bin目录下然后cmd,mysql -u 用户名 -p 密码 导入那个数据库<导入的文件路径;

JDBC

这是本文最后一部分也是学习Java,MySQL最为重要的一部分,就是JDBC;
像native的图形化操作虽然方便,但是在程序运转的时候,人是没有办法及时处理这些数据的,还是需要依赖程序语言,Java连接数据库就是使用的JDBC,它定义了一类规范标准,对应的是各种接口和抽象类,具体实现交给各数据库厂商去完成,MySQL的有自己的实现类并打成jar包发布,供程序开发人员使用;
这里提供两个下载地址:
https://repo1.maven.org/maven2
https://mvnrepository.com/
下载好之后,我们在自己的项目导入jar,以ecpliese为例,我们在项目右键Build Path然后Configure Build Path将之加入我们的项目的CLASSPATH;
使用JDBC,最开始肯定是导包,然后注册驱动,建立连接,创建运行SQL语句,运行语句,处理运行结果,释放资源;
导包除了使用上面的方法,还可以使用在java项目下创建一个lib文件夹,然后将我们需要导入的包复制到lib中,右键->Build Path->Add to Path;

// 注册驱动,但是仅仅是把其类加载到内存中
Class.forName("com.mysql.jdbc.Driver");
// 注册驱动,创建堆内存对象,也就是会有一个import的操作 下面两个语句是相同作用
Class.forName("com.mysql.jdbc.Driver").newInstance();
new com.mysql.jdbc.Driver;
// 建立连接
第一个参数url(jdbc:mysql://IP:端口号/数据库),第二个参数是数据库用户名;第三个参数是数据库密码;
// 接下来导包的时候我们需要导入的是java.sql,因为导包尽量导父类包,不要导子类包
Connection conn = DriverManager.getConnection("","","");
// 创建SQL语句
String sql = "select * from test_9";
Statement stmt = conn.createStatement();
// 运行语句
ResultSet rs = stmt.executeQuery(sql);
// 处理语句
while(rs.next){
    System.out.println(rs.getInt("id")+" ");
    // 如果传入的是整型值,那么会或等对应的列
    System.out.println(rs.getInt(1));// 不建议使用
    System.out.println(rs.getString("id")+" ");
   System.out.println(rs.getString("name")+" ");
   System.out.println(rs.getString("course")+" ");
   System.out.println(rs.getDouble("score")+" ");
}
// 释放资源 依旧是先关闭最后开启的
rs.close();
stmt.close();
conn.close();

下面我会将代码优化一遍,然后放在下面,大家可以自行感受一下将变量先声明和使用try…catch…finally的优点:

package day27_JDBC;
import java.security.interfaces.RSAKey;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
/**
* @                           _ooOoo_
*                            o8888888o
*                            88" . "88
*                            (| -_- |)
*                            O\  =  /O
*                         ____/`---'\____
*                       .'  \\|     |//  `.
*                      /  \\|||  :  |||//  \
*                     /  _||||| -:- |||||-  \
*                     |   | \\\  -  /// |   |
*                     | \_|  ''\---/''  |   |
*                     \  .-\__  `-`  ___/-. /
*                   ___`. .'  /--.--\  `. . __
*                ."" '<  `.___\_<|>_/___.'  >'"".
*               | | :  `- \`.;`\ _ /`;.`/ - ` : | |
*               \  \ `-.   \_ __\ /__ _/   .-` /  /
*           ======`-.____`-.___\_____/___.-`____.-'======
*                             `=---='
*           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
*                     佛祖保佑        永无BUG
* @author 作者 SkyMei
* @version 创建时间:2021年4月27日 下午6:41:47
* 类说明
*/
public class Day27_JDBC01 {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;
        try {
             Class.forName("com.mysql.jdbc.Driver");
             connection =  DriverManager.getConnection("jdbc:mysql://localhost:3306/***","root","***");
             String sqlString = "select * from test";
             statement = connection.createStatement();
             resultSet =  statement.executeQuery(sqlString);
             while(resultSet.next()){
                 System.out.println(resultSet.getInt("id")+" ");
                 System.out.println(resultSet.getString("name")+" ");
             }
        } catch (Exception e) {
             // TODO: handle exception
             e.printStackTrace();
        }finally{
             try {
                 if(resultSet!=null){
                      resultSet.close();
                 }
                 if (statement!=null) {
                      statement.close();
                 }
                 if (connection!=null) {
                      connection.close();
                 }
             } catch (Exception e2) {
                 // TODO: handle exception
                 e2.printStackTrace();
             }
        }
    }
}

除了Select之外,我们还可以用DML来通过JDBC操作数据;
stmt = conn.createStatement();
String sql = “insert into test() values(”","")";
// 返回影响了几条数据
int count = stmt.executeUpdate(sql);
但是我们如果使用Statement会出现一个问题,哪就是Statement比较经过用于执行静态SQL语句,如果我们用它进行增删改很可能会导致SQL注入,所以我们尽量使用PrepareStatement,它是预编译的SQL语句对象,使用它的时候,SQL会被数据库预编译和预解析,放在缓冲区,每次执行一个PrepareStatement对象的时候,会被重新解析一次,但不会被再次编译,可以重复使用,减少编译此处,提高数据库性能,为了避免SQL注入,它使用单引号将一些字符转义,并且?可以当作动态包含的参数;
PrepareStatement prst = null

String sql = “select * from test where id=?”;
prst = conn.prepareStatement(sql);
// 设置第一个?的值
prst.setInt(1,id);
String sql = “insert into test() values(?,?,?)”;
prst.setInt(1,id);
prst.setString(2,name);
prst.setDouble(3,money);
// 返回也是影响的条数
int count = prst.executeUpdate();
上面我写的简化的代码,其中连接数据库,关闭数据库的操作其实只需要进行一遍就可以,所以我们可以单独把它们抽出来,变成独立的方法,又可以进一步简化代码;

package day27_JDBC;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* @                           _ooOoo_
*                            o8888888o
*                            88" . "88
*                            (| -_- |)
*                            O\  =  /O
*                         ____/`---'\____
*                       .'  \\|     |//  `.
*                      /  \\|||  :  |||//  \
*                     /  _||||| -:- |||||-  \
*                     |   | \\\  -  /// |   |
*                     | \_|  ''\---/''  |   |
*                     \  .-\__  `-`  ___/-. /
*                   ___`. .'  /--.--\  `. . __
*                ."" '<  `.___\_<|>_/___.'  >'"".
*               | | :  `- \`.;`\ _ /`;.`/ - ` : | |
*               \  \ `-.   \_ __\ /__ _/   .-` /  /
*           ======`-.____`-.___\_____/___.-`____.-'======
*                             `=---='
*           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
*                     佛祖保佑        永无BUG
* @author 作者 SkyMei
* @version 创建时间:2021年4月27日 下午7:10:23
* 类说明
*/
public class Day27_JDBC02 {
    public static void main(String[] args) throws  ClassNotFoundException, SQLException {
        // TODO Auto-generated method stub
         getConnection();
        close(getConnection());
    }
    public static Connection getConnection() throws  ClassNotFoundException,SQLException {
        String username = "root";
        String password = "root";
        String url =  "jdbc:mysql://127.0.0.1:3306/***";
        Class.forName("com.mysql.jdbc.Driver");
        Connection connection =  DriverManager.getConnection(url,username,password);
        return connection;
    }
    // 因为Connection和Statement/PrepareStatement以及ResultSet都实现了AutoCloseable接口,所以我们可以直接写AutoCloseable接口
    public static void close(AutoCloseable obj) {
        if(obj!=null){
             try {
                 obj.close();
             } catch (Exception e) {
                 // TODO: handle exception
                 e.printStackTrace();
             }
        }
    }
}

前面部分都是单语句的执行,那么对于多语句操作,我们应该怎么处理呢?Statement和PreparedStatement实现相同吗?
我们一般使用Batch来实现JDBC的多语句执行;
stmt.addBatch(“sql语句”);
stmt.addBatch();
stmt.executeBatch();
String sql = “”;
prst.setInt()
prst.setString;
prst.addBatch();
prst.executeBatch();

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卑微-程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值