12月15日——培训第22天

本文探讨了数据建模中的复杂关系处理,包括多向联系、继承关系的实现方法及实体间的约束建模。通过具体案例说明如何在PowerDesigner中建立多对多关系,并讨论了实体与关系结点的应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 今天继续讲数据建模,主要是关系这里,因为关系这部分在建模中是比较复杂的。

关系的多向性:
联系有时不仅局限于两个实体之间,有可能涉及到更多的实体,这就构成了多向联系。

选课关系中,老师、学生、课程三个实体,
学生选课是多对多关系,
老师开课是多对多关系,

多对多关系必须用关系表来实现,关系表里面放什么能够把学生选课的信息保存下来呢?
关系表里可以存三个实体的对应主键。

在powerdesigner中如果要建立上面的关系的话首先建立好学生课程和老师三个实体

学生:id和name
老师:id和name
课程:id和name

然后创建一个association,然后再用association_link来将各个实体引向这个association,
注意这个association有个属性是成绩!

然后再生成物理模型可以得到关系表和各个实体表

当然上面这种用一个关系表存三个实体的主键的方式不是太合适,从设计角度来说虽然看着明了,但是
假如老师开了门课,但是没有人选,由于关系表中三个主键缺一不可,所以如果没有学生选课的话,
老师这门课根本就开不出来,所以实际应用中用这种方式不好

----------------------------
所以可以先让老师和课程之间产生一个关系,然后让学生和这个关系产生一个关系,
这种情况下需要再产生一个实体,才有可能和学生建立起关系。

建立一个关系,让其作为一个实体出现,也就是建立一个实体,让它和课程以及老师之间的关系均是多对一的关系,
再让学生和这个实体建立一个多对多的关系


小总结:
什么时候使用关系结点:
1 如果关系具有属性的话,则必须使用关系结点,当然也可以使用实体来代替关系,但是应该优先使用关系结点,除非这个关系结点
  还要和其他实体去建立关系。
2 如果需求中明确要求以关系表的形式实现关系的时候,需要使用关系结点
3 具有多向性的关系,即参与关系的实体是多个的时候,必须要使用关系结点

什么时候要用实体来代替关系呢?
1 关系要和其他的实体发生关系的时候,需要用实体来代替关系,因为关系与关系不能再发生关系的!
----------------------
练习:
该工厂需要管理的实体有:职工、部门、项目、零件、供应商、仓库。

关系如下:
1 一个职工只能在一个部门工作,一个部门有多个职工
2 一个职工可以参加一个以上的项目,每个项目可能有多个职工参加
3 每个项目确定一个负责人,一个人可以负责多个项目
4 一个供应商可以为多个项目供应零件,每个项目允许从不同的供应商那里采购零件,
  每个项目需要多种零件
5 一种零件可以由其他几种零件组装而成
6 一个仓库可以放多种零件,一种零件可以存放在不同的仓库里面

---------------------------------------------------------

中午领取了一本书名字叫做Spring技术手册,由于上一次张老师白送的那本书自己已经有了,
所以这次这本书不用掏钱买,白拿就可以了。

380多页的书,竟然48元钱,真够可以的……

-------------------------------
下午netmeeting拨不上去了,……看来如果最后一个拨的话几乎不可能拨的上去………………

算了先忍着吧,等下课再说。


数据库设计的基本原则:
真实性、避免冗余、简单性等,在设计数据库的过程中,一定要设法避免引入过多的元素,尽量做到简单明了!


下午第一节课一个ip一个ip的试,都没拨上去,但是课间的时候终于进去了……真不知道怎么搞的,以后上netmeeting
的时候可不能被弄到最后一个拨啊!一定要尽早的拨,越早越好啊!!

------------------------------------

子类表示方法:
在关系型数据库中,实现父子关系有多种办法

比如课程和实习课的关系,课程和认证课的关系,都属于父子类的关系

就建立一张表可否表示出这种关系?

可以建立一个实体,实体里面有课程id,课程名称,实习课程名称,认证课程名称以及课程类型。
用课程类型这个识别字段来完成父类和子类的映射关系。

上面这种方式不好,会有许多字段为空,浪费存储空间很厉害

换一种方式吧:
课程名字和课程id是共有的,也就是属于父类实体中,那么再建立一个表,里面有id和实习课程名称
还有一个认证表,里面有id和认证课程名称。

让父表的主键和子表的主键建立起一种关系。

courses表中有course_id 和course_name

practice_course:course_id和project_name
certification_courses:course_id和cert_name

比如course_id为1、2、3、4的是实习课程,那么practice_course中的主键也是对应1、2、3、4
然后5、6、7的是认证课程,那么course课程中的course_id里面有5、6、7,而且同时,certification_courses
里面的主键是从5开始,是5、6、7且对应的是认证课程。

也就是说,practise_course和certification_courses这两个表的主键由于分别和courses的主键建立了外键关系,
因为courses的主键是唯一的,所以呢,practice_course和certification_course这两个表中的主键是绝对不可能
重复的,比如不可能出现第一个表中有一个主键为13,而且第二个表中也有一个主键值为13,这是绝对不允许的,也就是
说,子表中的主键是不可以设为自增的!

继承关系的三种实现:
一张表对应整个类层次:无论有多少个类层次,所有信息都存储在一张表中,由表的一个字段来识别当前的记录是哪种类型。

一张表对应一个类(存在连接表的问题):父类和子类都对应着相应的表,父类存储共有信息,子类存储特有信息

一张表对应一个具体类,只要不是抽象类就对应一张具体的表,共有信息和特有信息同时存储在这个类所对应的表中

一般优先使用第三种,然后考虑第二种

----------------------------------------

约束建模
主键约束:能唯一标识一条记录的就是主键,一个类中的任何两个对象在构成主键的属性集上的取值绝对不可以相同,
否则主键就不可以唯一的标识一个对象

主键如果有逻辑含义的话,是不好的,但是实际开发中很多主键都有逻辑含义,比如学生的学号一般都有
前两个字母表明是硕士还是博士之类,身份证也可以说是有逻辑含义的主键。一旦以后逻辑含义变了,比如
由硕士变成了博士,这就会发生主键的变化问题,主键一旦变化了,和它关联的那些外键也要跟着变化……

单值约束:也叫作唯一性约束。

完整性约束:对属性,要求有非空的单值(not null约束)
    对联系,要求引用的对象存在且唯一。

 一个实体的主键属性值不能为空,成为实体完整性;
 一个实体的某个属性值只能引用另一个实体确实存在的主键属性值,成为引用完整性。

域约束:也就是检查约束,例如要求成绩在1到100之间。
-------------------------------
我们主要设计实体,实体间关系,建立实体的约束


用例:use case

标题:学生选课用例
1、学生登陆进入系统,系统提示登陆结果;
2、学生查看已开的课程,系统显式课程;
3、学生选择课程,系统列出该门课程所有的主讲老师
4、学生选择主讲老师,系统提示确认;
5、学生提交选课结果,系统保存并提示成功。

老师开课用例

老师分班用例

提交文档:
1、CDM(Conceptual Data Model) :学生、课程、老师、班级,要包含上面三个用例所需的所有的实体
2、程序源代码。

学生登陆,提示用户名、密码输入;
登陆进去后,显示出所有的课程;(select * from course)

选择课程后,显示出这门课程的老师,根据course_id去找老师的id,找到老师的id后再去找老师的姓名


最近比较的混乱,日记好像也有点马马虎虎的
老师下午布置的那个选课练习没有写完,先附上我的部分源代码如下,免得丢了


package classSelection;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

 

public class Job {

 static Connection conn = null ;
 static PreparedStatement pstmt = null ;
 static ResultSet rs = null ;
 
 public static void ptr(String str)
 {
  System.out.println(str);
 }
 //获取连接
 public static Connection getConnection()
 {
  Connection conn = null ;
  try
  {
   Class.forName("com.mysql.jdbc.Driver");
   conn = DriverManager.getConnection
   ("jdbc:mysql://localhost:3306/j2ee","root","root");
  }
  catch(ClassNotFoundException ex)
  {
   ex.printStackTrace();
   ptr("类加载失败");
  }
  catch(SQLException ex)
  {
   ex.printStackTrace();
   ptr("获得连接失败");
  }
  
  return conn ;
 }
 
 //关闭连接
 public static void closeConnection(Connection conn)
 {
  try
  {
   if(conn!=null && !conn.isClosed())
   {
    conn.close();
    conn = null ;
   }
  }
  catch(SQLException ex)
  {
   ex.printStackTrace();
   ptr("试图关闭连接时出现了异常");
  }
 }
 
 //关闭语句
 public static void closePreparedStatement(PreparedStatement pstmt)
 {
  try
  {
   if(pstmt!=null)
   {
    pstmt.close();
    pstmt = null ;
   }
  }
  catch(SQLException ex)
  {
   ex.printStackTrace();
   ptr("试图关闭语句时出现了异常");
  }
 }
 
 //关闭结果集
 public static void closeResultSet(ResultSet rs)
 {
  try
  {
   if(rs!=null)
   {
    rs.close();
    rs = null ;
   }
  }
  catch(SQLException ex)
  {
   ex.printStackTrace();
   ptr("试图关闭语句时出现了异常");
  }
 }
 //读取一行
 public static String readLine()
 {
  try {
   return new BufferedReader(new InputStreamReader(System.in)).readLine();
  } catch (IOException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
   return "";
  }
 }
 
 
//***************************************************************************************
 //对老师和学生进行注册操作,传入"1"则对教师进行注册,传入参数为"2"则对学生进行注册
 public static void registerUser(String flag)
 {
  ptr("******************************************");
  ptr("请您输入用户名:");
  String username = readLine();
  ptr("请您输入密码:");
  String password = readLine();
  
  try
  {
   String tea_sql = "insert into teachers (tea_username,tea_password)" +
      "values(rtrim(?),rtrim(?))";
   String stu_sql = "insert into students (stu_username,stu_password)" +
      "values(rtrim(?),rtrim(?))";
   
   conn = getConnection();
   if("1".equals(flag))
   {
    pstmt = conn.prepareStatement(tea_sql);
    pstmt.executeUpdate();
    ptr("已经成功的注册了您的信息!");    
   }
   else if("2".equals(flag))
   {
    pstmt = conn.prepareStatement(stu_sql);
    pstmt.executeUpdate();
    ptr("已经成功的注册了您的信息!");
   }
   else
   {
    ptr("在注册过程中出现了严重的错误!程序结束");
    System.exit(1);
   }

  }
  catch (Exception e)
  {   
   e.printStackTrace();
   ptr("注意:您在注册时出现了异常!");   
  }
  finally
  {
   closePreparedStatement(pstmt);
   closeConnection(conn);
  }
  ptr("注册成功!接下来将直接以您的这个身份进行对应的操作");
  
  if("1".equals(flag))
   doTeacher();
  else if("2".equals(flag))
   doStudent();  
  
 }
 public static void register()
 {  
  ptr("******************************************");
  ptr("请您选择注册用户的类型");
  ptr("");
  ptr("1 教师");
  ptr("2 学生");
  
  String flag = readLine();
  
  if("1".equals(flag)||"2".equals(flag))
  {
   registerUser(flag);
  }
  else
  {
   ptr("您的输入错误,程序结束!!");
   System.exit(1);
  }
 }
//****************************************************************************************
 


//登陆及相应的操作
//****************************************************************************************
 public static void loginUser(String flag)
 {
  
 }
 
 public static void login()
 {
  ptr("******************************************");
  ptr("请您选择登陆用户的类型");
  ptr("");
  ptr("1 教师");
  ptr("2 学生");
  
  String flag = readLine();
  
  if("1".equals(flag)||"2".equals(flag))
  {
   loginUser(flag);
  }
  else
  {
   ptr("您的输入错误,程序结束!!");
   System.exit(1);
  }
 }
//****************************************************************************************
 

 
 public static void doStudent()
 {
  
 }
 
 public static void doTeacher()
 {
  
 }

 public static void main(String[] args) {
  
  ptr("******************************************");
  ptr("请您选择程序对应的功能……");
  ptr("");
  ptr("1 注册");
  ptr("2 登陆");
  String flag = readLine();
  if("1".equals(flag))
  {
   register();
  }
  else if("2".equals(flag))
  {
   login();
  }
  else
  {
   ptr("您的输入错误,程序结束!!");
   System.exit(1);
  }
  
 }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值