Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Example Analysis of Database access Design in PetShop data access layers

2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article mainly introduces the PetShop data access layers in the database access design example analysis, has a certain reference value, interested friends can refer to, I hope you can learn a lot after reading this article, the following let the editor with you to understand.

Database access Design of PetShop data access layer

In series 1, I analyzed the architectural design of PetShop as a whole and mentioned the concept of layering. Starting from this section, I will analyze each layer at the code level in turn in order to get a more detailed and in-depth understanding. In PetShop 4.0, due to the introduction of some new features of ASP.Net 2.0, the content of the data layer is more extensive and complex, including: database access, Messaging, MemberShip, Profile four parts. In Series 2, I will introduce the design of database access.

In PetShop, the database objects that the system needs to deal with are divided into two categories: one is the data entity, which corresponds to the corresponding data table in the database. They have no behavior and are only used to represent the data of the object. These entity classes are placed in the Model assembly, such as the entity class OrderInfo corresponding to the data table Order, whose class diagram is as follows:

These objects do not have the function of persistence, to put it simply, they are used as carriers of data to facilitate business logic to read / write to the corresponding data tables. Although the attributes of these classes map the columns of the data table respectively, and each object instance corresponds to every row of the data table, these entity classes do not have the corresponding database access ability.

Because both the data access layer and the business logic layer will operate on these data entities, the assembly Model will be referenced by the modules in these two layers.

The second type of database object is the business logic object of the data. The business logic referred to here is not domain business logic in the sense of the business logic layer (in this sense, I prefer to refer to the business logic layer as the "domain logic layer"). Generally speaking, these business logic are basic database operations, including Select,Insert,Update and Delete. Because these business logic objects only have behavior and are independent of data, they are abstracted as a separate interface module IDAL, such as the interface IOrder corresponding to the data table Order:

It is in line with the spirit of object-oriented to separate data entities from related database operations. First of all, it embodies the principle of "separation of responsibilities". Separating the data entity from its behavior weakens the dependence between the two. When the data behavior changes, it does not affect the data entity object in the Model module, and avoids the "catastrophic" influence of the citation of this class because one class has too many responsibilities and too much responsibility. Second, it embodies the spirit of abstraction, or the best embodiment of interface-oriented programming. Abstract interface module IDAL, completely isolated from the specific database access implementation. This implementation-independent design not only ensures the scalability of the system, but also ensures the portability of the database. In PetShop, SQLServer and Oracle can be supported, so their specific implementation is placed in two different modules SQLServerDAL and OracleDAL.

Take Order as an example, there are different implementations in SQLServerDAL and OracleDAL modules, but they all implement IOrder interfaces at the same time, as shown in the figure:

From the point of view of the implementation of the database, PetShop reflects the bloated and ugly without the ORM framework. Because of the Insert and Select operations on the data table, take SQL Server as an example, objects such as SqlCommand,SqlParameter,SqlDataReader are used to complete these operations. Particularly complicated is the passing of Parameter, where a large number of string constants are used to hold the names of parameters in PetShop. In addition, PetShop specifically provides abstract Helper classes for SQL Server and Oracle, wrapping some common operations, such as ExecuteNonQuery, ExecuteReader, and so on.

In the absence of ORM, using Helper class is a better strategy. Using it to complete the encapsulation of basic database operations can reduce a lot of code related to database operations, which reflects the principle of object reuse. PetShop unifies these Helper classes into the DBUtility module, and the methods exposed by Helper classes in different databases are basically the same, except for some special requirements, such as the way bool types are handled differently in Oracle and SQL Server, thus providing OraBit and OraBool methods specifically. In addition, the methods in the Helper class are static methods for easy invocation. The class diagram of OracleHelper is as follows:

For the data access layer, the biggest headache is the processing of SQL statements. In the early CS structure, because there is no three-tier architecture design, the data access layer and business logic layer are closely mixed together, so SQL statements are all over every corner of the system. This brings great difficulties to the maintenance of the program. In addition, because Oracle uses PL-SQL, while SQL Server and Sybase use T-SQL, although both follow the standard SQL syntax, there are still differences in many details. If a large number of SQL statements are used in the program, it will undoubtedly bring difficulties to the possible database migration.

The best way is to use stored procedures. This method makes the program cleaner, in addition, because stored procedures can exist in the form of database scripts, it is also easy to port and modify. But this approach is still flawed. First, the testing of stored procedures is relatively difficult. Although there are corresponding debugging tools, it is still more complex and inconvenient than debugging the code. Second, it brings obstacles to the update of the system. If the database access is done by the program, under the .net platform, we only need to xcopy the recompiled assembly to the deployed server after modifying the program. If a stored procedure is used, there must be a dedicated DBA to rerun the script for the stored procedure for security reasons, and the way it is deployed is limited.

I once used a special table to store SQL statements in a project. If you want to use the relevant SQL statement, use keyword search to get the corresponding statement. This approach is similar to a call to a stored procedure, but avoids deployment problems. However, this approach cannot be guaranteed in terms of performance. It is only suitable for scenarios where there are few SQL statements. However, with good design, we can provide different tables for various businesses to store SQL statements. By the same token, these SQL statements can also be stored in XML files, which is more conducive to the expansion or modification of the system. However, we need to provide it with a special SQL statement management tool.

The use of SQL statements is inevitable, and there is no final conclusion on how to better use SQL statements, but there is a principle worth following, that is, "we should try our best to make SQL statements exist in the concrete implementation of the data access layer."

Of course, if you apply ORM, then everything will be different. Because the ORM framework already provides basic Select,Insert,Update and Delete operations for data access. For example, in NHibernate, we can directly call the Save method of the ISession object to Insert (or Create) a data entity object:

Public void Insert (OrderInfo order) {ISession s = Sessions.GetSession (); ITransaction trans = null; try {trans = s.BeginTransaction (); s.Save (order); trans.Commit ();} finally {s.Close ();}}

There are no SQL statements, no annoying Parameters, and you don't even need to think about transactions. In addition, such a design is also database-independent, and NHibernate can support different databases through the Dialect (dialect) mechanism. The only thing to do is that we need to define the hbm file for OrderInfo.

Of course, the ORM framework is not omnipotent. In the face of complex business logic, it can not completely eliminate SQL statements and replace complex database access logic, but it well embodies the "80 / 20 (or 90 / 10) rule" (also known as "Pareto rule"), that is to say, it takes less effort (10% / 20%) to solve most of the problems (80% / 90%). It takes a lot more effort to solve the few remaining problems. At the very least, the CRUD operations that make up the vast majority of the data access layer need to be solved with very little time and effort by using the ORM framework. This undoubtedly shortens the development cycle of the entire project.

Let's get back to the discussion of PetShop. Now that we have data entities, abstract interfaces and implementations of data objects, it can be said that the subject of database access has been completed. There are still two problems left for us to solve:

1. Management of data object creation

2. It is beneficial to the transplant of database.

In PetShop, the data objects to be created include Order,Product,Category,Inventory,Item. In the previous design, these objects have been abstracted into corresponding interfaces, and their implementation varies from database to database. That is, there are multiple categories of objects created, and each category has a different implementation, which is a typical application scenario of the abstract factory pattern. The two problems mentioned above can also be solved by abstracting the factory pattern. The standard abstract factory pattern class diagram is as follows:

For example, the Order object that creates the SQL Server is as follows:

PetShopFactory factory = new SQLServerFactory (); IOrder = factory.CreateOrder ()

To take into account the portability of the database, the factory must be a global variable and instantiated when the main program is running. However, although this design has achieved the purpose of "encapsulating changes", it is still inevitable that there is a concrete class SQLServerFactory when creating PetShopFactory objects, that is to say, the program has a strong dependence on SQLServerFactory at this level. Once the entire system is required to support Oracle, you also need to modify this line of code to:

PetShopFactory factory = new OracleFactory ()

This behavior of modifying the code is obviously unacceptable. The solution is "dependency injection". The function of "dependency injection" is usually provided by a special IoC container, which includes Spring,PicoContainer on the Java platform. On the .net platform, the most common one is Spring.Net. However, in PetShop systems, there is no need for a special container to implement "dependency injection". The simple approach is to use configuration files and reflection capabilities to do so. That is, we can configure the complete class name of the specific Factory object in the web.config file. However, when we take advantage of configuration files and reflection capabilities, the creation of specific factories seems to be a bit "icing on the cake", and we can point directly to specific database object implementation classes, such as PetShop.SQLServerDAL.IOrder, in the configuration file. In that case, the related factory in the abstract factory pattern can be simplified to a factory class, so I call this pattern "abstract factory pattern with simple factory characteristics", and its class diagram is as follows:

The DataAccess class completely replaces the factory class architecture created earlier, which is a sealed class, where the methods for creating various data objects are static methods. The reason why this class can be used as an abstract factory is because of the use of configuration files and reflections, as shown in the following code snippet:

Public sealed class DataAccess {/ / Look up the DAL implementation we should be using private static readonly string path = ConfigurationManager.AppSettings ["WebDAL"]; private static readonly string orderPath = ConfigurationManager.AppSettings ["OrdersDAL"]; public static PetShop.IDAL.IOrder CreateOrder () {string className = orderPath + ".order"; return (PetShop.IDAL.IOrder) Assembly.Load (orderPath) .CreateInstance (className);}}

In PetShop, this way of relying on configuration files and reflection to create objects is extremely common, including IBLLStategy, CacheDependencyFactory, and so on. These implementation logic is scattered throughout the PetShop system, and in my opinion, it can be refactored on this basis. In other words, we can provide an implementation similar to "Service Locator" for the entire system:

Public static class ServiceLocator {private static readonly string dalPath = ConfigurationManager.AppSettings ["WebDAL"]; private static readonly string orderPath = ConfigurationManager.AppSettings ["OrdersDAL"]; / /. Private static readonly string orderStategyPath = ConfigurationManager.AppSettings ["OrderStrategyAssembly"]; public static object LocateDALObject (string className) {string fullPath = dalPath + "." + className; return Assembly.Load (dalPath) .CreateInstance (fullPath);} public static object LocateDALOrderObject (string className) {string fullPath = orderPath + "." + className; return Assembly.Load (orderPath) .CreateInstance (fullPath);} public static object LocateOrderStrategyObject (string className) {string fullPath = orderStategyPath + "." + className; return Assembly.Load (orderStategyPath) .CreateInstance (fullPath);} / /. }

Then all the code related to the so-called "dependency injection" can be done with ServiceLocator. For example, the class DataAccess can be simplified to:

Public sealed class DataAccess {public static PetShop.IDAL.IOrder CreateOrder () {return (PetShop.IDAL.IOrder) ServiceLocator. LocateDALOrderObject ("Order");}}

Through ServiceLocator, all namespace values related to configuration files are managed uniformly, which is beneficial to the management and future maintenance of various dynamically created objects.

Thank you for reading this article carefully. I hope the article "sample Analysis of data Base access Design in PetShop data access layers" shared by the editor will be helpful to you. At the same time, I also hope you will support us and pay attention to the industry information channel. More related knowledge is waiting for you to learn!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report