C#反射实现对象关系映射(ORM)

ORM在业界广泛使用,导致程序员对数据库的操作水平不断下降,可能技术发展就是这样吧,老技术终究会被新技术取代,因为其不断地封装,使得代码操作上越来越简单。也许只有BAT和微软更注重老技术的传承了吧。

笔者也简单的封装了一个ORM框架。因为没有研究过别人的ORM到底是怎么封装的,全凭反射和动态拼sql这2个ORM的重要概念。所以封装的不好的地方轻喷

先给出类结构,注意这个类的设计必须符合JavaBean的规范,不明白什么是JavaBean的读者可以百度去查阅相关资料。这里索引列,即id必须放在第一行

class Student
{
    private int id;
    private string name;
    private Boolean sex;
    private string phone;

    public int Id
    {
        get { return id; }
        set { id = value; }
    }

    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    public Boolean Sex
    {
        get { return sex; }
        set { sex = value; }
    }

    public string Phone
    {
        get { return phone; }
        set { phone = value; }
    }
}

class Course
{
    private int id;
    private string name;
    private decimal cost;

    public int Id
    {
        get { return id; }
        set { id = value; }
    }
    public string Name
    {
        get { return name; }
        set { name = value; }
    }
    public decimal Cost
    {
        get { return cost; }
        set { cost = value; }
    }
}

数据库中创建符合这2个类的表结构,Boolean类型在数据库中是BIT

CREATE TABLE STUDENT
(
	ID INT IDENTITY(1,1) PRIMARY KEY,
	NAME VARCHAR(30),
	SEX BIT,
	PHONE VARCHAR(20)
)
    
CREATE TABLE COURSE
(
    ID INT IDENTITY(1,1) PRIMARY KEY,
    NAME VARCHAR(40),
    COST DECIMAL
)
INSERT INTO STUDENT(NAME,SEX) VALUES('波多野结衣',1,'13333333333')
INSERT INTO STUDENT(NAME,SEX) VALUES('苍井空',1,'17333333333')
INSERT INTO COURSE(NAME,COST) VALUES('Java从入门到放弃',98.8)
INSERT INTO COURSE(NAME,COST) VALUES('MySql从删库到跑路',45.5)

接下来先演示一下笔者封装好的ORM

SqlConnection sqlConnection = new SqlConnection("Server=.;user=sa;pwd=find/-perm4000;database=DBTest");
//这个ReflectUtil就是ORM的封装,里面有增删改查基本功能
ReflectUtil<Student> rf1= new ReflectUtil<Student>(sqlConnection);
Student stu1=rf1.Select(1);
Student stu2=rf1.Select(2);
ReflectUtil<Course> rf2=new ReflectUtil<Course>(sqlConnection);
Course course1=rf2.Select(1);
Course course2=rf2.Select(2);
Console.WriteLine("--------------学生类----------------");
Console.Write("学号:{0}\t", stu1.Id);
Console.Write("姓名:{0}\t", stu1.Name);
Console.Write("性别:{0}\t", stu1.Sex?"女":"男");
Console.WriteLine("手机号:{0}", stu1.Phone);
Console.Write("学号:{0}\t", stu2.Id);
Console.Write("性别:{0}\t", stu2.Sex?"女":"男");
Console.WriteLine("手机号:{0}", stu2.Phone);
Console.WriteLine("--------------课程类----------------");
Console.Write("课程编号:{0}\t", course1.Id);
Console.Write("课程名称:{0}\t", course1.Name);
Console.WriteLine("课本金额:{0}", course1.Cost);
Console.Write("课程编号:{0}\t", course2.Id);
Console.Write("课程名称:{0}\t", course2.Name);
Console.WriteLine("课本金额:{0}", course2.Cost);
Console.Read();

C#反射实现对象关系映射(ORM)

演示插入后直接全部查出,返回的是List对象,接着上面写

    Student stu3 = new Student();
    stu3.Name = "加藤鹰";
    stu3.Sex = false;
    stu3.Phone = "15333333333";
    Student stu4 = new Student();
    stu4.Name = "毛衣女";
    stu4.Sex = true;
    stu4.Phone = "13866666666";
    //这里持久化到数据库
    rf1.Insert(stu3);
    rf1.Insert(stu4);
    Course course3 = new Course();
    course3.Name = "论挑逗乳房全技巧";
    course3.Cost = 568.3M;
    Course course4 = new Course();
    course4.Name = "加藤鹰之手的前世今生";
    course4.Cost = 788.6M;
    //持久化
    rf2.Insert(course3);
    rf2.Insert(course4);
    List<Student> stuList=rf1.Select();
    List<Course> courseList = rf2.Select();
    //打印展示
    Console.WriteLine("--------------学生类----------------");
    foreach (Student stu in stuList)
    {
        Console.Write("学号:{0}\t", stu.Id);
        Console.Write("姓名:{0}\t", stu.Name);
        Console.Write("性别:{0}\t", stu.Sex ? "女" : "男");
        Console.WriteLine("手机号:{0}", stu.Phone);
    }
    Console.WriteLine("--------------课程类----------------");
    foreach(Course course in courseList)
    { 
        Console.Write("课程编号:{0}\t", course.Id);
        Console.Write("课程名称:{0}\t", course.Name);
        Console.WriteLine("课本金额:{0}", course.Cost);
    }
    Console.Read();

C#反射实现对象关系映射(ORM)
再看看数据库,可以看出确实写入了
C#反射实现对象关系映射(ORM)
接下来演示更新操作,更新同样传入一个对象,设置的id就是需要更新的行,实际上除了id列,其余的列都会执行一次更新,如果有不想更新的列就设置原值,就以Student表为例,这里不接着上面写了
ReflectUtil rf1 = new ReflectUtil(sqlConnection);
Student stu = new Student();
stu.Id = 4;
stu.Name = “加藤鹰亲传弟子”;
stu.Sex = true;
stu.Phone = “1386666666”;
rf1.Update(stu);
List stuList = rf1.Select();
foreach (Student stu_ in stuList)
{
Console.Write(“学号:{0}\t”, stu_.Id);
Console.Write(“姓名:{0}\t”, stu_.Name);
Console.Write(“性别:{0}\t”, stu_.Sex ? “女” : “男”);
Console.WriteLine(“手机号:{0}”, stu_.Phone);
}
Console.Read();

C#反射实现对象关系映射(ORM)

执行删除,删除没有那么多的嘻嘻哈哈,传入一个id就行,简单粗暴

    List<Student> stuList = rf1.Select();
    foreach (Student stu_ in stuList)
    {
        Console.Write("学号:{0}\t", stu_.Id);
        Console.Write("姓名:{0}\t", stu_.Name);
        Console.Write("性别:{0}\t", stu_.Sex ? "女" : "男");
        Console.WriteLine("手机号:{0}", stu_.Phone);
    }
    rf1.Delete(2);
    List<Student> stuList2 = rf1.Select();
    foreach (Student stu_ in stuList2)
    {
        Console.Write("学号:{0}\t", stu_.Id);
        Console.Write("姓名:{0}\t", stu_.Name);
        Console.Write("性别:{0}\t", stu_.Sex ? "女" : "男");
        Console.WriteLine("手机号:{0}", stu_.Phone);
    }
    Console.Read();

C#反射实现对象关系映射(ORM)

基本功能演示完成,下面给出ReflectUtil源代码,实际编写的时候不可能一次就能成功写好的,肯定需要经过多次的测试和调整,拼接的sql在发出的时候先看看拼的格式是否正确等等。坑是肯定有的,就比如id列必须要第一个定义,否则Select(int id)最终拼接的sql会差强人意的。可能有些读者会觉得:我为啥要自己封装ORM,网上现成的多得是。没错,现成的轮子完全没必要再去造,用现成的就好了。这种思想如果不能转变就永远是个菜鸟程序员。这就是跟大牛的差别。别人造轮子,你用轮子。

    class ReflectUtil<T>
    {
        private SqlConnection sqlConnection;
        private SqlCommand sqlCommand;

        public ReflectUtil(SqlConnection sqlConnection)
        {
            this.sqlConnection = sqlConnection;
        }

        //根据指定id获取一条记录
        public T Select(int id)
        {
            if(this.sqlConnection.State==ConnectionState.Closed)
            { 
                this.sqlConnection.Open();
            }
            string className = GetName(typeof(T));
            //拼接sql
            string sqlText = "SELECT * FROM " + className + " WHERE ID="+ id;
            sqlCommand = new SqlCommand(sqlText,sqlConnection);
            DataSet ds = new DataSet();
            SqlDataAdapter sda = new SqlDataAdapter(sqlCommand);
            sda.Fill(ds);
            T t = Select1(ds.Tables[0]);
            this.sqlConnection.Close();
            return t;
        }
        private T Select1(DataTable dt)
        {
            //实例化该泛型对象
            T t = Activator.CreateInstance<T>();
            //获取该对象的属性集合
            PropertyInfo[] fields = typeof(T).GetProperties();
            //获取DataTable的第一行,这里已经确定DataTable只有一条数据
            DataRow dr = dt.Rows[0];
            //获取列数
            int count = dt.Columns.Count;
            //遍历属性并赋值
            foreach (PropertyInfo field in fields)
            {
                for(int i=0;i<count;i++)
                {
                    if(field.Name.ToUpper()==dt.Columns[i].ColumnName.ToUpper())
                    {
                        field.SetValue(t, dr[i], null);
                        break;
                    }
                }
            }
            return t;
        }
        
        //获取泛型对象的集合,这里获取所有
        public List<T> Select()
        {
            if(this.sqlConnection.State==ConnectionState.Closed)
            { 
                this.sqlConnection.Open();
            }
            string className = GetName(typeof(T));
            //拼接sql
            string sqlText = "SELECT * FROM " + className;
            sqlCommand = new SqlCommand(sqlText, sqlConnection);
            DataSet ds = new DataSet();
            SqlDataAdapter sda = new SqlDataAdapter(sqlCommand);
            sda.Fill(ds);
            DataTable dt = ds.Tables[0];
            List<T> arrList = Select2(dt);
            this.sqlConnection.Close();
            return arrList;
        }
        private List<T> Select2(DataTable dt)
        {
            List<T> arrList = new List<T>();
            //获取该对象的属性集合
            PropertyInfo[] fields = typeof(T).GetProperties();
            //获取列数
            int columnCount = dt.Columns.Count;
            //获取行数
            int rowCount = dt.Rows.Count;
            //遍历赋值
            for(int i=0;i<rowCount;i++)
            {
                T t = Activator.CreateInstance<T>();
                for(int j=0;j<columnCount;j++)
                {
                    foreach(PropertyInfo field in fields)
                    {
                        if(field.Name.ToUpper()==dt.Columns[j].ColumnName.ToUpper())
                        {
                            field.SetValue(t, dt.Rows[i][j], null);
                            break;
                        }
                    }
                }
                arrList.Add(t);
            }
            return arrList;
        }
        
        //插入一个泛型对象,返回受影响的行数
        public int Insert(T t)
        {
            if(this.sqlConnection.State==ConnectionState.Closed)
            { 
                this.sqlConnection.Open();
            }
            //得到类名
            string className = GetName(typeof(T));
            StringBuilder sql = new StringBuilder("INSERT INTO ");
            sql.Append(className+"(");
            StringBuilder sqlValue = new StringBuilder();
            //拼接sql属性
            PropertyInfo[] fields = t.GetType().GetProperties();
            SqlParameter[] sqlParameters = new SqlParameter[fields.Length-1];
            //第一个属性不需要拼接,在数据库中是自增序列
            for(int i=1;i<fields.Length;i++)
            {
                if(i==fields.Length-1)
                {
                    sql.Append(fields[i].Name+") VALUES(");
                    sqlValue.Append("@"+fields[i].Name + ")");
                }
                else
                {
                    sql.Append(fields[i].Name + ",");
                    sqlValue.Append("@"+fields[i].Name+ ",");
                }
                sqlParameters[i-1] = new SqlParameter("@" + fields[i].Name, fields[i].GetValue(t));
            }
            sql.Append(sqlValue.ToString());
            SqlCommand sqlCommand = new SqlCommand(sql.ToString(), sqlConnection);
            sqlCommand.CommandType = CommandType.Text;
            sqlCommand.Parameters.AddRange(sqlParameters);
            int count= sqlCommand.ExecuteNonQuery();
            this.sqlConnection.Close();
            return count;
        }
        
        //插入泛型对象集合,返回受影响的总行数
        public int Insert(List<T> arrList)
        {
            int result = 0;
            //直接调用已实现的单对象插入即可
            for(int i=0;i<arrList.Count;i++)
            {
                result+=Insert(arrList[i]);
            }
            return result;
        }
        
        //修改某一泛型对象,返回受影响的行数
        public int Update(T t)
        {
            if(this.sqlConnection.State==ConnectionState.Closed)
            { 
                this.sqlConnection.Open();
            }
            //得到类名
            string className = GetName(typeof(T));
            StringBuilder sql = new StringBuilder("UPDATE ");
            sql.AppendFormat("{0} SET ", className);
            PropertyInfo[] fields = t.GetType().GetProperties();
            SqlParameter[] sqlParameters = new SqlParameter[fields.Length];
            //遍历属性拼sql,第一个id属性单独拼
            for(int i=1;i<fields.Length;i++)
            {
                if(i==fields.Length-1)
                { 
                    sql.AppendFormat("{0}[email protected]{0} ", fields[i].Name);
                }
                else
                {
                    sql.AppendFormat("{0}[email protected]{0},", fields[i].Name);
                }
                sqlParameters[i-1] = new SqlParameter("@" + fields[i].Name, fields[i].GetValue(t));
            }
            sql.AppendFormat("WHERE {0}[email protected]{0}", fields[0].Name);
            sqlParameters[fields.Length - 1] = new SqlParameter("@" + fields[0].Name, fields[0].GetValue(t));
            SqlCommand sqlCommand = new SqlCommand(sql.ToString(), sqlConnection);
            sqlCommand.CommandType = CommandType.Text;
            sqlCommand.Parameters.AddRange(sqlParameters);
            int count=sqlCommand.ExecuteNonQuery();
            this.sqlConnection.Close();
            return count;
        }
        //修改泛型对象集合,返回受影响的总行数
        public int Update(List<T> arrList)
        {
            int result = 0;
            for(int i=0;i<arrList.Count;i++)
            {
                result += Update(arrList[i]);
            }
            return result;
        }

        //删除指定ID的泛型对象,并将该对象返回
        public T Delete(int id)
        {
            if (this.sqlConnection.State == ConnectionState.Closed)
            {
                this.sqlConnection.Open();
            }
            T t = Activator.CreateInstance<T>();
            t = Select(id);
            string className = GetName(typeof(T));
            PropertyInfo[] fields = typeof(T).GetProperties();
            StringBuilder sql = new StringBuilder();
            sql.AppendFormat("DELETE {0} WHERE {1}={2}", className, fields[0].Name, id);
            if (this.sqlConnection.State == ConnectionState.Closed)
            {
                this.sqlConnection.Open();
            }
            sqlCommand = new SqlCommand(sql.ToString(), sqlConnection);
            sqlCommand.CommandType = CommandType.Text;
            int count=sqlCommand.ExecuteNonQuery();
            this.sqlConnection.Close();
            return t;
        }
        //获取指定泛型的类名
        private string GetName(Type t)
        {
            //得到该类名称
            return t.Name;
        }
    }