数据库设计以及和项目之间的关系
数据库设计以及和项目之间的关系(涉及JDBC)
规范数据设计
糟糕的数据库设计:
-
数据冗余,浪费空间
-
数据库插入和删除都会麻烦、异常[屏蔽使用物理外键)
-
程序的性能差
良好的数据库设计:
- 节省内存空间
- 保证数据库的完整性
- 方便我们开发系统
软件开发中,关于数据库的设计
- 分析需求: 分析业务和需要处理的数据库的需求
- 概要设计: 设计关系图 E-R 图
设计数据库的步骤(例如设计博客网站)
-
收集信息,分析需求(表的类型,每种表需要的信息)
- 用户表 (用户登录注销,用户的个人信息,写博客,创建分类)
- 分类表(文章分类,谁创建的)
- 文章表 (文章的信息)
- 自定义表(系统信息,某个关键的字,或者一些主字段) key : value
-
标识实体(落实需求字段,如设计用户表,里面有ID,name,text等对应的分别是什么)
-
标识实体之间的联系
- 写博客: user-->blog (要用到用户表和文章表)
- 创建分类: user-->category(要用到用户表和分类表)
- 关注: user-->user(要用到用户表和用户表)
- ..........
为什么要使用三大范式
为什么要数据规范化
为了防止以下情况
- 信息重复
- 更新异常
- 插入异常
- 无法正常显示信息
- 删除异常
- 丢失有效的信息
三大范式
第一范式(1NF)
原子性:每一列属性都是不可再分的属性值
第二范式(2NF)
总结:每张表只描述一件事
前提:满足第一范式
要求:每一行的数据只能与其中一列相关,即一行数据只做一件事。如果有联合主键,那必须和这几个主键都有关才行。
第三范式(3NF)
前提:满足第一第二范式
要求:表中的每一列只与主键直接相关而不是间接相关。(表中的每一列只能依赖于主键)
实际数据和三大范式的冲突(数据库的设计)
冲突原因:规范性和性能的问题
一般情况下关联查询的表不得超过三张表
- 考虑商业化的需求和目标。(成本,用户体验!) 数据库的性能更加重要。
- 在规范性能的问题的时候,需要适当的考虑一下 规范性!
- 故意给某些表增加一些冗余的字段。(从多表查询中变为单表查询)
- 故意增加一些计算列 (从大数据量降低为小数据量的查询: 索引)
JDBC相关
可以说是数据库万能驱动,有了这个才能操作数据库
数据库驱动
不同数据库有不同驱动,如MySQL和Oracle是不同驱动
JDBC
JDBC (Java Database Connectivity) API,即java数据库编程接口,是一组标准的Java语言中的接口和类,使用这些接口和类,Java客户端程序可以访问各种不同类型的数据库。比如建立数据库连接、执行SQL语句进行数据的存取操作。
即应用程序连接JDBC,JDBC去连接各个数据库的驱动执行操作
代码如下(记得往lib导入JDBC的jar包)
package jdbcTest;
import java.sql.*;
public class jdbcTest001 {
//总共可以分为六部
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1 加载JDBC驱动
Class.forName("com.mysql.jdbc.Driver");//固定写法
//2 设置参数,连接的数据库,用户名,密码
//支持中文编码 useUnicode=true 设置编码方式characterEncoding=utf8 使用安全的连接useSSL=true
String url = "jdbc:mysql://localhost:3306/testschool?useUnicode=true&characterEncoding=utf8&useSSL=true";
String userName = "xuanli";
String passWord = "xuanlichaobang";
//3 用这些参数去连接数据库
Connection connection = DriverManager.getConnection(url,userName,passWord);
//4 创建执行SQL的对象 Statement
Statement statement = connection.createStatement();
//5 写SQL并且执行获取结果
String sql = "SELECT * FROM testmd5";
//resultSet就是结果集
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()){
System.out.println(resultSet.getObject("id"));
}
//6 关闭连接
resultSet.close();
statement.close();
connection.close();
}
}
JDBD涉及对象详解
DriverManager
// 1 加载JDBC驱动
// 本来标准的是用下面注释掉的这句。但是Driver源代码本身就只有一个静态的代码块,所以没有必要
//DriverManager.registerDriver(new com.mysql.jdbc.Driver());
Class.forName("com.mysql.jdbc.Driver");//固定写法
// 2 connection其实就是代表数据库 所以可以执行事务等操作
Connection connection = DriverManager.getConnection(url,userName,passWord);
url
String url = "jdbc:mysql://localhost:3306/testschool?useUnicode=true&characterEncoding=utf8&useSSL=true";
//1 已有写法是右边 jdbc:mysql://localhost:3306/数据库名?参数1&参数2&参数3
//2 localhost是本地的,但是可能是远程的,端口号也不一定是3306 所以可以优化成以下 PS:mysql的端口号默认是3306
//jdbc:mysql://主机地址:端口号/数据库名?参数1&参数2&参数3
//oracle 端口号默认是1521 写法是 jdbc:oracle:thin:@localhost:1521:sid oracle全是表,没有数据库
String userName = "xuanli";
String passWord = "xuanlichaobang";
Statment 执行SQL的对象 是一个执行类。即执行sql相关操作(增删改查)
String sql = "SELECT * FROM testmd5";
statement.executeQuery(sql);//执行查询
statement.execute(sql);//执行任何SQL操作 所以效率低一点
statement.executeUpdate(sql);//更新,插入,删除 ps:即返回的是一个受影响的行数,无论是更新,插入,删除都是一个行 而且这里返回值就是int了
statement.executeBatch();//执行多个SQL,需要配合addBatch使用
ResultSet 查询的结果集:封装了所有的查询结果
resultSet.getObject();//在不知道结果类型的时候使用
//如果知道想要的类型就用下面的
resultSet.getInt();//返回值是int
resultSet.getBoolean();//返回值是布尔
resultSet.getBigDecimal();//返回值是BigDecimal
....//其他的返回值类似上面
释放资源
//6 释放连接 很耗费资源
resultSet.close();
statement.close();
connection.close();
代码实现完整的流程
-
首先要有一个db.properties文件放一些参数(如下)
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/testschool?useUnicode=true&characterEncoding=utf8&useSSL=true userName=xuanli passWord=xuanlichaobang
-
设置一个untils包,下面写一个jdbcUtils类(工具类),将连接JDBC的方法写到这个里面,实现高内聚低耦合,之后每次连接就不用再写连接的
package utils; import java.io.IOException; import java.io.InputStream; import java.sql.*; import java.util.Properties; //此类的作用是为了 public class jdbcUtils { private static String driver = null; private static String url = null; private static String userName = null; private static String passWord = null; static { try { //这里可以通过反射获得想要的参数或者类 即可以获得任何想要的资源,因为都在src目录下 InputStream in = jdbcUtils.class.getClassLoader().getResourceAsStream("db.properties"); Properties properties = new Properties(); properties.load(in); driver = properties.getProperty("driver"); url = properties.getProperty("url"); userName = properties.getProperty("userName"); passWord = properties.getProperty("passWord"); // 驱动只用加载一次 所以这里加载就行 Class.forName(driver); } catch (Exception e) { e.printStackTrace(); } } //获取连接 public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, userName, passWord); } //释放资源 public static void release(Connection connection, Statement statement,ResultSet resultSet) { if(resultSet!=null){ try { resultSet.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if(statement!=null){ try { statement.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if(connection!=null){ try { connection.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } } }
-
最后就是写一个方法测试能不能成功
package jdbcTest; import utils.jdbcUtils; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class jdbcTest002Insert { public static void main(String[] args) { //放在外面是因为这几个资源无论如何都是要释放的,但是如果放在try里面可能就没办法释放了 Connection connection = null; Statement statement = null; ResultSet resultSet = null; try { connection = jdbcUtils.getConnection(); statement = connection.createStatement(); String sql = "INSERT INTO testmd5 (id,`name`,`pwd`) VALUES(4,'axu','SADDADSFA')"; //是insert所以不用接收结果集 只用判断结果是不是大于0就行 int i = statement.executeUpdate(sql); if(i>0){ System.out.println("插入成功"); } } catch (SQLException throwables) { throwables.printStackTrace(); }finally { jdbcUtils.release(connection,statement,resultSet); } } }
SQL注入问题
将Statement换成PreparedStatement可以解决,之后详解
概念:SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。
举例:
SELECT * FROM user WHERE `User` = '' or '1=1'
-- 这句可以查出所有的user表的信息,因为有or存在,or后面的条件必定满足,同理,如果在代码中需要输入类似参数的时候输入这种的话也会被渗透并且实现非法操作
PreparedStatement对象
PreparedStatement可以防止注入问题
用上面JDBC中的类似方法试验
package jdbcTest;
import utils.jdbcUtils;
import java.sql.*;
public class jdbcTest003PreparedStatement {
public static void main(String[] args) {
//放在外面是因为这几个资源无论如何都是要释放的,但是如果放在try里面可能就没办法释放了
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
connection = jdbcUtils.getConnection();
// 这里就是重点,首先使用占位符?代替参数
// 或者说这里就是防止SQL注入的本质,将传入的参数当做字符
// 这样当参数中有存在转义字符的时候,比如单引号会被直接转义
String sql = "INSERT INTO testmd5 (id,`name`,`pwd`) VALUES(?,?,?)";
// 这里先预编译SQL但是不执行
statement = connection.prepareStatement(sql);
// 然后继续使用PreparedStatement自带的方法输入参数
// 都是statement.xxx 此参数都是两个,第一个是sql的第几个参数,第二个是sql参数的具体值
statement.setInt(1,9);
statement.setString(2,"axu");
statement.setString(3,"awsdadsadadwasad");
// 执行SQL 这里的executeUpdate就不需要参数了
int i = statement.executeUpdate();
// // 是insert所以不用接收结果集 只用判断结果是不是大于0就行
// int i = statement.executeUpdate(sql);
if(i>0){
System.out.println("插入成功");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
jdbcUtils.release(connection,statement,resultSet);
}
}
}
使用JDBC连接数据库
-
第一步,连接上MySQL,如图
-
第二步,选中自己想要操作的数据表
-
第三步,操作表格注意事项
更改数据前,有个箭头是灰色的,如下
更改数据后,这个箭头是绿色的,点击提交后,数据才能正式更改
java实现事务
只需要加两行代码,如下
//增加的第一行代码 关闭自动提交。自动会开启事务
connection.setAutoCommit(false);
statement = connection.createStatement();
String sql = "INSERT INTO testmd5 (id,`name`,`pwd`) VALUES(5,'axu','SADDADSFA')";
int i = statement.executeUpdate(sql);
//增加的第二行代码 业务完毕,提交事务
connection.commit();
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· winform 绘制太阳,地球,月球 运作规律
· 上周热点回顾(3.3-3.9)