软件设计:DAO层该如何设计

本文讨论了DAO层的两种设计方案:每表一DAO和基础DAO类。第一种可能导致大量重复代码,第二种利用泛型减少重复,但代码量仍然较多。作者寻求关于如何更高效地设计DAO层的建议,期望实现业务与数据库操作的解耦,以提高系统的独立性和可维护性。

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

关于Dao层的设计我现在也还是有点模糊,大大小小的项目也做了五六个了,负责的数据库设计也有三四个了。

在对Dao层进行设计时采用过两种方案:

  方案一:每一表对应一个Dao类(接口也可),每个Dao将完成对该表的增删改查以及业务上要求的查询操作。这么设计的话如果表很多的话将会产生很多类,并且将会出现大量重复的代码,因为每一个Dao中都将涉及到基础的增删改查。

  方案二:写一个基础的类,可以完成基本的增删改查,其他的对于业务上有额外需求的表单独在写一个类,不过这个类只包括额外的功能。这里的基础类写的时候是需要严格注意的,因为采用的类似映射的实现,需要你把实体类设计的同表结构一摸一样,因为在该类中对数据库的增删改查的Sql语句就是通过对实体类类名以及对实体类类中属性的提取完形成的。

  这里给出一个基本的添加方法(vb.net实现):

Public Class SqlDao : Implements Dal.IDao

        Private SqlDr As SqlDataReader
        Private SqlCon As SqlConnection
        Private SqlCmd As SqlCommand
        '从配置文件app.config中取得连接数据库的字符串
        Private strConnect As String = ConfigurationManager.AppSettings("strCon")
        '得到类名
        Private strClassName As String
        '得到类的类型
        Private mType As Type
        '得到属性集
        Private mProS As PropertyInfo()

        '在初始化方法中连接数据库
        'Public Sub Init(ByVal obj As Object) Implements IDao.Init
        '    SqlCon = New SqlConnection(strConnect)

        '    '在构造函数中对必要类型进行初始化
        '    strClassName = TypeName(obj)
        '    mType = obj.GetType()
        '    mProS = mType.GetProperties
        'End Sub

        '连接数据库的一个私有方法
        Private Function GetCon() As SqlConnection Implements IDao.GetCon
            Try
                If (SqlCon.State = ConnectionState.Closed) Then
                    SqlCon.Open()
                End If
            Catch ex As Exception
                MsgBox("打开数据库时:" + ex.Message)
            End Try

            Return SqlCon
        End Function


        ''' <summary>
        ''' 将对象添加到对应的表中,参数为对象,返回值为Int型,表示影响的行数
        ''' </summary>
        ''' <param name="Entity "></param>
        ''' <returns>Integer</returns>
        ''' <remarks></remarks>
        Public Function AddObj(Of T)(ByVal Entity As T) As Integer Implements IDao.AddObj
            Dim res As Integer = 0 '用来返回该操作影响的行数
            '定义单个属性
            Dim mPro As PropertyInfo
            '定义sql参数
            Dim para As SqlParameter
            Dim strFields As String = ""
            Dim strCondition As String = ""
            Dim strSql As String = ""
            For Each mPro In mProS
                '该循环用来进行参数组合
                '再添加時不用添加时间
                strFields = CStr(Trim(mPro.GetValue(Entity, Nothing)))  '获取属性值
                If strFields <> "0" And strFields <> "00:00:00" And strFields <> "" Then

                    strSql = strSql + "@" + mPro.Name + ","
                    '组合形成字段名
                    strCondition = strCondition + mPro.Name + ","
                End If
            Next
            '最后再插入最后的括号

            strSql = Left(strSql, Len(strSql) - 1) + ")"
            strCondition = Left(strCondition, Len(strCondition) - 1) + ")"
            strSql = "INSERT INTO " + strClassName + " (" + strCondition + " VALUES (" + strSql
            'MsgBox(strSql)
            Try
                Using sCmd As New SqlCommand(strSql, GetCon)
                    '设定执行方式
                    sCmd.CommandType = CommandType.Text
                    For Each mPro In mProS
                        '进行参数的赋值
                        'Dim stra As String = mPro.GetValue(obj, Nothing)
                        '一般的添加不用加入时间,除了下机表
                        strFields = CStr(Trim(mPro.GetValue(Entity, Nothing)))  '获取属性值
                        If strFields <> "0" And strFields <> "00:00:00" And strFields <> "" Then
                            para = New SqlParameter("@" + mPro.Name, mPro.GetValue(Entity, Nothing))

                            sCmd.Parameters.Add(para)
                        End If
                    Next
                    res = sCmd.ExecuteNonQuery
                End Using
            Catch ex As Exception
                MsgBox("进行对象添加时:" + ex.Message)
            End Try
            Return res '返回该操作影响的行数
        End Function
End Class

  这几天从网上也查了一些资料主要就是关于Dao层该如何设计的问题,最直接的设计方案就是每一个表对应一个Dao,说是代码重复太多,不过这样设计那些基本的代码是不用写的,都有现成的工具,直接根据表生成对应的增删改查。

  不过因为需要重复的代码太多了,因此有人提出这种方法:

    public interface BaseDao<T> {  
        public void create (T t);  
        public void delete (T t);  
        public void update (T t);  
    }  
      
    public interface WindDao extends BaseDao<Wind> {  
        public void other (Wind wind);  
    }

  将基本的增删改查通过泛型放置到一个基础的接口中,其他的只需实现该接口,如果有额外的需求便可自行添加方法。这可谓一个典型的继承的应用。不过说实话,这种方法也不能使代码量减少。

  这块还真是不太明白,论坛里有人说:“其实可以将basedao注入baseservice来实现,这样就不用每个dao都写一个类了”,实在是理解不了,如果有大牛恰好经过劳烦指点一二,不胜感激。

曹师哥点拨:

遇到一个问题,我会采用软件工程上的3w原则(what,why,how)来思考。

首先what:

  • dao(data access object),数据访问对象,既然是对象那么就有封装,他封装了业务及相关数据与数据库进行交互的一系列的接口。

why:

  • 1.用户不需要了解这个对象细节,只需要了解这个对象的接口就可以数据库进行交互,这样方便了用户的使用。
  • 2.设计一个dao层,上面所有的业务层都调用这个dao层的接口,这样就实现了软件的重用性。
  • 3.dao层的存在使得业务逻辑层跟访问数据库的代码分开了。
  • 4.dao层可以处理不同数据库的差异性,使得软件在oracle,mysql,db2等数据库上迁移时改变代码很少。
  • 5.dao层的封装不需要开发人员直接跟数据库交互(有了dao层,通过dao层交互),增加了数据库的安全性。等等

how:通过以上的why的分析,我们在设计dao层的时候,要注意:

  • 1.提供丰富的接口供用户调用,
  • 2.在dao中不能涉及业务内容,一个dao层接口就对应一次数据库操作(是原子性的)。
  • 3.考虑软件在不通数据库上的迁移。
  • 4.dao层是用来实现业务及相关数据和数据库交互的桥梁,那么dao层就需要对数据库的操作进行一系列的封装,包括增删改查,数据库的事务,存储过程,触发器等等操作。其中有一点要注意的是事务的处理,dao层一般不负责事务的处理,把事务处理遗留给业务层来做。原因是:如果一次业务逻辑需要调用多个dao的方法,一旦某个dao的方法失败,造成回滚,则已经执行的那些DAO则无法回滚。

总之,使用dao层,使得业务的操作跟数据库的操作进行了解耦,业务的变化不会影响数据的访问,而数据访问方式的改变(保证接口不变),不会影响业务,使得系统的各个部分相互独立。dao层的操作是对业务的一个分解,把一个完整的业务分解到数据库中的相关表中。

原创文章,转载请注明出处:http://www.the5fire.net/?p=168
本文固定链接:the5fire的技术博客 | 如果喜欢我的博客的话可以订阅本博以便及时收到更新

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值