简体   繁体   English

初学者数据库最佳实践

[英]Database Best-Practices for Beginners

So, I am a fairly new programmer working towards an undergraduate Comp Sci degree with a very small amount of work experience. 因此,我是一名相当新的程序员,致力于本科Comp Sci学位,并且工作经验非常少。 In looking for internship-type jobs for my program, I have noticed that what I've heard from several profs -- "working with databases makes up 90% of all modern computer science jobs" -- looks like it is actually true. 在为我的课程寻找实习型工作时,我注意到我从几位教授那里听到的 - “使用数据库占所有现代计算机科学工作的90%” - 看起来确实如此。 However, my program doesn't really have any courses with databases until 3rd year, so I'm trying to at least learn some things myself in the mean time. 但是,我的课程直到第3年才开始有任何数据库课程,所以我试着至少自己学习一些东西。

I've seen very little on SO and the internet in general for somebody like myself. 对于像我这样的人,我在SO和互联网上看到的很少。 There seem to be tons of tutorials on the mechanics of how to read and write data in a database, but little on the associated best practices. 似乎有大量关于如何在数据库中读取和写入数据的机制的教程,但很少有相关的最佳实践。 To demonstrate what I am talking about, and to help get across my actual question, here is what can easily be found on the internet : 为了展示我正在谈论的内容,并帮助我解决实际问题,可以在互联网上轻松找到

public static void Main ()
{
    using (var conn = new OdbcConnection())
    {
        var command = new OdbcCommand();
        command.Connection = conn;
        command.CommandText = "SELECT * FROM Customer WHERE id = 1";
        var dbAdapter = new OdbcDataAdapter();
        dbAdapter.SelectCommand = command;
        var results = new DataTable();
        dbAdapter.Fill(results);
    }

    // then you would do something like
    string customerName = (string) results.Rows[0]["name"]; 
}

And so forth. 等等。 This is pretty simple to understand but obviously full of problems. 这很容易理解,但显然有很多问题。 I started out with code like this and quickly started saying things like "well it seems dumb to just have SQL all over the place, I should put all that in a constants file." 我开始使用这样的代码,很快就开始说“好像只是让SQL到处都是愚蠢的,我应该把所有这些都放在一个常量文件中。” And then I realized that it was silly to have those same lines of code all over the place and just put all that stuff with connection objects etc inside a method: 然后我意识到,在整个地方使用相同的代码行并将所有内容与连接对象等放在方法中是很愚蠢的:

public DataTable GetTableFromDB (string sql)
{
    // code similar to first sample
}    

string getCustomerSql = String.Format(Constants.SelectAllFromCustomer, customerId);
DataTable customer = GetTableFromDB(getCustomerSql);
string customerName = (string) customer.Rows[0]["name"];

This seemed to be a big improvement. 这似乎是一个很大的进步。 Now it's super-easy to, say, change from an OdbcConnection to an SQLiteConnection. 现在,从OdbcConnection更改为SQLiteConnection是非常容易的。 But that last line, accessing the data, still seemed awkward; 但访问数据的最后一行似乎仍然很尴尬; and it is still a pain to change a field name (like going from "name" to "CustName" or something). 更改字段名称(例如从“名称”转到“CustName”或其他内容)仍然很痛苦。 I started reading about using typed Data sets or custom business objects . 我开始阅读有关使用类型化数据集或自定义业务对象的内容 I'm still kind of confused by all the terminology, but decided to look into it anyway. 我仍然对所有术语感到困惑,但无论如何都决定调查它。 I figure that it is stupid to rely on a shiny Database Wizard to do all this stuff for me (like in the linked articles) before I actually learn what is going on, and why. 我认为依靠闪亮的数据库向导为我做所有这些事情是愚蠢的(比如在链接的文章中),然后才能真正了解发生了什么,以及为什么。 So I took a stab at it myself and started getting things like: 所以我自己就把它刺了一下,开始得到这样的东西:

public class Customer
{
    public string Name {get; set;}
    public int Id {get; set;}

    public void Populate ()
    {
        string getCustomerSql = String.Format(Constants.SelectAllFromCustomer, this.Id);
        DataTable customer = GetTableFromDB(getCustomerSql);
        this.Name = (string) customer.Rows[0]["name"]; 
    }

    public static IEnumerable<Customer> GetAll()
    {
        foreach ( ... ) { 
            // blah blah
            yield return customer;
        }
    }
}

to hide the ugly table stuff and provide some strong typing, allowing outside code to just do things like 隐藏丑陋的表格并提供一些强大的输入,允许外部代码只做一些事情

var customer = new Customer(custId);
customer.Populate();
string customerName = customer.Name;

which is really nice. 这真的很好。 And if the Customer table changes, changes in the code only need to happen in one place: inside the Customer class. 如果Customer表发生更改,则代码中的更改只需要在一个位置进行: Customer类内部。

So, at the end of all this rambling, my question is this. 所以,在所有这些漫无边际的结尾,我的问题就是这个。 Has my slow evolution of database code been going in the right direction? 我数据库代码的缓慢演变是否朝着正确的方向发展? And where do I go next? 我下一步去哪儿? This style is all well and good for small-ish databases, but when there are tons of different tables, writing out all those classes for each one would be a pain. 这种风格对于小型数据库来说都很好,但是当有大量不同的表时,为每个表写出所有这些类都会很痛苦。 I have heard about software that can generate that type of code for you, but am kind of still confused by the DAL/ORM/LINQ2SQL/etc jargon and those huge pieces of software are kind of overwhelming. 我听说过可以为你生成这种类型的代码的软件,但是仍然有点被DAL / ORM / LINQ2SQL / etc行话弄糊涂了,那些巨大的软件都是压倒性的。 I'm looking for some good not-overwhelmingly-complex resources that can point me in the right direction. 我正在寻找一些优秀而非压倒性复杂的资源,可以指引我朝着正确的方向前进。 All I can find on this topic are complex articles that go way over my head, or articles that just show you how to use the point-and-click wizards in Visual Studio and such. 我在这个主题上找到的只是复杂的文章,或者只是向您展示如何在Visual Studio中使用点击式向导的文章。 Also note that I'm looking for information on working with Databases in code, not information on Database design/normalization...there's lots of good material on that out there. 另请注意,我正在寻找有关在代码中使用数据库的信息,而不是有关数据库设计/规范化的信息...那里有很多很好的材料。

Thanks for reading this giant wall of text. 感谢您阅读这个巨大的文字墙。

Very good question indeed and you are certainly on the right track! 确实非常好的问题,你肯定是在正确的轨道上!

Being a computer engineer myself, databases and how to write code to interact with databases was also never a big part of my university degree and sure enough I'm responsible for all the database code at work. 作为一名计算机工程师,数据库以及如何编写与数据库交互的代码也从未成为我大学学位的重要组成部分,而且我确实负责所有工作中的数据库代码。

Here's my experience, using legacy technology from the the early 90s on one project and modern technology with C# and WPF on another. 这是我的经验,使用90年代早期的传统技术在一个项目和现代技术与C#和WPF在另一个项目。

I'll do my best to explain terminology as I go but I'm certainly not an expert myself yet. 我会尽我所能解释术语,但我自己肯定不是专家。

Tables, Objects, and Mappings Oh My! 表,对象和映射哦,我的!

A database contains tables but what really is that? 数据库包含表但实际上是什么? It's just flat data related to other flat data and if you dive in and start grabbing things its going to get messy quickly! 这只是与其他平面数据相关的平面数据,如果你潜入并开始抓住它会迅速变得混乱! Strings will be all over the place, SQL statements repeated, records loaded twice, etc... It's therefore generally a good practice to represent each table record ( or collection of tables records depending on their relationships ) as an single object, generally referred to as a Model. 字符串将遍布各处,SQL语句重复,记录加载两次等等...因此,将每个表记录(或表记录集合取决于它们的关系)表示为单个对象通常是一种很好的做法,通常称为作为一个模型。 This helps to encapsulate the data and provide functionality for maintaining and updating its state. 这有助于封装数据并提供维护和更新其状态的功能。

In your posting your Customer class would act as the Model! 在您的发布中,您的Customer类将充当模型! So you've already realized that benefit. 所以你已经意识到了这个好处。

Now there are a variety of tools/frameworks (LINQ2SQL, dotConnect, Mindscape LightSpeed) that will write all your Model code for you. 现在有各种各样的工具/框架(LINQ2SQL,dotConnect,Mindscape LightSpeed)可以为您编写所有的Model代码。 In the end they are mapping objects to relational tables or O/R mapping as they refer to it. 最后,他们将对象映射到关系表或O / R映射,因为它们引用它。

As expected when your database changes so do your O/R mappings. 正如预期的那样,当您的数据库发生更改时,您的O / R映射也会如此。 Like you touched on, if your Customer changes, you have to fix it in one place, again why we put things in classes. 就像你提到的那样,如果你的客户发生变化,你必须在一个地方修复它,这也是为什么我们把东西放在课堂上。 In the case of my legacy project, updating models consumed a lot of time because their were so many, while in my newer project it's a few clicks BUT ultimately the result is the same. 对于我的遗留项目,更新模型消耗了大量时间,因为它们太多了,而在我的新项目中只需点击几下,但最终结果是相同的。

Who should know what? 谁应该知道什么?

In my two projects there has been two different ways of how objects interact with their tables. 在我的两个项目中,对象如何与其表进行交互有两种不同的方式。

In some camps, Models should know everything about their tables, how to save themselves, have direct shared access to the connection/session and can perform actions like Customer.Delete() and Customer.Save() all by themselves. 在某些阵营中,模型应该知道有关其表的所有内容,如何自我保存,对连接/会话具有直接共享访问权限,并且可以单独执行Customer.Delete()Customer.Save()

Other camps, put reading, writing, deleting, logic in a managing class. 其他阵营,在管理课上放读,写,删除,逻辑。 For example, MySessionManager.Save( myCustomer ) . 例如, MySessionManager.Save( myCustomer ) This methodology has the advantage of being able to easily implement change tracking on objects and ensuring all objects reference the same underlying table record. 该方法的优点是能够轻松实现对象的更改跟踪并确保所有对象引用相同的基础表记录。 Implementing it however is more complex than the previously mention method of localized class/table logic. 然而,实现它比前面提到的本地化类/表逻辑方法更复杂。

Conclusion 结论

You're on the right track and in my opinion interacting with databases is extremely rewarding. 你正走在正确的轨道上,在我看来,与数据库进行交互是非常有益的。 I can remember my head spinning when I first started doing research myself. 当我第一次开始自己研究时,我记得我的脑袋旋转。

I would recommend experimenting a bit, start a small project maybe a simple invoicing system, and try writing the models yourself. 我建议尝试一下,启动一个小项目可能是一个简单的发票系统,并尝试自己编写模型。 After that try another small project and try leveraging a database O/R mapping tool and see the difference. 之后尝试另一个小项目并尝试利用数据库O / R映射工具并查看差异。

Your evolution is definitely in the right direction. 你的进化肯定是正确的方向。 A few more things to consider: 还有一些事情需要考虑:

My advice if you want to learn about databases, the first step is forget about the programming language, next, forget about which database you are using and learn SQL. 我的建议是,如果你想了解数据库,第一步是忘记编程语言,接下来,忘记你正在使用哪个数据库并学习SQL。 Sure there are many differences between mySQL, MS SQLserver and Oracle but there is so much that is the same. 当然,mySQL,MS SQLserver和Oracle之间存在很多差异,但有很多相同之处。

Learn about joins, select as, date formats, normalization. 了解联接,选择日期格式,规范化。 Learn what happens when you have millions and millions of records and things start to slow down, then learn to fix it. 了解当您拥有数百万条记录并且事情开始变慢时会发生什么,然后学会修复它。

Create a test project related to something that interests you, for example a bike store. 创建与您感兴趣的内容相关的测试项目,例如自行车商店。 See what happens when you add a few million products, and a few million customers and think of all the ways the data needs to relate. 了解添加数百万产品和数百万客户时会发生什么,并考虑数据需要的所有相关方式。

Use a desktop app for running queries on a local database (sequel pro, mysql workbench etc) as it's much quicker than uploading source code to a server. 使用桌面应用程序在本地数据库(续集专业版,mysql工作台等)上运行查询,因为它比将源代码上传到服务器要快得多。 And have fun with it! 并享受它的乐趣!

IMHO, you're definitely going in the right direction for really nice to work with maintainable code! 恕我直言,你肯定会朝着正确的方向前进,因为我可以使用可维护的代码! However I'm not convinced the approach will scale to a real app. 但是我不相信这种方法可以扩展到真正的应用程序。 A few thoughts that may be helpful 一些可能有帮助的想法

  1. While the code you're writing will be really nice to work with and really maintainable, it involves a lot of work up-front, this is part of the reason the wizards are so popular. 虽然您正在编写的代码非常适合使用并且可以维护,但它涉及大量的工作,这是向导如此受欢迎的部分原因。 They aren;t the nicest thing to work with, but save a lot of time. 它们不是最好的工作方式,但可以节省大量时间。
  2. Querying from the database is just the beginning; 从数据库查询只是一个开始; another reason for the use of typed datasets and wizards in general is that in most applications, users are at some stage going to edit your information and send it back for updating. 一般使用类型化数据集和向导的另一个原因是,在大多数应用程序中,用户在某个阶段会编辑您的信息并将其发回以进行更新。 Single records are fine, but what if your data is best represented in a Normalised way with a hierarchy of tables 4 deep? 单个记录很好,但如果您的数据最好以规范化的方式表示,并且表4的层次结构很深,那该怎么办? Writing code to auto-generate the update/insert/delete statements by hand for all that call be hellish, so tools are the only way forward. 编写代码来手动为所有调用自动生成更新/插入/删除语句,因此工具是唯一的前进方法。 typed DataSets will generate all the code to perform these updates for you and have some very powerful functionality for handling disconnected (eg Client-side) updates/rollbacks of recent modifications. 键入的DataSet将为您生成执行这些更新的所有代码,并具有一些非常强大的功能,用于处理最近修改的断开连接(例如客户端)更新/回滚。
  3. What the last guys said about SQL injection (which is a SERIOUSLY big deal in industry) and protecting yourself by using a DBCommand object and adding DbParameters. 最后一些人谈到SQL注入(这是业界非常重要的事情)并通过使用DBCommand对象和添加DbParameters来保护自己。

In general there's a really big problem in going from code to databases referred to as an impedance mismatch . 一般来说,从代码到数据库(称为阻抗不匹配)存在一个非常大的问题。 Bridging the gap is very tricky and that's why the majority of industry relies on tools to do the heavy lifting. 弥合差距是非常棘手的,这就是为什么大多数行业依赖工具来完成繁重的工作。 My advice would be to try the wizards out - because while stepping through a wizard is no test in skill, learning all their drawbacks/bugs and their various workarounds is a really useful skill in industry, and will allow you to get to some more advanced scenarios in data management more quickly (eg the disconnected update of a 4-deep table hierarchy I mentioned). 我的建议是尝试向导 - 因为虽然单步执行向导不是技能测试,但学习所有缺点/错误及其各种变通方法是行业中非常有用的技能,并且可以让您获得更高级的技能数据管理中的场景更快(例如,我提到的4深表层次结构的断开连接更新)。

If you're a bit scared of things like Linq to SQL and the Entity Framework, you could step half way in between and explore something like iBATIS.NET. 如果你有点害怕像Linq to SQL和Entity Framework这样的东西,你可以介入其中一半并探索像iBATIS.NET这样的东西。 It is simply a data mapper tool that takes some of the pain of the database connection management and mapping your result sets to custom domain objects. 它只是一个数据映射器工具,它可以解决数据库连接管理的一些难题,并将结果集映射到自定义域对象。

You still have to write all of your object classes and SQL, but it maps all of your data to the classes for you using reflection, and you don't have to worry about all of the underlying connectivity (you could easily write a tool to generate your classes). 您仍然必须编写所有对象类和SQL,但它使用反射将所有数据映射到类,并且您不必担心所有底层连接(您可以轻松编写工具到生成你的课程)。 When you're up and running with iBATIS (assuming you might be interested), your code will start to look like this: 当你启动并运行iBATIS(假设你可能感兴趣)时,你的代码将开始如下所示:

var customer = Helpers.Customers.SelectByCustomerID(1);

That SelectByCustomerID function exists inside the Customers mapper, whose definition might look like: SelectByCustomerID函数存在于Customers映射器中,其定义可能如下所示:

public Customer SelectByCustomerID(int id)
{
    Return Mapper.QueryForObject<Customer>("Customers.SelectByID", id);
}

The "Customers.SelectByID" maps to an XML statement definition where "Customers" is the namespace and "SelectByID" is the ID of the map containing your SQL: “Customers.SelectByID”映射到XML语句定义,其中“Customers”是命名空间,“SelectByID”是包含SQL的映射的ID:

<statements>
    <select id="SelectByID" parameterClass="int" resultClass="Customer">
        SELECT * FROM Customers WHERE ID = #value#
    </select>
</statements>

Or when you want to change a customer you can do things like: 或者当您想要更换客户时,您可以执行以下操作:

customer.FirstName = "George"
customer.LastName = "Costanza"

Helpers.Customers.Update(customer);

LINQ to SQL and the Entity Framework get fancier by producing the SQL for you automatically. 通过为您自动生成SQL,LINQ to SQL和实体框架变得更加漂亮。 I like iBATIS because I still have full control of the SQL and what my domain objects look like. 我喜欢iBATIS,因为我仍然可以完全控制SQL以及我的域对象的外观。

Check out iBATIS (now migrated to Google under the name MyBatis.NET). 查看iBATIS (现在以名称MyBatis.NET迁移到Google)。 Another great package is NHibernate , which is a few steps ahead of iBATIS and closer to a full ORM. 另一个很棒的软件包是NHibernate ,它比iBATIS提前几步,更接近完整的ORM。

Visual page of database just with combobox and datagrid 数据库的可视化页面只有combobox和datagrid

namespace 命名空间

TestDatabase.Model TestDatabase.Model

{ class Database {class Database

{
    private MySqlConnection connecting;
    private MySqlDataAdapter adapter;

    public Database()
        {
        connecting = new MySqlConnection("server=;uid=;pwd=;database=;");
        connecting.Open();
        }

    public DataTable GetTable(string tableName)
    {
        adapter = new MySqlDataAdapter("SELECT * FROM "+ tableName, connecting);
        DataSet ds = new DataSet();
        adapter.Fill(ds);

        adapter.UpdateCommand = new MySqlCommandBuilder(adapter).GetUpdateCommand(); 
        adapter.DeleteCommand = new MySqlCommandBuilder(adapter).GetDeleteCommand(); 

        ds.Tables[0].RowChanged += new DataRowChangeEventHandler(Rowchanged);
        ds.Tables[0].RowDeleted += new DataRowChangeEventHandler(Rowchanged);

        return ds.Tables[0];
    }
    public void Rowchanged(object sender, DataRowChangeEventArgs args)
    {
        adapter.Update(sender as DataTable);
    }

}

} }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM