精华内容
下载资源
问答
  • HolloWorld是编程语言共同默认的第一个可执行代码,然并卵!我们老祖宗教育我们要“学以致用”!...这是一个用Go编写的具有日志处理、数据库连接的简单的网站程序代码,Golang初学者最佳学习参考代码
  • [来源] builder.com.cn 本文的目标是用不到140行的C#代码编写一个简单的面向对象的数据库(OODB)。很显然这不会是一个最简单的项目;但是C#和.NET框架提供了很多内置的函数,我们可以利用它们来帮助我们实现这一...
    
    

    [来源] builder.com.cn

    本文的目标是用不到140行的C#代码编写一个简单的面向对象的数据库(OODB)。很显然这不会是一个最简单的项目;但是C#和.NET框架提供了很多内置的函数,我们可以利用它们来帮助我们实现这一目标。这个数据库的要求如下:

    • 必须能够保存对象的状态,而不需要向对象加入额外的信息,也不需要准确地告诉数据库如何保存对象。
    • 必须能够从数据存储区里获得对象,并向用户提供一个完整的业务对象,而不需要用户将数据库的字段映射给对象。
    • 必须能够通过一级C#表示来查询。例如,查询不一定要放在双引号里,比如SQL查询所要求的双引号,而且应该是类型安全的。
    • 必须要有一种特别的查询机制。这一机制要求查询必须放在双引号里,而且不用是类型安全的。
    • 必须能够从数据库里删除对象,提供一个“深度删除(deep delete)”的选项。
    • 必须能够更新数据库里的对象。

    很显然,企业级OODB的要求会严格得多,而且会有其他的特性,比如索引、触发器和并发等。本文所呈现的(核心思想)是充分利用.NET框架内置的功能就可以创建一个简单的OODB。我们在这里不是要颠覆性地创建一个OODB,而是要显示利用一点点创造力能够实现什么。

    如何保存数据

    将对象保存在数据库里我们有很多选择。其中最容易想到的一个是使用二进制序列化将其序列化。这让我们能够利用内置的二进制序列化(System.Runtime.Serialization命名空间)的优势,并使得保存/获取对象变得轻而易举。但是如果采取这种方式,我们就不得不需要编写搜索例程来搜索数据了。我们还必须面对对象以及对象类型相互隔离的状况。由于这些限制以及项目目标(的要求),二进制序列化对于我们来说不是最佳的选择。

    第 二种选择是XML序列化。XML序列化提供了很多二进制序列化的优势,以及一些有趣的功能。与二进制序列化比较起来,XML序列化最大的一个优势 是对象保存在XML里。这意味着这些对象的搜索以及对象/对象类型的隔离已经在极大程度上被处理了。由于这些原因,XML序列化就是我们将要在这个项目里 使用的方式,它能够满足第1项和第2项要求,并能够让我们轻轻松松就实现其他的要求。

    关于XML序列化要知道的一件事情是,它一次只能够序列化/反序列化一个父对象。这意味着我们将需要在同一个文件里用一个XML布局保存多个父对象。这一问题可以通过将StringReaderStringWriter对象当作流而不是对象使用来解决。一旦我们把经过序列化的对象放到字符串里,我们就可以使用一个XmlDocument对象把对象的数据添加到数据库的XML文件里。要获得更多关于XML序列化的信息,请参阅我的XML序列化文章

    一个带有两个Customer(用户)对象的简单数据库看起来就像列表A一样。

    列表A

    <Database>
      <XmlDB.Order>
        <Order xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                       xmlns:xsd="http://www.w3.org/2001/XMLSchema">
          <Identity>76a0558b-a8c7-42e3-8f1d-c56319365787</Identity>
          <CustomerIdentity>6f5e9a2b-b68f-4b6d-9298-fbe5f135dd25</CustomerIdentity>
          <DatePlaced>2006-11-21T07:12:16.3176493-05:00</DatePlaced>
        </Order>
        <Order xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                       xmlns:xsd="http://www.w3.org/2001/XMLSchema">
          <Identity>16d8f0b8-46c6-47c3-ac6b-a0b0e0852970</Identity>
          <CustomerIdentity>61cf2db4-0071-4380-83df-65a102d82ff2</CustomerIdentity>
          <DatePlaced>2006-11-21T07:12:26.0533326-05:00</DatePlaced>
        </Order>
      </XmlDB.Order>
      <XmlDB.Customer>
        <Customer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:xsd="http://www.w3.org/2001/XMLSchema">
          <Identity>6f5e9a2b-b68f-4b6d-9298-fbe5f135dd25</Identity>
          <LastName>Cunningham</LastName>
          <FirstName>Marty</FirstName>
        </Customer>
        <Customer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
          <Identity>61cf2db4-0071-4380-83df-65a102d82ff2</Identity>
          <LastName>Smith</LastName>
          <FirstName>Zach</FirstName>
        </Customer>
      </XmlDB.Customer>
    </Database>

    正如你能看到的,这个格式允许我们快速地分析XML,并找到对象类型的不同分组。

    既然我们已经决定用XML序列化来保存对象,那么我们就需要找一种在内存中保存数据的方式,以便可以让用户与其交互。实现这一目标的一个显而易见的选择是XmlDocument类,因为它为XML文件提供了搜索和更新能力。

    体系结构

    在开始深入代码的细节之前,我们需要看看我们的解决方案背后的体系结构。其基本的体系结构由两个类和一个接口构成:

    类:

    • XmlDBState ——这是一个抽象类,包括数据库所需要的所有功能。其中有搜索、保存、删除,以及文件管理/创建功能。
    • XmlDBBase——这是一个公共类,专门用作要被保存到数据库里的对象的基类。虽然我们并不要求对象继承自这个类,但是继承自XmlDBBase会自动地实现IxmlSerializable接口并保存编码时间。

    接口

    • IXmlSerializable——任何要被保存到数据库里的对象都必须实现这个接口。正如上面所提到的,如果一个对象继承自XmlDBBase,那么它就已经实现了这个接口,因此把对象保存到数据库里不需要采取进一步的操作。

    既然已经搭好了基本的体系结构,我们就可以开始研究源代码,看看这个数据库是如何工作的了。

    加载数据库

    下面的XML(列表A)是在数据库被写入磁盘时的样子,仅供参考:

    列表A

    <Database>
      <XmlDB.Order>
        <Order xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                       xmlns:xsd="http://www.w3.org/2001/XMLSchema">
          <Identity>76a0558b-a8c7-42e3-8f1d-c56319365787</Identity>
          <CustomerIdentity>6f5e9a2b-b68f-4b6d-9298-fbe5f135dd25</CustomerIdentity>
          <DatePlaced>2006-11-21T07:12:16.3176493-05:00</DatePlaced>
        </Order>
        <Order xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                       xmlns:xsd="http://www.w3.org/2001/XMLSchema">
          <Identity>16d8f0b8-46c6-47c3-ac6b-a0b0e0852970</Identity>
          <CustomerIdentity>61cf2db4-0071-4380-83df-65a102d82ff2</CustomerIdentity>
          <DatePlaced>2006-11-21T07:12:26.0533326-05:00</DatePlaced>
        </Order>
      </XmlDB.Order>
      <XmlDB.Customer>
        <Customer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:xsd="http://www.w3.org/2001/XMLSchema">
          <Identity>6f5e9a2b-b68f-4b6d-9298-fbe5f135dd25</Identity>
          <LastName>Cunningham</LastName>
          <FirstName>Marty</FirstName>
        </Customer>
        <Customer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
          <Identity>61cf2db4-0071-4380-83df-65a102d82ff2</Identity>
          <LastName>Smith</LastName>
          <FirstName>Zach</FirstName>
        </Customer>
      </XmlDB.Customer>
    </Database>

    在本文的示例里,有两个客户和两个订单被保存在数据库里。保存在数据库的每个对象类型都被包含在一个节点里,这个节点专门用于特定类型的对象。例如,DatabaseXmlDB.Order (Database[namespace].[type])节点就包含有所有已经被保存的Order(订单)对象。

    在每个专用的类型节点里都有保存序列化对象的对象节点。列表B就是这样一个例子。


    列表B

        <Order xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                       xmlns:xsd="http://www.w3.org/2001/XMLSchema">
          <Identity>16d8f0b8-46c6-47c3-ac6b-a0b0e0852970</Identity>
          <CustomerIdentity>61cf2db4-0071-4380-83df-65a102d82ff2</CustomerIdentity>
          <DatePlaced>2006-11-21T07:12:26.0533326-05:00</DatePlaced>
        </Order>

    这只是序列化形式的Order对象。

    要加载XML数据库,我们就要创建一个XmlDocument对象,并调用它的“Load(加载)”方法来加载XML。这个功能由XmlDBState类的“OpenDatabase”函数来实现。(列表C

    列表C

        public static void OpenDatabase(string path)
        {
            //Set the path to the database file.
            _path = path;

            //Douse the database file already exist?
            if (File.Exists(path))
                Database.Load(path); //If so, load it.

            //If a main node already exists in the database
            // use it. If not, create it.
            MainNode = (Database.ChildNodes.Count > 0) ?
                Database.SelectSingleNode(rootName) :
                Database.CreateElement(rootName);

            //If the main node doesn't exist, add it.
            if (Database.SelectSingleNode(rootName) == null)
                Database.AppendChild(MainNode);
        }

    这个函数的一个很有意思的特性是,如果数据库文件不存在,它会在内存中自动地创建一个数据库来使用。当对象被保存并被保持到磁盘之后,内存中的数据库就会被写到磁盘上。

    上面代码中参考的“MainNode(主节点)”指的是XML的“Database(数据库)”节点。所有的对象都被保存在这个节点下面,它被认为是XML文档的“根节点”。


    保存对象

    在数据库被加载到XmlDBState对象之后,我们就希望将对象保存在它里面。这也可以通过XmlDBState用一个叫做“SaveObject”的方法来处理。SaveObject的代码见列表D

    列表D

    public static void SaveObject(IXmlDBSerializable data, bool persistData)
    {
        Type type = data.GetType();
        string typeString = type.ToString();
        PropertyInfo[] properties = type.GetProperties();

        //Remove the object if it's currently in the database.
        XmlNode typeNode = RemoveCurrentObject(typeString, data);

        //Loop through each property in our object and see
        // if we need to save them in the database.
        foreach (PropertyInfo property in properties)
        {
            //Get the property's value.
            object propertyValue = property.GetValue(data, null);

            //Check to see if the property is IXmlDBSerializable,
            // and if it is save it to the database.
            if (propertyValue is IXmlDBSerializable)
                ((IXmlDBSerializable)propertyValue).Save(persistData);
            else if (propertyValue is System.Collections.ICollection)
            {
                //This property is a collection of objects.
                // We need to see if this collection contains
                // IXmlDBSerializable objects, and serialize
                // those objects if needed.
                IList propertyList = propertyValue as IList;

                //Does the collection contain IXmlDBSerializable
                // objects?
                if (propertyList != null &&
                    propertyList.Count > 0 &&
                    propertyList[0] is IXmlDBSerializable)
                {
                    //It does contain IXmlDBSerializable objects
                    // so save each of them.
                    foreach (object listObject in propertyList)
                        ((IXmlDBSerializable)listObject).Save(persistData);
                }
            }
        }

        //If the type which is being saved isn't currently
        // represented in the database, create a place to
        // hold that specific type.
        if (typeNode == null)
        {
            typeNode = XmlDBState.Database.CreateElement(typeString);
            XmlDBState.MainNode.AppendChild(typeNode);
        }

        //Prepare the objects we will need for serializing
        // the object.
        XmlSerializer serializer = new XmlSerializer(type);
        StringWriter writer = new StringWriter();
        XmlDocument objectDocument = new XmlDocument();

        //Serialize the object into our StringWriter object.
        serializer.Serialize(writer, data);

        //Create an XmlDocument from our serialized object.
        objectDocument.InnerXml = writer.ToString();

        //If the serialized object had data, import it into
        // the database.
        if (objectDocument.ChildNodes.Count > 0)
        {
            //Set the object's Node property to the serialized
            // data.
            data.Node =
                XmlDBState.Database.ImportNode(objectDocument.ChildNodes[1],
                                                true);
            //Append the serialized object to the type node.
            typeNode.AppendChild(data.Node);
        }

        //If requested, persist these changes to the XML file
        // held on disk. If this is not called, the change is
        // made in memory only.
        if (persistData)
            XmlDBState.Database.Save(XmlDBState.Path);
    }

    这个函数毫无疑问地担当着数据库最重要的功能。下面就是这个函数从开始到结束所经历的步骤。

    • 如果对象在数据库里存在的话,它就会把这个对象删除。我们这样做的理由是,在数据库里删除序列化对象并重新保存它要比(直接)更新序列化对象容易。被调用的RemoveCurrentObject函数还会返回XmlNode,负责存留我们正在保存的对象类型。如果看一下上面的示例数据库,你会看到XmlDB.OrderXmlDB.Customer这两个节点——它们都是类型节点,一个用于保存Order对象,另一个用于保存Custom对象。
    • 在删除了当前对象之后,我们需要使用反射来检查正在被保存的对象。只有这样做我们才能够把任何子对象/集合以及主对象保存到数据库里。如果我们发现对象有必须保存的子级,那么子级就要被明确地送到IXmlDBSerializable,用来调用Save()方法。
    • 下一步,我们检查typeNode是否存,如果不存在,我们就创建一个。
    • 然后我们必须创建序列化形式的对象。使用XmlSerializer对象把对象序列化成为StringWriter对象就行了。这让我们可以通过String(字符串)访问序列化对象,然后把它导入一个临时的XmlDocument。我们把它导入XmlDocument,这样它就能够被导入到主数据库XmlDocument里了。利用主XmlDocument对象的ImportNodeAppendChild方法就能够实现这一目标。
    • 最后,如果需要的话,我们要把数据库保持到硬盘上。调用XmlDocument.Save方法并把数据库的路径作为参数传递可以达到这一目标。这会使得XmlDocument覆盖磁盘上的当前内容。把数据库写回到磁盘上的过程很缓慢,这就是为什么我们可以选择不保持数据的原因。例如,如果我们要保存10,000个对象,将这10,000保存到内存里(Save(false))然后调用XmlDBBase.Database.Save(XmlDBBase.Path)要比仅仅对10,000个对象调用Save(true)快得多。

    删除对象

    对象的删除由XmlDBState的两个函数来处理——DeleteRemoveCurrentObjectDelete的代码见列表E

    列表E:

        public static void Delete(IXmlDBSerializable data, bool deep)
        {
            //If this is a "deep delete", we look through the object's
            // properties and delete all child objects from the database.
            if (deep)
            {
                PropertyInfo[] properties = data.GetType().GetProperties();

                foreach (PropertyInfo property in properties)
                {
                    object propertyValue = property.GetValue(data, null);

                    if (propertyValue is IXmlDBSerializable)
                        Delete((IXmlDBSerializable)propertyValue, true);
                    else if (propertyValue is System.Collections.ICollection)
                    {
                        IList propertyList = propertyValue as IList;

                        if (propertyList != null &&
                            propertyList.Count > 0 &&
                            propertyList[0] is IXmlDBSerializable)
                            foreach (object listObject in propertyList)
                                Delete((IXmlDBSerializable)listObject, true);
                    }
                }
            }

            //Remove the object from the database.
            XmlDBState.RemoveCurrentObject(data.GetType().ToString(), data);

            //Persist the database to disk.
            XmlDBState.Database.Save(XmlDBState.Path);
        }

    正如你看到的,Delete在内部使用RemoveCurrentObject,但是还提供了“深度删除(deep delete)”的选项。这意味着正在被删除的对象的每个子对象也要被从数据库里删除。RemoveCurrentObject的代码见列表F

    列表F:

        public static XmlNode RemoveCurrentObject(string typeString, IXmlDBSerializable data)
        {
            //Find the node that holds this type's data.
            XmlNode typeNode = XmlDBState.MainNode.SelectSingleNode(typeString);

            //If the object has a node associated with it, remove
            // the node from the database.
            if (data.Node != null)
                typeNode.RemoveChild(data.Node);

            //Return the node that is responsible for this type's
            // data.
            return typeNode;
        }

    RemoveCurrentObject的基本功能是发现数据库当前对象里的对象,并使用类型的XmlNodeRemoveChild方法从数据库里删除序列化对象。这是一个对内存进行的操作,这就是为什么Delete方法要用额外的步骤调用XmlDBState.Database.Save把更改的内容保持到磁盘上的原因。

    谓词查询

    通过向用户提供使用Predicate(谓词)方法搜索数据库的选项,我们能够实现一个类型安全的、集成C#的查询机制。这项功能通过Search(搜索)方法的重载在数据库里实现(列表G)。

    列表G

        public static List<DynamicType> Search<DynamicType>(
                                        Predicate<DynamicType> searchFunction)
                                        where DynamicType : IXmlDBSerializable
        {
            //Get the Type of the object we're searching for.
            Type type = typeof(DynamicType);

            //Get the nodes of those objects in our database.
            XmlNodeList nodeList =
                XmlDBState.Database.SelectNodes(String.Format(@"/Database//",
                                                type.FullName, type.Name));

            //Get a collection of DynamicType objects via the
            // ExtractObjectsFromNodes method.
            List<DynamicType> matches = ExtractObjectsFromNodes<DynamicType>(nodeList);

            //Use the List<T>.FindAll method to narrow our results
            // to only what was searched for.
            return matches.FindAll(searchFunction);
        }

    这个函数会选择数据库里给定类型的所有节点,使用ExtractObjectsFromNodes将节点反序列化,然后使用.NET框架提供的List<T>.FindAll方法过滤集合。ExtractObjectsFromNodes的代码见列表H

    列表H

        private static List<DynamicType> ExtractObjectsFromNodes<DynamicType>(
    XmlNodeList nodeList)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(DynamicType));
            List<DynamicType> objects = new List<DynamicType>();

            foreach (XmlNode node in nodeList)
            {
                StringReader reader = new StringReader(node.OuterXml);
                DynamicType deserialized = (DynamicType)serializer.Deserialize(reader);
                ((IXmlDBSerializable)deserialized).Node = node;

                objects.Add(deserialized);
            }

            return objects;
        }

    这个方法只会在我们XmlNodeList的每个节点里循环,将每个对象反序列化成为活的业务对象。它然后把对象添加到List<T>集合里,在循环结束的时候返回集合。

    这 种类型的查询要求我们将保存在数据库里的被请求类型的所有实例都反序列化,知道这一点是十分重要的。这意味着如果数据库里有10,000个 Customer对象,我们要使用Predicate查询过滤它们,那么这10,000对象都必须被反序列化,然后数据库才能够开始过滤结果。这显然是一 个十分耗时的过程,这就是为什么我们提供了一个基于XPath的替代查询机制的原因。

    XPath查询

    正如我们在本系列的上篇中提到的,使用XML保存对象的一个主要优势是我们可以将XPath用作是一种特别的查询机制。这一机制由XmlDBState类通过Search方法的重载来实现,见列表I

    列表I

        public static List<DynamicType> Search<DynamicType>(
                                        string query)
                                        where DynamicType : IXmlDBSerializable
        {
            //Get the Type of the object we're searching for.
            Type type = typeof(DynamicType);
            //Create a List<DynamicType> collection to hold the results.
            List<DynamicType> matches = new List<DynamicType>();

            //Change single quotes to double quotes in the query.
            query = query.Replace("'", """);

            //Build our XPath query.
            string xpath = "Database/" + type.FullName + "/" +
                            type.Name + "[" + query + "]";

            try
            {
                //Select all nodes which match out XPath query.
                XmlNodeList nodes = XmlDBState.Database.SelectNodes(xpath);

                //If we have results, extract objects from those nodes.
                if (nodes != null)
                    matches = ExtractObjectsFromNodes<DynamicType>(nodes);
            }
            catch (Exception exception)
            {
                throw new Exception("Could not search. Possible bad query syntax?",
    exception);
            }

            return matches;
        }

    要注意的是,在使用XPath查询的时候我们只将我们知道符合查询条件的对象反序列化。这会极大地提高查询执行的速度。在测试中,Predicate查询返回10,000个对象需要一秒钟,而XPath查询只需要百分之一秒。

    还要注意的是,在这个方法的开始,我们用双引号替换掉了单引号。这样做的目的是用户不用在需要在它们的查询里转义双引号。但是这种做法存在一些副作用,因为单引号是合法的XPath字符,某些实例可能会要求使用它们。使用它只是为了实现可用性而做出的一种让步。

    IXmlDBSerializable接口

    IXmlDBSerializable接口必须通过要被保存到数据库里的任何对象来实现。这使得数据库能够对所有对象都一视同仁,避免了人们去猜测某个对象是否能够被保存到数据库里。IXmlDBSerializable的代码见列表J

    列表J

    namespace XmlDBLibrary
    {
        public interface IXmlDBSerializable
        {
            System.Guid Identity { get; set; }
            System.Xml.XmlNode Node { get; set; }
            void Save(bool persistData);
        }
    }

    我们需要Identity(标识)属性是因为每个被保存到数据库的对象都必须有唯一能够被识别的标记。这一属性应该自动地被生成,用于实现IXmlDBSerializable的任何对象。

    Node属性被用来把XmlNode保存到与当前对象对应的数据库里。这让数据库能够快速地删除对象,而不需要搜索对象的标识,还没有被保存到数据库里的对象的这一属性为空。

    我们还需要Save(保存)方法,这样XmlDBState.SaveObject方法就能够保存正在被保存的对象的子对象。通过将把子对象传给IXmlDBSerializable并对子对象调用Save,我们就能够实现这一目标。

    XmlDBBase类

    当对象需要符合IXmlDBSerializable 要求的时候,XmlDBBase类被当为开发人员所使用的一种快捷方式。这并不是一个必需的类,因为IXmlDBSerializable可以手动实现。当被用作一个基类时,XmlDBBase还提供了保存、搜索和删除功能。XmlDBBase的代码见列表K

    列表K

        public class XmlDBBase : IXmlDBSerializable
        {
            private XmlNode _node = null;
            private System.Guid _identity = System.Guid.NewGuid();

            public XmlDBBase()
            {
            }

            public void Delete()
            {
                this.Delete(false);
            }

            public void Delete(bool deep)
            {
                XmlDBState.Delete(this, deep);
            }

            public void Save()
            {
                Save(true);
            }

            public void Save(bool persistData)
            {
                XmlDBState.SaveObject(this, persistData);
            }

            public static List<DynamicType> Search<DynamicType>(
                                System.Predicate<DynamicType> searchFunction)
                                where DynamicType : IXmlDBSerializable
            {
                return XmlDBState.Search<DynamicType>(searchFunction);
            }

            public static List<DynamicType> Search<DynamicType>(
                                string query)
                                where DynamicType : IXmlDBSerializable
            {
                return XmlDBState.Search<DynamicType>(query);
            }

            public static DynamicType GetSingle<DynamicType>(
                                System.Predicate<DynamicType> searchFunction)
                                where DynamicType : IXmlDBSerializable
            {
                List<DynamicType> results =
                      XmlDBState.Search<DynamicType>(searchFunction);

                return (results.Count == 0) ? default(DynamicType) : results[0];
            }

            public System.Guid Identity
            {
                get { return _identity; }
                set { _identity = value; }
            }

            [System.Xml.Serialization.XmlIgnore]
            public XmlNode Node
            {
                get { return _node; }
                set { _node = value; }
            }
        }

    要注意的是,这些方法都可以同时用来进行XPath和Predicate查询。它们也提供了Delete(删除)和Save功能。这些方法中的大多数基本上都是传给XmlDBState对象的传递方法。

    有一个很有意思的事情需要指出,那就是对Node属性使用XmlIgnore,使用它是因为我们不希望Node属性被序列化到数据库里。如果将这个属性序列化,我们事实上就是在把序列化对象的一个副本保存在这个对象里。

    最终的想法

    虽 然这个数据库不是一个企业级的面向对象的数据库,但是我相信这是体现.NET框架灵活性的一个好例子。让这个数据库工作起来只需要非常少的代码, 所需要的代码基本上只是用来将各种不同的.NET框架的功能关联起来。这个数据库的每个主要函数都已经由.NET框架提供——从用于查询的 Predicates/XPath,到用于数据管理的XmlDocument对象。

     
    展开全文
  • 不要让ORM或库受阻,让您在应用程序中编写的代码尽可能地好。 支持: Kotlin:使用该语言构建库非常简洁,空值安全且高效。 协程:为查询添加协程支持。 RX Java:通过监听数据库更改并确保您订户是最新...
  • 这是用jdbc连接数据库编写食堂管理系统,是我第次做的简单项目,只是次练习,经验不够,代码不够好,冗余比较多,欢迎大佬在评论区指出,本人也在不断学习中。 这项目我写了两天时间,代码不难,...

    这是用jdbc连接数据库,编写食堂的管理系统,是我第一次做的简单项目,只是一次练习,经验不够,代码写的不够好,冗余比较多,欢迎大佬在评论区指出,本人也在不断学习中。

    这个项目我写了两天的时间,代码不难,就是逻辑有些绕。
    在这里插入图片描述

    有些地方的bug想的也很头秃
    在这里插入图片描述

    不多说了,这是我的目录
    在这里插入图片描述

    1.首先构建表

    在这里插入图片描述
    在这里插入图片描述

    2.将数据库的一些通用代码整合到一个类中

    package com.etc.dao;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    public class BaseDao {
    		//连接数据库
    		private static final String DRIVER="com.mysql.jdbc.Driver";
    		private static final String URL="jdbc:mysql://localhost:3306/canteen";
    		private static final String USERNAME="root";
    		private static final String USERPWD="root";
    	
    		// Connection
    		public static Connection getConnection() {
    			try {
    				Class.forName(DRIVER);
    			} catch (ClassNotFoundException e1) {
    				e1.printStackTrace();
    			}
    			Connection conn=null;
    			try {
    				conn = DriverManager.getConnection(URL, USERNAME, USERPWD);
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    			return conn;
    		}
    		//PreparedStatement
    		public static PreparedStatement setParam(Connection conn,String sql,Object...param) throws SQLException{
    			PreparedStatement pstmt=null;
    			pstmt=conn.prepareStatement(sql);
    			for(int i=0;i<param.length;i++) {
    				pstmt.setObject(i+1, param[i]);
    			}
    			return pstmt;
    		}
    		
    		//ResultSet
    		public static int exeUpdate(PreparedStatement pstmt) throws SQLException  {
    			int result=0;
    			result=pstmt.executeUpdate();
    			return result;
    			
    		}
    		//关闭连接
    		public static void closeAll(Connection conn,PreparedStatement pstmt,ResultSet rs) {
    			try {
    				if(pstmt!=null) {
    					pstmt.close();
    				}
    				if(conn!=null) {
    					conn.close();
    				}
    				if(rs!=null) {
    					rs.close();
    				}
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    		}	
    }
    
    

    3.编写用户类,顾客类和菜品类

    用户类

    package com.etc.entity;
    
    public class User {
    	//定义变量
    	private int UserId;
    	private String UserName;
    	private String Password;
    	private String Address;
    	public User() {
    	}
    	//构造方法
    	public User(int userId, String password) {
    		super();
    		UserId = userId;
    		Password = password;
    	}
    	public User(int userId, String userName, String password,String address) {
    		super();
    		UserId = userId;
    		UserName = userName;
    		Password=password;
    		Address = address;
    	}
    	//重写toString
    	@Override
    	public String toString() {
    		return "User [UserId=" + UserId + ", UserName=" + UserName + ", Password=" + Password + ", Address=" + Address
    				+ "]";
    	}
    	//set和get方法
    	public String getPassword() {
    		return Password;
    	}
    	public void setPassword(String password) {
    		Password = password;
    	}
    	public int getUserId() {
    		return UserId;
    	}
    	
    	public void setUserId(int userId) {
    		UserId = userId;
    	}
    	public String getUserName() {
    		return UserName;
    	}
    	public void setUserName(String userName) {
    		UserName = userName;
    	}
    	public String getAddress() {
    		return Address;
    	}
    	public void setAddress(String address) {
    		Address = address;
    	}
    }
    

    管理员类

    package com.etc.entity;
    
    public class Manager {
    	private int ManagerId;
    	private String ManagerName;
    	private String Password;
    	public Manager() {
    		
    	}
    	//构造方法
    	public Manager(int managerId, String password) {
    		super();
    		ManagerId = managerId;
    		Password = password;
    	}
    	public Manager(int managerId, String managerName, String password) {
    		super();
    		ManagerId = managerId;
    		ManagerName = managerName;
    		Password = password;
    	}
    	//重写toString
    	@Override
    	public String toString() {
    		return "Manager [ManagerId=" + ManagerId + ", ManagerName=" + ManagerName + ", Password=" + Password + "]";
    	}
    	//set和get方法
    	public int getManagerId() {
    		return ManagerId;
    	}
    	public void setManagerId(int managerId) {
    		ManagerId = managerId;
    	}
    	public String getManagerName() {
    		return ManagerName;
    	}
    	public void setManagerName(String managerName) {
    		ManagerName = managerName;
    	}
    	public String getPassword() {
    		return Password;
    	}
    	public void setPassword(String password) {
    		Password = password;
    	}
    }
    
    

    菜品类

    package com.etc.entity;
    
    public class Dish {
    	private int DishId;
    	private String DishName;
    	private double Price;
    	private String Material;
    	private String Practice;
    	private int Stock;
    	
    	public Dish() {
    		super();
    	}
    	
    	//构造方法
    	public Dish(int dishId, String dishName, double price, String material, String practice, int stock) {
    		super();
    		DishId = dishId;
    		DishName = dishName;
    		Price = price;
    		Material = material;
    		Practice = practice;
    		Stock = stock;
    	}
    
    	//重写toString
    	@Override
    	public String toString() {
    		return "Dish [DishId=" + DishId + ", DishName=" + DishName + ", Price=" + Price + ", Material=" + Material
    				+ ", Practice=" + Practice + ", Stock=" + Stock + "]";
    	}
    	
    	//set和get方法
    	public int getDishId() {
    		return DishId;
    	}
    
    	public void setDishId(int dishId) {
    		DishId = dishId;
    	}
    
    	public String getDishName() {
    		return DishName;
    	}
    
    	public void setDishName(String dishName) {
    		DishName = dishName;
    	}
    
    	public double getPrice() {
    		return Price;
    	}
    
    	public void setPrice(double price) {
    		Price = price;
    	}
    
    	public String getMaterial() {
    		return Material;
    	}
    
    	public void setMaterial(String material) {
    		Material = material;
    	}
    
    	public String getPractice() {
    		return Practice;
    	}
    
    	public void setPractice(String practive) {
    		Practice = practive;
    	}
    
    	public int getStock() {
    		return Stock;
    	}
    
    	public void setStock(int stock) {
    		Stock = stock;
    	}
    }
    
    

    4.之后就是写接口层和实现接口

    接口层

    package com.etc.dao;
    
    import java.util.List;
    
    import com.etc.entity.Dish;
    import com.etc.entity.Manager;
    import com.etc.entity.User;
    
    
    public interface DishDao {
    	//菜品添加
    	void dishAdd(Dish dish);
    	//菜品删除
    	void dishDel(int dishId);
    	//菜品修改
    	void dishUpdate(Dish dish);
    	//菜品购买
    	double dishbuy(int dishId);
    	//地址修改
    	void addressupd(int userId,String password,String address);
    	//菜品查询
    	List<Dish> dishQuery();
    	//管理员注册
    	boolean managerRegister(Manager manager);
    	//管理员登录
    	boolean managerLogon(Manager manager);
    	//用户注册
    	boolean userRegister(User user);
    	//用户登录
    	boolean userLogon(User user);
    	//地址查询
    	void addrque(int userId);
    }
    

    实现接口

    有点多,不过没有难点。

    package com.etc.dao.impl;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.List;
    
    import com.etc.dao.BaseDao;
    import com.etc.dao.DishDao;
    import com.etc.entity.Dish;
    import com.etc.entity.Manager;
    import com.etc.entity.User;
    
    public class DishDaoImpl implements DishDao {
    
    	// 添加菜品
    	@Override
    	public void dishAdd(Dish dish) {
    		String sql = "INSERT INTO menu(DishId,DishName,Price,Material,Practice,Stock) VALUES(?,?,?,?,?,?)";
    		Connection conn = null;
    		PreparedStatement pstmt = null;
    		try {
    			conn = BaseDao.getConnection();
    			pstmt = BaseDao.setParam(conn, sql, dish.getDishId(), dish.getDishName(), dish.getPrice(),
    					dish.getMaterial(), dish.getPractice(), dish.getStock());
    			int result = BaseDao.exeUpdate(pstmt);
    			if (result > 0) {
    				System.out.println("插入成功");
    			} else {
    				System.out.println("插入失败");
    			}
    		} catch (SQLException e) {
    			e.printStackTrace();
    		} finally {
    			BaseDao.closeAll(conn, pstmt, null);
    		}
    	}
    
    	// 删除菜品
    	@Override
    	public void dishDel(int dishId) {
    		String sql = "delete from menu where DishId=?";
    		Connection conn = null;
    		PreparedStatement pstmt = null;
    		try {
    			conn = BaseDao.getConnection();
    			pstmt = BaseDao.setParam(conn, sql, dishId);
    			int result = pstmt.executeUpdate();
    			if (result > 0) {
    				System.out.println("删除成功");
    			} else {
    				System.out.println("删除失败");
    			}
    		} catch (SQLException e) {
    			e.printStackTrace();
    		} finally {
    			BaseDao.closeAll(conn, pstmt, null);
    		}
    
    	}
    
    	// 修改菜品
    	@Override
    	public void dishUpdate(Dish dish) {
    		String sql = "UPDATE menu SET dishName=? , Price=?,Material=?,Practice=?,Stock=? WHERE dishId=?";
    		Connection conn = null;
    		PreparedStatement pstmt = null;
    		String sql1 = "SELECT * from menu";
    		int []list = new int[100];	//存放查询出的dishId
    		ResultSet rs = null;
    		int i=0;
    		int flag=0;
    		//获取已存在菜品编号
    		try {
    			conn = BaseDao.getConnection();
    			pstmt = BaseDao.setParam(conn, sql1);
    			rs = pstmt.executeQuery();
    			int dishId =0;
    			while (rs.next()) {
    				dishId = rs.getInt(1);
    				list[i]=dishId;
    				i++;
    			}
    		} catch (SQLException e) {
    			e.printStackTrace();
    		} finally {
    			BaseDao.closeAll(conn, pstmt, rs);
    		}
    		//重置下标
    		i=0;
    		while(list[i]!=0) {
    			if(dish.getDishId()==list[i]) {
    				flag=1;
    				break;
    			}
    			i++;
    		}
    		//判断菜品编号是否存在
    		if(flag==1) {
    			try {
    				conn = BaseDao.getConnection();
    				pstmt = BaseDao.setParam(conn, sql, dish.getDishName(), dish.getPrice(),
    						dish.getMaterial(), dish.getPractice(), dish.getStock(),dish.getDishId());
    				int result = pstmt.executeUpdate();
    				if (result > 0) {
    					System.out.println("修改成功");
    				} else {
    					System.out.println("修改失败");
    				}
    			} catch (SQLException e) {
    				e.printStackTrace();
    			} finally {
    				BaseDao.closeAll(conn, pstmt, null);
    			}
    		}else if(flag==0) {
    			System.out.println("编号不存在,请重输!");
    		}
    	}
    	//购买菜品
    	public double dishbuy(int dishId) {
    		String sql = "UPDATE menu SET stock=stock-1 WHERE dishId=?";
    		Connection conn = null;
    		PreparedStatement pstmt = null;
    		String sql1 = "SELECT * from menu";
    		int[] listId = new int[100];
    		int[] liststock=new int[100];
    		double[] price=new double[100];
     		ResultSet rs = null;
    		int i=0;
    		int flag=0;
    		//获取已存在菜品编号
    		//判断是否菜品编号已存在
    		try {
    			conn = BaseDao.getConnection();
    			pstmt = BaseDao.setParam(conn, sql1);
    			rs = pstmt.executeQuery();
    			while (rs.next()) {
    				listId[i]= rs.getInt(1);
    				price[i]=rs.getDouble("Price");
    				liststock[i]=rs.getInt("Stock");
    				i++;
    			}
    		} catch (SQLException e) {
    			e.printStackTrace();
    		} finally {
    			BaseDao.closeAll(conn, pstmt, rs);
    		}
    		//重置数组下标
    		i=0;
    		//判断是否菜品编号已存在
    		while(listId[i]!=0 ) {
    			if(dishId==listId[i] && liststock[i]>0) {
    				flag=1;
    				break;
    			}
    			i++;
    		}
    		if(flag==1) {
    			try {
    				conn = BaseDao.getConnection();
    				pstmt = BaseDao.setParam(conn, sql, dishId);
    				int result = pstmt.executeUpdate();
    				if (result > 0) {
    					System.out.println("购买成功");
    					return price[dishId-1];
    				} else {
    					System.out.println("购买失败");
    				}
    			} catch (SQLException e) {
    				e.printStackTrace();
    			} finally {
    				BaseDao.closeAll(conn, pstmt, null);
    			}
    		}else if(flag==0) {
    			System.out.println("编号不存在或该菜已售完,请重输!");
    		}
    		return 0;
    	}
    
    	// 查询菜品
    	@Override
    	public List<Dish> dishQuery() {
    		String sql = "SELECT * from menu";
    		List<Dish> list = new ArrayList<Dish>();
    		Connection conn = null;
    		PreparedStatement pstmt = null;
    		ResultSet rs = null;
    		try {
    			conn = BaseDao.getConnection();
    			pstmt = BaseDao.setParam(conn, sql);
    			rs = pstmt.executeQuery();
    			while (rs.next()) {
    				int dishId = rs.getInt(1);
    				String dishName = rs.getString("DishName");
    				double price = rs.getDouble("Price");
    				String material = rs.getString("Material");
    				String practice = rs.getString("Practice");
    				int stock = rs.getInt("Stock");
    				System.out.println("菜品编号:" + dishId + " 菜品名: " + dishName + " 价格: " + price + " 原材料: " + material
    						+ " 做法: " + practice + " 库存: " + stock);
    				list.add(new Dish(dishId, dishName, price, material, practice, stock));
    			}
    		} catch (SQLException e) {
    			e.printStackTrace();
    		} finally {
    			BaseDao.closeAll(conn, pstmt,rs);
    		}
    		return list;
    	}
    
    	// 管理员注册
    	@Override
    	public boolean managerRegister(Manager manager) {
    		String sql = "INSERT INTO Manager(ManagerId,ManagerName,Password) VALUES(?,?,?)";
    		Connection conn = null;
    		PreparedStatement pstmt = null;
    		
    		String sql1 = "SELECT * from manager";
    		int []list = new int[100];
    		ResultSet rs = null;
    		int i=0;
    		int flag=0;
    		//判断是否编号已存在
    		try {
    			conn = BaseDao.getConnection();
    			pstmt = BaseDao.setParam(conn, sql1);
    			rs = pstmt.executeQuery();
    			while (rs.next()) {
    				int dishId = rs.getInt(1);
    				list[i]=dishId;
    				i++;
    			}
    		} catch (SQLException e) {
    			e.printStackTrace();
    		} finally {
    			BaseDao.closeAll(conn, pstmt,rs);
    		}
    		i=0;
    		while(list[i]!=0) {
    			if(manager.getManagerId()==list[i]) {
    				flag=1;
    				break;
    			}
    			i++;
    		}
    		if(flag==0) {
    			try {
    				conn = BaseDao.getConnection();
    				pstmt = BaseDao.setParam(conn, sql, manager.getManagerId(), manager.getManagerName(),
    						manager.getPassword());
    				int result = BaseDao.exeUpdate(pstmt);
    				if (result > 0) {
    					System.out.println("注册成功");
    				} else {
    					System.out.println("注册失败");
    				}
    			} catch (SQLException e) {
    				e.printStackTrace();
    			} finally {
    				BaseDao.closeAll(conn, pstmt, null);
    			}
    			return true;
    		}else if(flag==1) {
    			System.out.println("编号已存在,请重输!");
    		}
    		return false;
    	}
    
    	// 管理员登录
    	public boolean managerLogon(Manager manager) {
    		String sql = "SELECT *  from manager";
    		int flag=0;
    		Connection conn = null;
    		PreparedStatement pstmt = null;
    		ResultSet rs = null;
    		try {
    			conn = BaseDao.getConnection();
    			pstmt = BaseDao.setParam(conn, sql);
    			rs = pstmt.executeQuery();
    			while (rs.next()) {
    				int managerId = rs.getInt(1);
    				String managerName = rs.getString("ManagerName");
    				String password = rs.getString("Password");
    				if(manager.getManagerId()==managerId && manager.getPassword().equals(password)) {	//登录时进行ID和密码比较
    					flag=1;
    					break;
    				}
    			}
    		} catch (SQLException e) {
    			e.printStackTrace();
    		} finally {
    			BaseDao.closeAll(conn, pstmt,rs);
    		}
    		if(flag==1) {
    			System.out.println("登录成功");
    			return true;
    		}
    		System.out.println("输入错误");
    		return false;
    	}
    
    	// 用户注册
    	public boolean userRegister(User user) {
    		String sql = "INSERT INTO user(UserId,UserName,Password,Address) VALUES(?,?,?,?)";
    		Connection conn = null;
    		PreparedStatement pstmt = null;
    		String sql1 = "SELECT * from user";
    		int []list = new int[100];
    		ResultSet rs = null;
    		int i=0;
    		int flag=0;
    		//判断是否编号已存在
    		try {
    			conn = BaseDao.getConnection();
    			pstmt = BaseDao.setParam(conn, sql1);
    			rs = pstmt.executeQuery();
    			while (rs.next()) {
    				int userId = rs.getInt(1);
    				list[i]=userId;
    				i++;
    			}
    		} catch (SQLException e) {
    			e.printStackTrace();
    		} finally {
    			BaseDao.closeAll(conn, pstmt, rs);
    		}
    		i=0;
    		while(list[i]!=0) {
    			if(user.getUserId()==list[i]) {
    				flag=1;
    				break;
    			}
    			i++;
    		}
    		if(flag==0) {
    			try {
    				conn = BaseDao.getConnection();
    				pstmt = BaseDao.setParam(conn, sql, user.getUserId(), user.getUserName(),user.getPassword(), user.getAddress());
    				int result = BaseDao.exeUpdate(pstmt);
    				if (result > 0) {
    					System.out.println("注册成功");
    				} else {
    					System.out.println("注册失败");
    				}
    			} catch (SQLException e) {
    				e.printStackTrace();
    			} finally {
    				BaseDao.closeAll(conn, pstmt, null);
    			}	
    			return true;
    		}else if(flag==1) {
    			System.out.println("编号已存在,请重输!");
    		}
    		return false;
    	}
    
    	// 用户登录
    	public boolean userLogon(User user) {
    		String sql = "SELECT *  from user";
    		int flag=0;
    		Connection conn = null;
    		PreparedStatement pstmt = null;
    		ResultSet rs = null;
    		try {
    			conn = BaseDao.getConnection();
    			pstmt = BaseDao.setParam(conn, sql);
    			rs = pstmt.executeQuery();
    			while (rs.next()) {
    				int userId = rs.getInt(1);
    				String password = rs.getString("Password");
    				if(user.getUserId()==userId && user.getPassword().equals(password)) {	//登录时进行ID和密码比较
    					flag=1;
    					break;
    				}
    			}
    		} catch (SQLException e) {
    			e.printStackTrace();
    		} finally {
    			BaseDao.closeAll(conn, pstmt,rs);
    		}
    		if(flag==1) {
    			System.out.println("登录成功!");
    			return true;
    		}
    		System.out.println("登录失败!");
    		return false;
    	}
    	
    	//修改地址
    	public void addressupd(int userId,String password,String address) {
    		String sql = "UPDATE user SET Address=? WHERE UserId=?";
    		Connection conn = null;
    		PreparedStatement pstmt = null;
    		String sql1 = "SELECT * from user";
    		int[] listId = new int[100];
    		String[] psd=new String[100];
    		String[] addr=new String[100];
     		ResultSet rs = null;
    		int i=0;
    		int flag=0;
    		
    		try {
    			conn = BaseDao.getConnection();
    			pstmt = BaseDao.setParam(conn, sql1);
    			rs = pstmt.executeQuery();
    			while (rs.next()) {
    				listId[i]= rs.getInt(1);   //获取用户id
    				psd[i]=rs.getString("Password");	//获取用户密码
    				addr[i]=rs.getString("Address");	//获取用户地址
    				i++;
    			}
    		} catch (SQLException e) {
    			e.printStackTrace();
    		} finally {
    			BaseDao.closeAll(conn, pstmt, rs);
    		}
    		i=0;
    		while(listId[i]!=0 ) {
    			if(userId==listId[i] && password.equals(psd[i])) {
    				flag=1;
    				break;
    			}
    			i++;
    		}
    		if(flag==1) {
    			try {
    				conn = BaseDao.getConnection();
    				pstmt = BaseDao.setParam(conn, sql, address,userId);
    				int result = pstmt.executeUpdate();
    				if (result > 0) {
    					System.out.println("修改成功");
    				} else {
    					System.out.println("修改失败");
    				}
    			} catch (SQLException e) {
    				e.printStackTrace();
    			} finally {
    				BaseDao.closeAll(conn, pstmt, null);
    			}
    		}else if(flag==0) {
    			System.out.println("输入错误!");
    		}
    	}
    	//根据用户Id查询地址
    	public void addrque(int userId) {
    		String sql = "SELECT * from user";
    		Connection conn = null;
    		PreparedStatement pstmt = null;
    		ResultSet rs = null;
    		int[] listId = new int[100];
    		String[] addr=new String[100];
    		int i=0;
    		int flag=0;
    		try {
    			conn = BaseDao.getConnection();
    			pstmt = BaseDao.setParam(conn, sql);
    			rs = pstmt.executeQuery();
    			while (rs.next()) {
    				listId[i]= rs.getInt(1);   //获取用户id
    				
    				addr[i]=rs.getString("Address");	//获取更新后的地址
    				i++;
    			}
    		} catch (SQLException e) {
    			e.printStackTrace();
    		} finally {
    			BaseDao.closeAll(conn, pstmt, rs);
    		}
    		i=0;
    		while(listId[i]!=0 ) {	//进行用户的ID比较
    			if(userId==listId[i]) {	//id存在则输出
    				System.out.println("您的地址是:"+addr[i]);
    				break;
    			}
    			i++;
    		}
    	}
    
    }
    
    

    5.编写菜单类

    package com.etc.option;
    
    import java.util.Scanner;
    
    import com.etc.dao.DishDao;
    import com.etc.dao.impl.DishDaoImpl;
    import com.etc.entity.Dish;
    import com.etc.entity.Manager;
    import com.etc.entity.User;
    
    
    public class TestMenu {
    	private DishDao dd=new DishDaoImpl();
    	private Dish dish=new Dish();
    	private Manager manager=new Manager();
    	private User user=new User();
    	
    	Scanner sc = new Scanner(System.in);
    	String err=""; // 吸收错误输入
    	public void menu() {
    
    		do {
    			System.out.println("----欢迎食堂管理系统----");
    			System.out.println("1、管理员");
    			System.out.println("2、顾客");
    			System.out.println("3、退出系统");
    			System.out.println("请输入对应菜单序号:(输入1~3的数字)");
    			int checkNum =0;
    			if(sc.hasNextInt()) {	//判断是否输入格式错误
    				checkNum = sc.nextInt();
    			}else {
    				System.out.println("输入错误!");
    				err=sc.next();	//吸收错误
    				menu();
    			}
    			switch (checkNum) {// 选择点击菜单
    			case 1:
    				int flag=0;
    				boolean flagmana=manager();	//判断管理员是否登录成功
    				while(flag==0) {
    					if(flagmana==true) {
    						System.out.println("请输入你要进行的操作:(请输入数字)");
    						System.out.println("1.查看菜品");
    						System.out.println("2.添加菜品");
    						System.out.println("3.修改菜品");
    						System.out.println("4.删除菜品");
    						System.out.println("5.返回主界面");
    						int manachoose=0;
    						if(sc.hasNextInt()) {	//判断是否输入格式错误
    							manachoose=sc.nextInt();
    						}else{
    							System.out.println("输入错误,请输入数字!");
    							err=sc.next();
    							menu();	//返回菜单
    						}
    						switch(manachoose) {
    						case 1:
    							dishque();
    							continue;	//此处不用break,可以让 管理员自主选择退出界面
    						case 2:
    							dishadd();
    							continue;
    						case 3:
    							dishupd();
    							continue;
    						case 4:
    							dishdel();
    							continue;
    						case 5:
    							flag=1;
    							System.out.println("退出成功!");
    							menu();
    							break;
    						default :
    							System.out.println("输入错误!");
    							break;
    						}	
    					}
    					
    				}
    				
    				break;
    			case 2:
    				int flag02=0;
    				boolean userflag=false;	//判断用户是否登录成功
    				user();
    				while(flag02==0) {
    					if(userflag==false) {
    						System.out.println("请输入你要进行的操作:(请输入数字)");
    						System.out.println("1.查看菜品:");
    						System.out.println("2.购买菜品:");
    						System.out.println("3.修改地址:");
    						System.out.println("4.返回主菜单:");
    						int userchoose=0;
    						if(sc.hasNextInt()) {	//判断是否输入格式错误
    							userchoose=sc.nextInt();
    						}else{
    							System.out.println("输入错误,请输入数字!");
    							err=sc.next();
    							menu();	//登录失败,重新回到菜单界面
    						}
    						switch(userchoose) {
    						case 1:
    							dishque();
    							continue;
    						case 2:
    							dishbuy();
    							continue;
    						case 3:
    							addrupd();
    							continue;
    						case 4:
    							flag=1;
    							menu();
    							System.out.println("退出成功!");
    							break;
    						default :
    							System.out.println("输入错误!");
    							break;
    						}	
    					}
    				}
    					break;
    				case 3:
    					System.out.println("退出成功!");
    					System.exit(1);
    					break;
    				default:
    					System.out.println("输入错误!!!");
    					break;
    				}
    			} while (1==1);
    				
    	}
    
    	// 管理员操作
    	public boolean manager() {
    		boolean flag=false;
    		while(flag==false) {
    			System.out.println("1.登录");
    			System.out.println("2.注册");
    			System.out.println("3.返回主菜单");
    			int k=0;
    			if(sc.hasNextInt()) {	//判断是否输入格式错误
    				k=sc.nextInt();
    			}else {
    				System.out.println("输入错误,请输入数字!");
    				err=sc.next();
    				manager();
    			}
    			switch(k) {
    			case 1:	//管理员登录功能
    				System.out.println("请输入编号:(请输入数字)");
    				int managerno=0;
    				if(sc.hasNextInt()) {	//判断是否输入格式错误
    					managerno=sc.nextInt();
    				}else {
    					System.out.println("输入错误,请输入数字!");
    					err=sc.next();
    					manager();
    				}
    				System.out.println("请输入密码:");
    				String password=sc.next();
    				manager=new Manager(managerno,password);
    				if(flag=dd.managerLogon(manager)) {
    					return true;
    				}
    				break;
    			case 2:	//管理员登录注册
    				System.out.println("请输入密钥:");
    				String prpassword=sc.next();
    				if(prpassword.equals("xing1999")) {
    					System.out.println("请输入编号:(请输入数字)");
    					int managerno1=0;
    					if(sc.hasNextInt()) {
    						managerno1=sc.nextInt();
    					}else {
    						System.out.println("输入错误,请输入数字!");
    						err=sc.next();
    						manager();
    					}
    					System.out.println("请输入姓名:");
    					String managerName=sc.next();
    					System.out.println("请输入密码:");
    					String password1=sc.next();
    					manager=new Manager(managerno1,managerName,password1);
    					if(flag=dd.managerRegister(manager)) {
    						return true;
    					}
    				}else {
    					System.out.println("密钥错误!");
    				}
    				break;
    			case 3:
    				flag=true;
    				menu();
    				break;
    			default:
    				System.out.println("输入错误!!!");
    				break;
    			}
    		}
    		return false;
    		
    	}
    	//用户操作
    	public boolean user() {
    		boolean flag=false;
    		while(flag==false) {
    			System.out.println("1.登录");
    			System.out.println("2.注册");
    			System.out.println("3.返回主菜单");
    			int k=0;
    			if(sc.hasNextInt()) {	//判断是否使用数字输入
    				k=sc.nextInt();
    			}else {
    				System.out.println("输入错误,请输入数字!");
    				err=sc.next();
    				user();
    			}
    			switch(k) {
    			case 1:
    				System.out.println("请输入编号(数字):");
    				int userno=0;
    				if(sc.hasNextInt()) {
    					userno=sc.nextInt();
    				}else {
    					System.out.println("输入错误,请输入数字!");
    					err=sc.next();
    					user();
    				}
    				System.out.println("请输入密码:");
    				String password=sc.next();
    				user=new User(userno,password);
    				if(flag=dd.userLogon(user)) {
    					return true;
    				}
    				break;
    			case 2:
    				System.out.println("请输入编号(数字):");
    				int userno1=0;
    				if(sc.hasNextInt()) {
    					userno1=sc.nextInt();
    				}else {
    					System.out.println("输入错误,请输入数字!");
    					err=sc.next();
    					user();
    				}
    				System.out.println("请输入姓名:");
    				String userName=sc.next();
    				System.out.println("请输入密码:");
    				password=sc.next();
    				System.out.println("请输入地址:");
    				String address=sc.next();
    				user=new User(userno1,userName,password,address);
    				if(flag=dd.userRegister(user)) {
    					return true;
    				}
    				break;
    			case 3:
    				flag=true;
    				menu();
    				break;
    			default:
    				System.out.println("输入错误!!!");
    				break;
    			}	
    		}	
    		return false;
    	}
    	//查询菜品
    	public void dishque() {
    		dd.dishQuery();
    	}
    	//添加菜品
    	public void dishadd() {
    		System.out.println("请输入菜品编号:(请输入数字!)");
    		int dishId=sc.nextInt();
    		System.out.println("请输入菜品名:");
    		String dishName=sc.next();
    		System.out.println("请输入价格:");
    		double price=sc.nextDouble();
    		System.out.println("请输入材料:");
    		String material=sc.next();
    		System.out.println("请输入做法:");
    		String practice = sc.next();
    		System.out.println("请输入库存:");
    		int stock=sc.nextInt();
    		dish=new Dish(dishId,dishName,price,material,practice,stock);
    		dd.dishAdd(dish);
    	}
    	
    	//修改菜品
    	public void dishupd() {
    		System.out.println("请输入菜品编号:(请输入数字!)");
    		int dishId=sc.nextInt();
    		System.out.println("请输入菜品名:");
    		String dishName=sc.next();
    		System.out.println("请输入价格:");
    		double price=sc.nextDouble();
    		System.out.println("请输入材料:");
    		String material=sc.next();
    		System.out.println("请输入做法:");
    		String practice = sc.next();
    		System.out.println("请输入库存:");
    		int stock=sc.nextInt();
    		dish=new Dish(dishId,dishName,price,material,practice,stock);
    		dd.dishUpdate(dish);
    	}
    	
    	//菜品删除
    	public void dishdel() {
    		System.out.println("请输入你要删除的菜品编号:(请输入数字)");
    		int dishId=sc.nextInt();
    		dd.dishDel(dishId);
    	}
    	
    	//购买菜品
    	public void dishbuy() {
    		int flag=0;	//判断是否结束购买
    		int k=0;
    		int dishId=0;	
    		double price=0;	//存储总价
    		while(flag==0){
    			System.out.println("请输入你要购买的菜品编号:(请输入数字)");
    			dishId=sc.nextInt();
    			price=price+dd.dishbuy(dishId);
    			System.out.println("是否继续购买?(1.是  2.否)");
    			k=sc.nextInt();
    			if(k==2) {
    				flag=1;
    			}
    		}
    		System.out.println("总价为:"+price);
    	}
    	//修改地址
    	public void addrupd() {
    		System.out.println("请输入你的编号:(请输入数字)");
    		int userId=sc.nextInt();
    		System.out.println("请输入密码:");
    		String password=sc.next();
    		System.out.println("请输入地址:");
    		String address=sc.next();
    		dd.addressupd(userId, password, address);
    		dd.addrque(userId);
    	}
    }
    
    

    6.最后就是实现菜单类

    package com.etc.option;
    
    import com.etc.dao.DishDao;
    import com.etc.dao.DishDao;
    import com.etc.dao.impl.DishDaoImpl;
    import com.etc.entity.Dish;
    import com.etc.entity.Manager;
    import com.etc.entity.User;
    
    public class Test {
    	public static void main(String[] args) {
    		TestMenu menu=new TestMenu();
    		menu.menu();
    
    	}
    }
    

    在这里插入图片描述

    在这里插入图片描述

    展开全文
  • 那么有没有办法,只编写一个方法呢?通过分析,不难发现,对于查询,我们发现除了SQL语句变动意外,就是实体类变动。其它基本保持不变。于是 我们就可以利用Java反射就可以做到这一点,但需要注意一个问题:就是...

    在用JDBC的过程中,我们可能会根据实际需求来编写N个查询方法(特别是新手),这样虽然简单,但是会产生大量的重复代码。且不容易维护。那么有没有办法,只编写一个方法呢?

    通过分析,不难发现,对于查询,我们发现除了SQL语句的变动意外,就是实体类的变动。其它基本保持不变。

    于是 我们就可以利用Java反射就可以做到这一点,但需要注意一个问题:就是实体类的属性名一定要和数据库查询结果的字段名名保持一致。(如果有别名,就和别名一致)

    以下是我萌新的个人思路,大神看看就好。

    比如我们数据库中有以下一张表:

    788e645a827c8a543b706a1992b6f009.png

    那么我们就应该对应一个实体类:

    public class User

    {

    private int id;

    private String username;

    private char sex;

    private String tell;

    //GET和SET方法......略

    }查询代码如下:

    public class Test

    {

    public static void main(String[] args) throws Exception

    {

    //对于其它的查询,我们只需要带入一个SQL参数,和一个实体类的Class就可以了。不过用的时候记得强制转换回来

    Listobj = query("select * from userinfo;",User.class);

    //打印查询结果

    for(User u : obj.toArray(new User[0]))

    {

    System.out.println(u.toString());

    }

    }

    public static Listquery(String querySql,Class objClass) throws Exception

    {

    //查询结果集

    ListqueryResult = new ArrayList();

    //数据库连接,这段代码应该写出去,这里为了演示,写里面了

    Class.forName("com.mysql.jdbc.Driver");

    String url = "jdbc:mysql://localhost:3306/mydatabase";

    String username = "root";

    String password = "12345678";

    Connection conn = DriverManager.getConnection(url, username, password);

    //执行查询

    PreparedStatement preparedStatement = conn.prepareStatement(querySql);

    ResultSet result = preparedStatement.executeQuery();

    //获取实体对象的方法和私有属性

    MapobjMap = new HashMap();

    Field [] fields = objClass.getDeclaredFields();

    Method [] methods = objClass.getDeclaredMethods();

    Field.setAccessible(fields, true);

    for(Method m:methods)

    {

    for(Field f:fields)

    {

    if(m.getName().toLowerCase().startsWith("set")

    && m.getName().toLowerCase().contains(f.getName().toLowerCase()))

    {

    objMap.put(f.getName(), m);

    }

    }

    }

    //获取结果

    while(result.next())

    {

    //利用反射执行SET方法

    Object obj = objClass.newInstance();

    for(String fieldValue : objMap.keySet())

    {

    Method setMethod = objMap.get(fieldValue);

    String paramenterType = setMethod.getParameterTypes()[0].getSimpleName();

    if(paramenterType.toLowerCase().equals("string"))

    {

    setMethod.invoke(obj, result.getString(fieldValue));

    }

    else if(paramenterType.toLowerCase().equals("int"))

    {

    setMethod.invoke(obj, result.getInt(fieldValue));

    }

    else if(paramenterType.toLowerCase().equals("char"))

    {

    setMethod.invoke(obj, result.getString(fieldValue).toCharArray()[0]);

    }

    //。。。。。。。。这里可以把剩余的基本数据类型补充完整

    }

    queryResult.add(obj);

    }

    preparedStatement.close();

    conn.close();

    return queryResult;

    }

    }

    展开全文
  • VC++编写一个相对完善ODBC数据库程序,一大亮点功能是实现动态创建数据源。其它功能数据库读娶添加、删除、修改等都比较简单,入门者参考话更有帮助。 源码下载地址:点击下载 备用下载地址:点击下载 ...

    源码下载简介

    VC++编写一个相对完善的ODBC数据库程序,一大亮点功能是实现动态创建数据源。其它功能数据库读娶添加、删除、修改等都比较简单,入门者参考的话更有帮助。

    源码下载地址:点击下载

    备用下载地址:点击下载

    展开全文
  • 以下为引用内容:$link = mysql_connect('localhost', 'root', '') or die('mysql database connect error');mysql_select_db('your database') or die('the ...//这里插入你html代码,$sql = ...
  • 那么有没有办法,只编写一个方法呢? 通过分析,不难发现,对于查询,我们发现除了SQL语句变动意外,就是实体类变动。其它基本保持不变。 于是 我们就可以利用Java反射就可以做到这一点,但需要注意一个问题...
  • C#编写的访问ACCESS数据库的简单程序, 内有一个简单的access数据库和C#编写的原代码
  • 在本机mysql 数据库中有一个名为yao库,其中有一个名为user表,表中内容如图 下面,则是python连接数据库的方法,及查找出表中内容,代码如下: #! /usr/bin/python # filename conn.py import MySQLdb ...
  • python 连接数据库操作, 方法如下:在本机mysql 数据库中有一个名为yao库,其中有一个名为user表,表中内容如图下面,则是python连接数据库的方法,及查找出表中内容,代码如下:#! /usr/bin/python# ...
  • 简单的个人博客数据库:练习个人博客数据库分析功能模块简介用户管理博文管理评论系统分类管理标签管理mysql代码 个人博客数据库分析 功能模块简介 用户管理:用户登陆,找回密码 博文管理:编写、修改、删除博客...
  • python 连接数据库操作, 方法如下:在本机mysql 数据库中有一个名为yao库,其中有一个名为user表,表中内容如图下面,则是python连接数据库的方法,及查找出表中内容,代码如下:#! /usr/bin/python# ...
  • 最近头儿让写一个类似于navicat可视化工具查询界面,要求在单一jsp界面完成,由于本人水平有限,用了好几天才搞定,现把代码记录于此,以便日后查阅。 知识点:jqueryajax、jdbc数据库连接。 //获取...
  • 这一节主要对第三节大代码进行详细测试 这里使用rspec工具进行测试用例编写。http://rspec.info/ RSpec是Ruby测试框架,确实不懂,先手工测试下吧 本次优化点 使用strtok替代scanf,并增加了对输入校验,...
  • 数据库创建代码: 1:用户表:(users) userid(主键),username(用户名),password(密码),sex(性别),head(头像),regdate(注册日期) 2:类别表:(types) tid(主键),type 3:技术表:(technic) teid(主键),tename(技术名),...
  • 刚开始写好程序之后,直接进行简单数据库调用,但是程序一直报空指针错误,后来查找资料才知道,jdk8里面是没有odbc所用驱动类,于是换成了jdk7就可以了。 问题描述: java.sql.SQLException: [Microsoft][ODBC ...
  • [图片说明](https://img-ask.csdn.net/upload/201705/18/1495076199_190365.png)如图,我想把第一个显示列表东西,通过直接点击商品前面光标点击添加添加到另外一个空白表格上,我应该怎么编写代码呢?...
  • 这是一个使用python语言编写的爬虫脚本,提供各种存储方式(txt,csv,mysql数据库存储),有requests+xpath库爬取与解析方式,也有selenium工具的简单使用,可实现动态网站爬取。需要自己完善核心数据代码(有...
  • DBFlow - 一个速度极快,功能强大,而且非常简单的 Android 数据库 ORM 库,为你编写数据库代码,DBFlow 已被证明是最好的解决方案。5 个顶级 Android 开源库
  • minorm.js是一个简单的库,用于对MySQL数据库使用JSON查询,这是一个用mini.orm命名的小ORM,用Node.js编写。 贡献 您要做的只是派生存储库,编写代码,提交并创建请求请求。 我建议使用GitHub Desktop。 安装 要...
  • 这是一个用于从数据库创建,读取,更新和删除项目的简单API。 此实现没有接口,您可以仅通过端点进行操作。 例如,您可以在Visual Studio中打开此解决方案,并在Postman中运行和使用端点。 该解决方案只有一个名...
  • 数据库(Oracle)的增,删,改,查(一个简单的例子)一、数据库建表1、封装数据库连接 一、数据库建表 1、封装数据库连接 对数据库连接代码封装一下,方便后续代码编写 在这里插入代码片 我们要向数据库中添加...
  • 代码class ConnDB{private $local;private $username;private $pwd;private $db;private $connID;private $code;public function __construct($local,$username,$pwd,$connID,$code,$db){$this->local=$local;...
  • 使用JAVA jdbc编写一段快速连接Mysql...做一个简单记录 话不多说,下面直接上代码。 public static void main(String[] args) { Connection conn = null; String sql; // String url = "jdbc:mysql://localho...
  • 在本机mysql 数据库中有一个名为yao库,其中有一个名为user表,表中内容如图 下面,则是python连接数据库的方法,及查找出表中内容,代码如下: #! /usr/bin/python # filename conn.py import ...
  • 创建一个create_table.sh文件 脚本代码 #!/bin/bash #数据库名 db_name=dongshaofei #表名 tb_name=new_table #注意partitioned by 是分区字段 代表这是个分区表 不需要分区可以去掉 #hql hive -e " use $db_name ...
  • 一个简单库存记账程序代码,录入界面用VB6编写数据库为accessmdb数据库。两个报表用access做
  • 用MFC编写的对mysql数据库的操作

    热门讨论 2011-12-12 17:13:33
    这是一个在visual studio2008环境用MFC语言对mysql数据库编写一个简单的学生信息管理系统!如果你要用的话,你必须要安装了mysql,而且用户是root,密码是:123456,因为只供MFC和数据库初学者使用,本资源有可执行...
  • 这里主要考虑就是,如何能有一个扩展,在更换数据库的时候可以方便切换而不用修改源代码。 我们先来定义一个接口,这个接口只有一个方法,就是查询表列信息: ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,734
精华内容 1,093
关键字:

编写一个简单的数据库代码