项目开发中的一些注意事项以及技巧总结 基于Repository模式设计项目架构—你可以参考的项目架构设计 Asp.Net Core中使用RSA加密 EF Core中的多对多映射如何实现? asp.net core下的如何给网站做安全设置 获取服务端https证书 Js异常捕获

项目开发中的一些注意事项以及技巧总结

 

1、jquery采用ajax向后端请求时,MVC框架并不能返回View的数据,也就是一般我们使用View()、PartialView()等,只能返回json以及content等,但是一般我们在开发的时候也是使用json返回的,此时如果需要渲染界面或者是加载局部视图,我们可以在ajax的success的事件中使用$.html()来渲染后台给前端传的View()数据。一开始我遇到这个问题的时候还很纳闷,为什么ajax给后端穿了POST之后,不能加载局部页面,后来一想,是不是因为ajax是异步请求的原因,于是我把ajax的同步开关打开了,同时将后台传输的视图数据直接使用$html()就渲染到了前端页面。具体请参考:https://blog.csdn.net/m0_37302219/article/details/78272081

2 、后端MVC架构,一般前端都是使用Razor视图,使用@来加载后台传过来的数据比如ViewData,以及ViewBag等。此时,我建议在后端的C#中,我们给ViewBag赋值了之后,在前端的cshtml页面中这么做:
@{
List<T> xxx = ViewBag.xxx;
T xxx = ViewBag.xxx;
}
然后直接使用显式类型的变量来做处理。

3、EF Core中,尤其要注意多对多的关联属性的使用。如果使用不当就会加载不出来数据,直接会出一个null值,在这里请参考
https://blog.oneunicorn.com/2017/09/25/many-to-many-relationships-in-ef-core-2-0-part-1-the-basics/
认真的跟着这个博客做一遍就可以理解了。

4、EF Core中,当我们使用code First建模方式生成数据库时,如果生成数据库有问题了(当然一般是没有问题的,但是我就遇到了,可能也是我操作不当)我强烈建议,不要删除这个生成有问题的数据库,千万不要觉得删除了之后,可以再生成一遍,会出问题的。具体请看
http://www.cjjjs.com/paper/lkkj/2018711212518513.html

5、EF Core下一对多的关联中,需要注意(我不怎么用过EF,也不知道在EF中是不是很正常,在这里仅仅说EF Core,可能两者在我要说的这点上是一致的)。我们以官方为例:https://docs.microsoft.com/en-us/ef/core/get-started/aspnetcore/existing-db
一个博客(Blog)对应多个文章(Post),一篇文章对应一个博客(Blog)。此时,当我们使用上下文来获取数据库数据时,代码如下:
List<Blog> Blogs = _context.Blogs.ToList(); 
List<Post> Posts = _context.Posts.ToList(); // 如果不写这句代码,不从数据库里面加载处理关联属性Post的数据,那么Blogs里面的Post关联属性也将会是null值,这点是要特别注意的,只有写了这句代码,Blogs类型的关联属性Post才能有数据。

6、项目中一定要设计好数据库,一般来讲如果有级联关系的,就设置为两个表(这里的级联关系我是这么定义的:假设一个类别下有多条数据,这就是一个级联关系,就像是一个省份下面有多个城市)。在实体方面,我们可以认为是一对多关系,如果是一对多关系,我们就建两个表。设计好了数据表,对我们的开发有着莫大的好处,有时候多加一个字段和少一个字段完全不一样。

7、建议在列表数据中使用ForEach来遍历,也就是一般的List<T>变量,使用List<T>.ForEach来遍历数据处理逻辑

 

基于Repository模式设计项目架构—你可以参考的项目架构设计

 

关于Repository模式,直接百度查就可以了,其来源是《企业应用架构模式》。
我们新建一个Infrastructure文件夹,这里就是基础设施部分,EF Core的上下文类以及Repository层都放在这里面。
新建一个IReposotory的接口,其内容就是封装了基本的CRUD:

复制代码
public interface IRepository<TEntity> where TEntity : class 
 { 
    ///获取当前实体的查询数据集 
    IQueryable<TEntity> Entities{get;} 
 
    ///获取当前实体的数据集 
    DbSet<TEntity> DbEntities{get;} 
 
 /// <summary> 
 /// Gets all objects from database 
 /// </summary> 
 /// <returns></returns> 
 IQueryable<TEntity> All(); 
 
 /// <summary> 
 /// Gets objects from database by filter. 
 /// </summary> 
 /// <param name="predicate">Specified a filter</param> 
 /// <returns></returns> 
 IQueryable<TEntity> Filter(Expression<Func<TEntity, bool>> predicate); 
 
 /// <summary> 
 /// Gets objects from database with filting and paging. 
 /// </summary> 
 /// <param name="filter">Specified a filter</param> 
 /// <param name="total">Returns the total records count of the filter.</param> 
 /// <param name="index">Specified the page index.</param> 
 /// <param name="size">Specified the page size</param> 
 /// <returns></returns> 
 IQueryable<TEntity> Filter(Expression<Func<TEntity, bool>> filter, out int total, int index = 0, int size = 50); 
 
 /// <summary> 
 /// Gets the object(s) is exists in database by specified filter. 
 /// </summary> 
 /// <param name="predicate">Specified the filter expression</param> 
 /// <returns></returns> 
 bool Contains(Expression<Func<TEntity, bool>> predicate); 
 
 /// <summary> 
 /// Find object by keys. 
 /// </summary> 
 /// <param name="keys">Specified the search keys.</param> 
 /// <returns></returns> 
 TEntity Find(params object[] keys); 
 
 /// <summary> 
 /// Find object by specified expression. 
 /// </summary> 
 /// <param name="predicate"></param> 
 /// <returns></returns> 
 TEntity Find(Expression<Func<TEntity, bool>> predicate); 
 
 /// <summary> 
 /// Create a new object to database. 
 /// </summary> 
 /// <param name="t">Specified a new object to create.</param> 
 /// <returns></returns> 
 int Create(TEntity t); 
 
 /// <summary> 
 /// Delete the object from database. 
 /// </summary> 
 /// <param name="t">Specified a existing object to delete.</param> 
 void Delete(TEntity t); 
 
 /// <summary> 
 /// Delete objects from database by specified filter expression. 
 /// </summary> 
 /// <param name="predicate"></param> 
 /// <returns></returns> 
 int Delete(Expression<Func<TEntity, bool>> predicate); 
 
 /// <summary> 
 /// Update object changes and save to database. 
 /// </summary> 
 /// <param name="t">Specified the object to save.</param> 
 /// <returns></returns> 
 int Update(TEntity t); 
 
 /// <summary> 
 /// Select Single Item by specified expression. 
 /// </summary> 
 /// <typeparam name="T"></typeparam> 
 /// <param name="expression"></param> 
 /// <returns></returns> 
 TEntity FirstOrDefault(Expression<Func<TEntity, bool>> expression); 
 }
复制代码

创建一个基类,用来实现IRepository接口,同时作其余的Repository的基类

复制代码
public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class 
 { 
 protected readonly DbContext Context; 
 
 public BaseRepository(DbContext context) 
 { 
 Context = context; 
 } 
 
/// 获取当前实体的查询数据集 
public IQueryable<TEntity> Entities 
{ 
get { return Context.Set<TEntity>().AsQueryable(); } 
} 
 
/// 获取当前实体 
public IQueryable<TEntity> Entities 
{ 
get { return Context.Set<TEntity>(); } 
} 
 
 public TEntity FirstOrDefault(Expression<Func<TEntity, bool>> expression) 
 { 
 return All().FirstOrDefault(expression); 
 } 
 
 public IQueryable<TEntity> All() 
 { 
 return Context.Set<TEntity>().AsQueryable(); 
 } 
 
 public virtual IQueryable<TEntity> Filter(Expression<Func<TEntity, bool>> predicate) 
 { 
 return Context.Set<TEntity>().Where<TEntity>(predicate).AsQueryable<TEntity>(); 
 } 
 
 public virtual IQueryable<TEntity> Filter(Expression<Func<TEntity, bool>> filter, out int total, int index = 0, 
 int size = 50) 
 { 
 var skipCount = index * size; 
 var resetSet = filter != null 
 ? Context.Set<TEntity>().Where<TEntity>(filter).AsQueryable() 
 : Context.Set<TEntity>().AsQueryable(); 
 resetSet = skipCount == 0 ? resetSet.Take(size) : resetSet.Skip(skipCount).Take(size); 
 total = resetSet.Count(); 
 return resetSet.AsQueryable(); 
 } 
 
 public virtual int Create(TEntity TObject) 
 { 
 Entities.Add(TObject); 
 Context.SaveChanges(); 
 } 
 
 public virtual int Delete(TEntity TObject) 
 { 
 Entities.Remove(TObject);. 
 Context.SaveChanges(); 
 } 
 
 public virtual void Update(TEntity TObject) 
 { 
 try 
 { 
 var entry = Context.Entry(TObject); 
 Context.Set<TEntity>().Attach(TObject); 
 entry.State = EntityState.Modified; 
 } 
 catch (OptimisticConcurrencyException ex) 
 { 
 throw ex; 
 } 
 } 
 
 public virtual int Delete(Expression<Func<TEntity, bool>> predicate) 
 { 
 var objects = Filter(predicate); 
 foreach (var obj in objects) 
 Context.Set<TEntity>().Remove(obj); 
 return Context.SaveChanges(); 
 } 
 
 public bool Contains(Expression<Func<TEntity, bool>> predicate) 
 { 
 return Context.Set<TEntity>().Any(predicate); 
 } 
 
 public virtual TEntity Find(params object[] keys) 
 { 
 return Context.Set<TEntity>().Find(keys); 
 } 
 
 public virtual TEntity Find(Expression<Func<TEntity, bool>> predicate) 
 { 
 return Context.Set<TEntity>().FirstOrDefault<TEntity>(predicate); 
 } 
 }
复制代码

新建一个实体类的接口:

public interface IStudentRepository : IRepository<Student> 
 { 
 int AddStudent(Student student); 
 }

然后我们创建一个实体类的Repository:

复制代码
public class StudentRepository : BaseRepository<Student>, IStudentRepository 
 { 
 private readonly SchoolContext _context; 
 
 public StudentRepository(SchoolContext context) 
 : base(context) 
 { 
 _context = context; 
 } 
 
int AddStudent(Student student) 
{ 
_context.Create(student); 
} 
 }
复制代码

在这里就已经做好了我们要做的了。接下来的就是注入依赖、在控制器里面的使用了。

我们完全可以自己来定制自己的Repository模式下的项目。其实整个的架构没有什么,我们只是将所有的CRUD操作封装到了IRepository接口里面,然后在BaseRepository中实现了一遍,而且如果你细心的话,你会发现IRepository里面的CRUD操作都是基于已有的扩展方法里面的,就是linq扩展方法的Add等源码,同时我们在BaseRepository类中,提供了DbSet<TEntity>属性以及查询数据集IQueryable<DbSet<TEntity>>,这也是有必要的,可以省却我们很多不必要的代码,因为我们所有的CRUD都是基于这两个的。然后我们基于BaseRepository来实现实体类的Repository,同时继承按需增加的IEntityRepository接口。但是在这里要注意,我们将DbContext的子类都放在了Infrastructure文件夹里面,是因为,一般我们继承自DbContext的子类都是操作数据库的中间类,属于基础设施一块,所以将其放在Infrastructure文件夹比较合适。

参考资料:
MVC实用架构设计(三)——EF-Code First(1):Repository,UnitOfWork,DbContext

分享基于Entity Framework的Repository模式设计(附源码)

《ASP.NET MVC框架揭秘》源码中的示例项目源码 S1402

 

 

EF Core中的多对多映射如何实现?

 

EF 6.X中的多对多映射是直接使用HasMany-HasMany来做的。但是到了EF Core中,不再直接支持这种方式了,可以是可以使用,但是不推荐,具体使用可以参考《你必须掌握的EntityFramework 6.X与Core 2.0》一文。在这里我就详细的说下如何在EF core下实现。
首先就是实体类的建立:

复制代码
public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }

    public ICollection<PostTag> PostTags { get; } = new List<PostTag>();
}

public class Tag
{
    public int TagId { get; set; }
    public string Text { get; set; }

    public ICollection<PostTag> PostTags { get; } = new List<PostTag>();
}

public class PostTag
{
    public int PostId { get; set; }
    public Post Post { get; set; }

    public int TagId { get; set; }
    public Tag Tag { get; set; }
}
复制代码

接下来就是映射了。派生自DbContext的上下文类:

复制代码
public class MyContext : DbContext
{
    public DbSet<Post> Posts { get; set; }
    public DbSet<Tag> Tags { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder.UseSqlServer(
            @"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0");

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<PostTag>().ToTable("PostTags");
        modelBuilder.Entity<PostTag>()
            .HasKey(t => new { t.PostId, t.TagId });
    }
}
复制代码

这样就完成了我们的多对多映射了。我们只是通过多建立了一个表,将两个实体类的Id作为联合主键。

在Identity框架中,如果你细心点,你会发现有个userroles表,这个表是就是用来做Users表和Roles表的映射的。那么接下来我们只要新建一个实体类,随后在上下文类中映射到表:

modelBuilder.Entity<UserRoles>.ToTable("userroles");

这样就可以了。然后我们就可以很方便的给用户添加角色了。

参考链接:https://blog.oneunicorn.com/2017/09/25/many-to-many-relationships-in-ef-core-2-0-part-1-the-basics/

 

asp.net core下的如何给网站做安全设置

 

首先,我们来看下stack overflow网站的请求头文件:

可以看到一些我们熟悉或是陌生的HTTP头部文件字段。
在这里我们在对HTTP输入流的头部文件中,做一些基本的防护。首先要明确,既然我们是对HTTP头部做处理,那么就需要在Startup.cs类的 
Configuration方法中做处理,因为这里就是处理HTTP输入流的。

首先做一些基本的处理,比如中间件和基本的类:

1
2
3
4
5
6
7
8
public class SecurityHeadersPolicy 
{
    public IDictionary<stringstring> SetHeaders { get; }
         new Dictionary<stringstring>();
 
    public ISet<string> RemoveHeaders { get; }
        new HashSet<string>();
}

这里的头部信息是我们定义好的,用来增加或是删除头部信息,然后就是我们的中间件:

复制代码
public class SecurityHeadersMiddleware  
{
    private readonly RequestDelegate _next;
    private readonly SecurityHeadersPolicy _policy;

    public SecurityHeadersMiddleware(RequestDelegate next, SecurityHeadersPolicy policy)
    {
        _next = next;
        _policy = policy;
    }

    public async Task Invoke(HttpContext context)
    {        
        IHeaderDictionary headers = context.Response.Headers;

        foreach (var headerValuePair in _policy.SetHeaders)
        {
            headers[headerValuePair.Key] = headerValuePair.Value;
        }

        foreach (var header in _policy.RemoveHeaders)
        {
            headers.Remove(header);
        }

       await _next(context);
    }
}
复制代码

基于IApplicationBuilder接口做一个中间件的扩展方法:

复制代码
public static class MiddlewareExtensions  
{
    public static IApplicationBuilder UseSecurityHeadersMiddleware(this IApplicationBuilder app, SecurityHeadersBuilder builder)
    {
        SecurityHeaderPolicy policy = builder.Build();
        return app.UseMiddleware<SecurityHeadersMiddleware>(policy);
    }
}
复制代码

封装好相关的安全类:

复制代码
public class SecurityHeadersBuilder  
{
    private readonly SecurityHeadersPolicy _policy = new SecurityHeadersPolicy();

    public SecurityHeadersBuilder AddDefaultSecurePolicy()
    {
        AddFrameOptionsDeny();
        AddXssProtectionBlock();
        AddContentTypeOptionsNoSniff();
        AddStrictTransportSecurityMaxAge();
        RemoveServerHeader();

        return this;
    }

    public SecurityHeadersBuilder AddFrameOptionsDeny()
    {
        _policy.SetHeaders[FrameOptionsConstants.Header] = FrameOptionsConstants.Deny;
        return this;
    }

    public SecurityHeadersBuilder AddFrameOptionsSameOrigin()
    {
        _policy.SetHeaders[FrameOptionsConstants.Header] = FrameOptionsConstants.SameOrigin;
        return this;
    }

    public SecurityHeadersBuilder AddFrameOptionsSameOrigin(string uri)
    {
        _policy.SetHeaders[FrameOptionsConstants.Header] = string.Format(FrameOptionsConstants.AllowFromUri, uri);
        return this;
    }

    public SecurityHeadersBuilder RemoveServerHeader()
    {
        _policy.RemoveHeaders.Add(ServerConstants.Header);
        return this;
    }

    public SecurityHeadersBuilder AddCustomHeader(string header, string value)
    {
        _policy.SetHeaders[header] = value;
        return this;
    }

    public SecurityHeadersBuilder RemoveHeader(string header)
    {
        _policy.RemoveHeaders.Add(header);
        return this;
    }

    public SecurityHeadersPolicy Build()
    {
        return _policy;
    }
}
复制代码

最后注入到HTTP的输入流中:

app.UseSecurityHeadersMiddleware(new SecurityHeadersBuilder() 
.AddDefaultSecurePolicy()
);

然后我们浏览一下网页,就可以在HTTP的头部信息中看到:

复制代码
HTTP/1.1 200 OK 
Content-Type: text/html; charset=utf-8 
X-Frame-Options: DENY 
X-XSS-Protection: 1; mode=block 
X-Content-Type-Options: nosniff 
Strict-Transport-Security: max-age=31536000 
X-Powered-By: ASP.NET
复制代码

还有一个就是CSRF的防护,如果之前你用过ASP.NET MVC,在最基本的MVC模板中,可能你会留意到已有的cshtml页面中的form表单有这么一句:

@Html.AntiForgeryToken()

这就是微软在MVC框架中为我们提供的防护CSRF的方法。我们在表单中直接使用上面那句代码就可以了,然后在表单提交的Action方法中:

[ValidateAntiForgeryToken]
[HttpPost]
public IActionResult AntiForm(string message)
{
return Content(message);
}

使用[ValidateAntiForgeryToken]属性,来验证CSRF。


参考链接:
How to add security headers in ASP.NET Core using custom middleware(如何使用自定义中间件在ASP.NET Core中添加安全标头)

初探CSRF在ASP.NET Core中的处理方式

代码地址:
https://github.com/RyanOvO/aspnetcore-fileup-demo

 

 

获取服务端https证书

最近开发一个需求,涉及获取服务端https证书。一般进行https调用我们都不太关心底层细节,直接使用WebClient或者HttpWebRequest来发送请求,这两种方法都无法获取证书信息,需要用到ServicePoint,这个类用于提供HTTP连接的管理。

 

写个Demo,拿新浪首页试一下:

复制代码
using System;
using System.Net;
using System.Security.Cryptography.X509Certificates;
 
namespace GetServerCertificateDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            //用WebClient访问新浪首页
            var http = new WebClient();
            var uri = new Uri("https://www.sina.com.cn");
            http.DownloadString(uri);
             
            //通过Uri获取ServicePoint
            var servicePoint = ServicePointManager.FindServicePoint(uri);
             
            //取服务端证书,X509Certificate格式,转一下
            var serverCert = new X509Certificate2(servicePoint.Certificate);
            Console.WriteLine("颁发给:{0}", serverCert.Subject);
            Console.WriteLine("颁发者:{0}", serverCert.Issuer);
            Console.WriteLine("序列号:{0}", serverCert.SerialNumber);
            Console.WriteLine("指  纹:{0}", serverCert.Thumbprint);
            Console.WriteLine("起  始:{0}", serverCert.NotBefore);
            Console.WriteLine("过  期:{0}", serverCert.NotAfter);
        }
    }
}
复制代码

 

运行看效果:

上半部分是程序运行结果,下面是用Firefox查看的服务端证书信息,各项信息都能对应上。如果程序中涉及多个不同服务器的访问也没关系,关键在于根据Uri获取ServicePoint,然后取到的证书就是此服务器的了。

 

 

Js异常捕获

 
复制代码
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <script>
            function demo(){
                try{
                    alert(str);
                }catch(e){
                    alert(e);
                }
            }
            demo();
        </script>
        
        <form>
            <input id="txt" type="text"/>
            <input id="btn" type="button" onclick="demo1()" value="按钮" />
        </form>
        <script>
            function demo1(){
                try{
                    var e=document.getElementById("txt").value;
                if(e==""){
                    throw "请输入";
                }
                }catch(e){
                    alert(e);
                }
                
            }
        </script>
    </body>
</html>
复制代码

 

js事件

onLoad    网页加载事件

onUnload    关闭网页事件

 

反射:获取Class对象的三种方式

 

数据库_mysql多表操作

 

 

数据库_mysql多表操作

 

多表操作

       实际开发中,一个项目通常需要很多张表才能完成。例如:一个商城项目就需要分类表(category)、商品表(products)、订单表(orders)等多张表。且这些表的数据之间存在一定的关系。

1.1    表与表之间的关系

l  一对多关系:

n  常见实例:客户和订单,分类和商品,部门和员工.

n  一对多建表原则:在从表(多方)创建一个字段,字段作为外键指向主表(一方)的主键.

l  多对多关系:

n  常见实例:学生和课程、用户和角色

n  多对多关系建表原则:需要创建第三张表,中间表中至少两个字段,这两个字段分别作为外键指向各自一方的主键.

l  一对一关系:(了解)

n  在实际的开发中应用不多.因为一对一可以创建成一张表.

n  两种建表原则:

u  外键唯一:主表的主键和从表的外键(唯一),形成主外键关系,外键唯一unique。

u  外键是主键:主表的主键和从表的主键,形成主外键关系。

1.2    外键约束

现在我们有两张表“分类表”和“商品表”,为了表明商品属于哪个分类,通常情况下,我们将在商品表上添加一列,用于存放分类cid的信息,此列称为:外键

此时“分类表category”称为:主表,“cid”我们称为主键。“商品表products”称为:从表,category_id称为外键。我们通过主表的主键和从表的外键来描述主外键关系,呈现就是一对多关系。

         外键特点:

u  从表外键的值是对主表主键的引用。

u  从表外键类型,必须与主表主键类型一致。

 

l  声明外键约束       

语法:alter table 从表 add [constraint] [外键名称] foreign key (从表外键字段名) references 主表 (主表的主键);

[外键名称]用于删除外键约束的,一般建议“_fk”结尾

    altertable 从表 drop foreignkey 外键名称

l  使用外键目的:

n  保证数据完整性

 

1.3    一对多操作

1.3.1    分析

  • category分类表,为一方,也就是主表,必须提供主键cid

  • products商品表,为多方,也就是从表,必须提供外键category_id

  • 1.3.2    实现:分类和商品

     

    ###创建分类表

    create table category(

      cidvarchar(32) PRIMARY KEY ,

      cnamevarchar(100)        #分类名称

    );

     

    # 商品表

    CREATE TABLE `products` (

      `pid`varchar(32) PRIMARY KEY  ,

      `name`VARCHAR(40) ,

      `price`DOUBLE

    );

     

    #添加外键字段

    alter table products add column category_id varchar(32);

     

    #添加约束

    alter table products add constraint product_fkforeign key (category_id) references category (cid);

     

    1.3.3    操作

    #1 向分类表中添加数据

    INSERT INTO category (cid ,cname) VALUES('c001','服装');

     

    #2 向商品表添加普通数据,没有外键数据,默认为null

    INSERT INTO products (pid,pname) VALUES('p001','商品名称');

     

    #3 向商品表添加普通数据,含有外键信息(数据存放在)

    INSERT INTO products (pid ,pname ,category_id)VALUES('p002','商品名称2','c001');

     

    #4 向商品表添加普通数据,含有外键信息(数据不存在) -- 不能异常

    INSERT INTO products (pid ,pname ,category_id)VALUES('p003','商品名称2','c999');

     

    #5 删除指定分类(分类被商品使用) -- 执行异常

    DELETE FROM category WHERE cid = 'c001';

     

    1.4    多对多

    1.4.1    分析

    • 商品和订单多对多关系,将拆分成两个一对多。

    • products商品表,为其中一个一对多的主表,需要提供主键pid

    • orders 订单表,为另一个一对多的主表,需要提供主键oid

    • orderitem中间表,为另外添加的第三张表,需要提供两个外键oid和pid

     

     

    1.4.2    实现:订单和商品

     

    ### 商品表[已存在]

     

    ### 订单表

    create table `orders`(

      `oid`varchar(32) PRIMARY KEY ,

     `totalprice` double   #总计

    );

     

    ### 订单项表

    create table orderitem(

      oidvarchar(50),-- 订单id

      pidvarchar(50)-- 商品id

    );

     

    ###---- 订单表和订单项表的主外键关系

    alter table `orderitem` add constraintorderitem_orders_fk foreign key (oid) references orders(oid);

     

    ###---- 商品表和订单项表的主外键关系

    alter table `orderitem` add constraintorderitem_product_fk foreign key (pid) references products(pid);

     

    ### 联合主键(可省略)

    alter table `orderitem` add primary key (oid,pid);

     

     

    1.4.3    操作

    #1 向商品表中添加数据

    INSERT INTO products (pid,pname) VALUES('p003','商品名称');

     

    #2 向订单表中添加数据

    INSERT INTO orders (oid ,totalprice)VALUES('x001','998');

    INSERT INTO orders (oid ,totalprice)VALUES('x002','100');

     

    #3向中间表添加数据(数据存在)

    INSERT INTO orderitem(pid,oid)VALUES('p001','x001');

    INSERT INTO orderitem(pid,oid)VALUES('p001','x002');

    INSERT INTO orderitem(pid,oid)VALUES('p002','x002');

     

    #4删除中间表的数据

    DELETE FROM orderitem WHERE pid='p002' AND oid ='x002';

     

    #5向中间表添加数据(数据不存在) -- 执行异常

    INSERT INTO orderitem(pid,oid)VALUES('p002','x003');

     

    #6删除商品表的数据 -- 执行异常

    DELETE FROM products WHERE pid = 'p001';

posted @ 2018-07-14 01:34  ~雨落忧伤~  阅读(257)  评论(0编辑  收藏  举报