系统架构一一ORM的应用

在没有ORM的时代,我们直接从数据库中返回完整的数据集,然后通过键值对取出相应字段的数据,当然更要需要,取出的数据还要转换成编程语言相应的类型,再进行数据的操作。这种方式存储数据干脆直接,还可以利用Sql语句进行查询的优化,有些复杂的查询逻辑还可以编写成存储过程,充分利用数据库的性能,降低客户端的压力。

//ADO.NET示例
        string connectionString =
            "Data Source=(local);Initial Catalog=Northwind;"
            + "Integrated Security=true";

        // Provide the query string with a parameter placeholder.
        string queryString =
            "SELECT ProductID, UnitPrice, ProductName from dbo.products "
                + "WHERE UnitPrice > @pricePoint "
                + "ORDER BY UnitPrice DESC;";

        // Specify the parameter value.
        int paramValue = 5;

        // Create and open the connection in a using block. This
        // ensures that all resources will be closed and disposed
        // when the code exits.
        using (SqlConnection connection =
            new SqlConnection(connectionString))
        {
            // Create the Command and Parameter objects.
            SqlCommand command = new SqlCommand(queryString, connection);
            command.Parameters.AddWithValue("@pricePoint", paramValue);

            // Open the connection in a try/catch block.
            // Create and execute the DataReader, writing the result
            // set to the console window.
            try
            {
                connection.Open();
                SqlDataReader reader = command.ExecuteReader();
                while (reader.Read())
                {
                    Console.WriteLine("\t{0}\t{1}\t{2}",
                        reader[0], reader[1], reader[2]);
                }
                reader.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            Console.ReadLine();
        }
    }

但直接利用Sql返回数据的方式,有个最大的问题就是可读性太差。首先程序代码中有很多拼合SQL的代码,以及数据迭代的代码。因为没有限制,还可以一边查询,一边处理业务逻辑,数据库操作代码和客户端代码混合一起,数量一上来,就变成天书了。所以有聪明的人,就把一些比较通用的部分分离出来,比如构建一个DBHelper,把数据库连接部分单独拉出来,再提供一些通用的查询函数,微软的企业库就是一个比较典型的实现。但这样也只是可以稍微偷懒一点,事务上好控制点。

//利用EntLib返回一个数据集。
        Database db = DatabaseFactory.CreateDatabase("ConStr");
            string sql = "select * from Brand";
            DbCommand cmd = db.GetSqlStringCommand(sql);
 
            DataSet set = db.ExecuteDataSet(cmd);

后来有了J2EE框架,里边定义了各种Bean,有一种Bean叫Entity Bean,这个Entity Bean基本上跟数据库一一对应的,可序列化,可用于远程传递数据的无状态类。也就是说J2EE里边,要求事先将数据记录转换成存值的对象,再通过Session Bean(也就是Bussiness Object、BO)的调用返回数据的Copy,所有的数据库存取的代码,限制在了持久层(也就是DataAccess Layer,DataAccess Object、DAO),按照这种思维一起,应用程序的结构就很清晰了,代码的可读性也提升了,代码的可维护性也就增强了,然后就可以往系统中增加更多的功能了。

虽说数据库操作放在了持久层里,但数据对像的生成还是要程序员去写的,一个表有上百个字段,那你就得写上几倍于数据表字段数量的get/set,以前直接写Sql是根据需要来做Select的投影,需要什么就查什么,而现在则要全部做个映射,这个工作量可想而知还是不小的。于是有人把这部分操作弄成了一个独立的组件,开放出相应的接口给下游程序调用,当然目标是Sql无关的,带来的好处就是干净,少了很多数据库层面的拼装。

//EntityFramework的一个例子
    public abstract class QueryBase<T> : IQueryBase<T> where T : EntityBase
    {
        protected AppDataContext DataContext { get; set; }

        public List<T> FindByPage()
        {
            return DataContext.Set<T>().ToList();
        }
    }
    


对应的Sql

select * from T --T must be replaced by actual table name

ORM还有个特性就是实现数据库的无关性。上述Entity Framework的例子里,因为不需要写出Sql的具体实现,所以可以通过规范,使用不同的数据库驱动,实现同一套代码,在不同的数据库上运行。

在Setup.cs中设置数据库配置。


            services.AddDbContext<AppDataContext>(
                options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))
                );

在Jpa中指定数据库方言

	@Bean
	public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws IOException, PropertyVetoException, SQLException {
		LocalContainerEntityManagerFactoryBean efactory = new LocalContainerEntityManagerFactoryBean();
		efactory.setDataSource(dataSource());
		HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
		jpaVendorAdapter.setGenerateDdl(true);
		jpaVendorAdapter.setDatabase(Database.MYSQL);
		jpaVendorAdapter.setShowSql(true);
		
		efactory.setJpaVendorAdapter(jpaVendorAdapter);
		efactory.setPersistenceProviderClass(org.hibernate.jpa.HibernatePersistenceProvider.class);
		efactory.setPersistenceUnitName("test");
		efactory.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());

		return efactory;
	}

ORM另一个目的就是就是数据操作面向对象话,消除了面向过程的Sql语句的干扰,编程语言就可以做许多神奇的事情来。比如springdata中,只需要定义接口,然后通过动态代码加上依赖注入的方式,就可以实现持久层的实现:

public interface AccountRepository extends JpaRepository<Account, String> {
	Account findByUsername(String username);
}

当然ORM也有很多不足,比如无形中增加了许多配置项,也增加了学习成本,查询性能上没法跟SQL相比,聚合查询不太容易实现等。

posted @ 2020-05-08 21:05  八爻老骥  阅读(316)  评论(0编辑  收藏  举报