Mysql笔记05:库表设计(API变形)

库表设计步骤:

1.整理需求
2.E-R 图(实体关系图)做辅助

E(entry)实体,设计实体就像定义一个类一样,指定从哪些方面描述对象,一个实体转换为数据库中的一个表。
R(relationship)关系,关系描述两个实体之间的对应规则,关系的类型包括包括一对一、一对多、多对多。

在这里插入图片描述
参考博客:E-R图

关系也是一种数据,需要通过一个字段存储在表中:
1) 实体A对实体B为1对1,则在表A或表B中创建一个字段,存储另一个表的主键值。
2) 实体A对实体B为1对多:在表B中创建一个字段,存储表A的主键值。
3) 实体A对实体B为多对多:新建一张表C,这个表只有两个字段,一个用于存储A的主键值,一个用于存储B的主键值。

参考博客:多对多处理

3.根据需求填写字段(字段类型、约束关系)
4.数据库设计的三大范式(减少数据存储的冗余度)

a) 表中的每个字段都是原子的,即每个字段不可再分。
字段是否可以再分
表:【联系人】(姓名,性别,电话) 如果在实际场景中,一个联系人有家庭电话和公司电话,那么这种表结构设计就没有达到 1NF。要符合 1NF 我们只需把列(电话)拆分,即:【联系人】(姓名,性别,家庭电话,公司电话)。1NF 很好辨别,但是 2NF 和 3NF 就容易搞混淆。

b) 有主键,每个字段应该是和主键有完整的依赖关系(没有包含在主键中的列必须完全依赖于主键,而不能只依赖于主键的一部分)
表是否可以再分

订单明细表:【OrderDetail】(OrderID,ProductID,UnitPrice,Discount,Quantity,ProductName)。 因为我们知道在一个订单中可以订购多种产品,所以单单一个 OrderID 是不足以成为主键的,主键应该是(OrderID,ProductID)。显而易见 Discount(折扣),Quantity(数量)完全依赖(取决)于主键(OderID,ProductID),而 UnitPrice,ProductName 只依赖于 ProductID。所以 OrderDetail 表不符合 2NF。不符合 2NF 的设计容易产生冗余数据。可以把【OrderDetail】表拆分为【OrderDetail】(OrderID,ProductID,Discount,Quantity)和【Product】(ProductID,UnitPrice,ProductName)来消除原订单表中UnitPrice,ProductName多次重复的情况。

c) 有主键,每个字段应该是和主键呈现直接关系,而不是间接关系。 即不能存在:非主键列 A 依赖于非主键列 B,非主键列 B 依赖于主键的情况。
表是否可以再分 (和第二范式比较类似,只不过第三范式是指对非主键的依赖,第二范式是指对主键(多个主键)的部分依赖)
一个订单表【Order】(OrderID,OrderDate,CustomerID,CustomerName,CustomerAddr,CustomerCity)主键是(OrderID)。 其中 OrderDate,CustomerID,CustomerName,CustomerAddr,CustomerCity 等非主键列都完全依赖于主键(OrderID),所以符合 2NF。不过问题是 CustomerName,CustomerAddr,CustomerCity 直接依赖的是 CustomerID(非主键列),而不是直接依赖于主键,它是通过传递才依赖于主键,所以不符合 3NF。 通过拆分【Order】为【Order】(OrderID,OrderDate,CustomerID)和【Customer】(CustomerID,CustomerName,CustomerAddr,CustomerCity)从而达到 3NF。

注意:

  • 第二范式(2NF)和第三范式(3NF)的概念很容易混淆,区分它们的关键点在于,2NF:非主键列是否完全依赖于主键,还是依赖于主键的一部分;3NF:非主键列是直接依赖于主键,还是直接依赖于非主键列。
  • 有时候为了避免关联查询,可以违反第三范式
5. 生成建表语句

JDBC API 变形

1 Statement 变成 PrepareStatement(提高运行效率,防止SQL注入)

SQL 注入:用户输入特殊字符导致SQL不是按照作者期望的方式运行:
比如输入用户名: ‘ OR 1 = 1 OR ‘。

使用PrepareStatement防止sql注入:

String sql = "SELECT id, username FROM users WHERE username = ? AND password = ?";
try (PreparedStatement statement = con.prepareStatement(sql)) {
    下标从 1 开始
    statement.setString(1, username);
    statement.setString(2, password);
    
    MySQL Driver 打印 SQL 语句的小技巧
    com.mysql.jdbc.PreparedStatement mysqlStatement = (com.mysql.jdbc.PreparedStatement) statement;
    System.out.println(mysqlStatement.asSql());

    try (ResultSet resultSet = statement.executeQuery()) {
        if (!resultSet.next()) {
            System.out.println("登录失败");
        } else {
            int id = resultSet.getInt("id");
            String usernameInTable = resultSet.getString("username");
            System.out.println("登录成功: " + id + ", " + usernameInTable);
        }
    }
}
2 获取连接的方式

DriverManager.getConnection();弊端: 每次获取连接,都是一次网络请求,用完之后马上销毁,导致效率很低

DataSource:使用方式简单,可以带有连接池功能(建议但不强制)

连接池: 池化技术,提升效率

3 获取插入的自增 id
PreparedStatement statement = 
con.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS)

ResultSet rs = statement.getGeneratedKeys()
rs.next();
rs.getInt(1);

0

4 总结(DataSource + PrepareStatement)
String sql = "SELECT id, username, password FROM users LIMIT ?";
MysqlDataSource mysqlDataSource = new MysqlDataSource();
mysqlDataSource.setServerName("127.0.0.1");
mysqlDataSource.setPort(3306);
mysqlDataSource.setUser("root");
mysqlDataSource.setPassword("");
mysqlDataSource.setDatabaseName("java20_0211");
mysqlDataSource.setUseSSL(false);
mysqlDataSource.setCharacterEncoding("utf8");
DataSource dataSource = mysqlDataSource;

try (Connection con = dataSource.getConnection()) {
    try (PreparedStatement statement = con.prepareStatement(sql)) {
        statement.setInt(1, 3);
        try (ResultSet rs = statement.executeQuery()) {
            while (rs.next()) {
                System.out.println(rs.getInt("id"));
                System.out.println(rs.getString("username"));
                System.out.println(rs.getString("password"));
            }
        }
    }
}

注意
Try-with-resource: 带着 Closable 的类都可以用

  • 对象的创建
  • 对象的使用
  • 对象的关闭 close()

用于: Connection、Statement、PrepareStatement、ResultSet
InputStream、OutputStream

Try (对象的创建) {
对象的使用
} // 隐含着 对象的关闭,自动执行 对象.close()

小结
1. 库表设计(重点) 需求分析 -> E-R 辅助 -> 三大范式

2. JDBC 的使用套路

a)注册 Driver
b)获取 Connection
c)获取 Statement
d)执行 SQL
e)关闭资源

3. JDBC 的变形

a)Try-with-resource: 代码简单点
b)Statement 换成了 PrepareStatement
i.前置知识:SQL 注入
ii.预编译,速度快;防止 SQL 注入;代码简单点
c)DriverManager 换成了 DataSource
i.可以出现连接池
ii.代码简单点
d)INSERT 之后获取自增的 id

4. 复杂查询

a)聚合查询
i.GROUP BY
ii.HAVING
b)联表查询(关联查询)
i.笛卡尔积/结果的筛选

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值