ORM 框架--EF详解

EF

Entity Framework是一个对象关系映射O/RM框架。
Entity Framework让开发者可以像操作领域对象(domain-specific objects)那样操作关系型数据(relational data)。
Entity Framework减少了大部分通常需要编写的数据操作代码。
Entity Framework中可以使用LINQ来查询数据,使用强类型(strongly typed objects)来检索和操作数据。
Entity Framework提供了以下服务,使开发者可以更加侧重于程序业务逻辑,而非数据访问的基本操作。

  1. 状态或变更跟踪(change tracking)
  2. 身份或主键识别(identity resolution)
  3. 懒加载(lazy loading)
  4. 查询翻译(query translation)

Entity Framework是ADO.NET的加强,它给开发者提供了数据库访问和存储的自动化机制。
Entity Framework是一个开源框架。

ORM

对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。ORM框架是连接数据库的桥梁,只要提供了持久化类与表的映射关系,ORM框架在运行时就能参照映射文件的信息,把对象持久化到数据库中。

EF的三种创建方法

  1. DataBase First(数据库优先):先创建数据库表,然后自动生成EDM文件,EDM文件生成模型类。
  2. Model First(模型优先):先创建Edm文件,Edm文件自动生成模型类和数据库;
  3. Code First(代码优先):程序员自己写模型类,然后自动生成数据库。没有Edm。

DataBase First简单、方便,但是当项目大了之后会非常痛苦;Code First入门门槛高,但是适合于大项目。Model First……

Code First的微软的推荐用法是程序员只写模型类,数据库由EF帮我们生成,当修改模型类之后,EF使用“DB Miguration”自动帮我们更改数据库。但是这种做法太激进,不适合很多大项目的开发流程和优化,只适合于项目的初始开发阶段。
Java的Hibernate中也有类似的DDL2SQL技术,但是也是用的较少。“DB Miguration”也不利于理解EF,因此在初学阶段,我们将会禁用“DB Miguration”,采用更实际

实体配置

1、 数据库中建表T_Perons,有Id(主键,自动增长)、Name、CreateDateTime字段。
2、 创建Person类

[Table("T_Persons")]//因为类名和表名不一样,所以要使用Table标注
public class Person    
{
  public long Id { set; get; }
  public string Name { get; set; }
  public DateTime CreateDateTime { get; set; }
}    

因为EF约定主键字段名是Id,所以不用再特殊指定Id是主键,如果非要指定就指定[Key]。
因为字段名字和属性名字一致,所以不用再特殊指定属性和字段名的对应关系,如果需要特殊指定,则要用[Column(“Name”)]*)必填字段标注[Required]、字段长度[MaxLength(5)]、可空字段用int?、如果字段在数据库有默认值,则要在属性上标注[DatabaseGenerated]
注意实体类都要写成public,否则后面可能会有麻烦。

3,创建DbContext类(模型类、实体类)

public class MyDbContext:DbContext
{
	public MyDbContext():base("name=conn1")//name=conn1表示使用连接字符串中名字为conn1的去连接数据库
	{
	}
	public DbSet<Person> Persons { get; set; }//通过对Persons集合的操作就可以完成对T_Persons表的操作
}    

4,运行测试

MyDbContext ctx = new MyDbContext();
Person p = new Person();
p.CreateDateTime = DateTime.Now;
p.Name = "rupeng";
ctx.Persons.Add(p);
ctx.SaveChanges();

异常的处理:
    如果数据有错误可能在SaveChanges()的时候出现异常,一般仔细查看异常信息或者一直深入一层层的钻InnerException就能发现错误信息。
举例:创建一个Person对象,不给Name、CreateDateTime赋值就保存。

配置

DataAnnotations

DataAnnotations:
[Table(“T_Persons”)]、[Column(“Name”)]这种在类上或者属性上标记的方式就叫DataAnnotations

好处与坏处:
这种方式比较方便,但是耦合度太高,不适合大项目开发。

一般的类最好是POCO(Plain Old C# Object没有继承什么特殊的父类,没有标注什么特殊的Attribute,没有定义什么特殊的方法,就是一堆普通的属性);

不符合大项目开发的要求。微软推荐使用FluentAPI的使用方式,因此后面主要用FluentAPI的使用方式。

FluentAPI

1,数据库中建表T_Perons,有Id(主键,自动增长)、Name、CreateDateTime字段。

2,创建Person类。模型类就是普通C#类

public class Person
{
	public long Id { set; get; }
	public string Name { get; set; }
	public DateTime CreateDateTime { get; set; }
}    

3,创建一个PersonConfig类,放到ModelConfig文件夹下(PersonConfig、EntityConfig这样的名字都不是必须的)

class PersonConfig: EntityTypeConfiguration<Person>
{
	public PersonConfig()
	{
		this.ToTable("T_Persons");//等价于[Table("T_Persons")]
	}
}

4,创建DbContext类

public class MyDbContext:DbContext
{
	public MyDbContext():base("name=conn1")
	{

	}
	protected override void OnModelCreating(DbModelBuilder modelBuilder)
	{
		base.OnModelCreating(modelBuilder);
		modelBuilder.Configurations.AddFromAssembly(Assembly.GetExecutingAssembly());
		//代表从这句话所在的程序集加载所有的继承自EntityTypeConfiguration为模型配置类。
	}
	public DbSet<Person> Persons { get; set; }
}

基本增删改查

增加

获取DbSet除了可以ctx.Persons之外,还可以ctx.Set()
1,增加,同上
注意:如果Id是自动增长的,创建的对象显然不用指定Id的值,并且在SaveChanges ()后会自动给对象的Id属性赋值为新增行的Id字段的值。

删除。

先查询出来要删除的数据,然后Remove。这种方式问题最少,虽然性能略低,但是删除操作一般不频繁,不用考虑性能。后续在“状态管理”中会讲其他实现方法

MyDbContext ctx = new MyDbContext();
var p1= ctx.Persons.Where(p => p.Id == 3).SingleOrDefault();//先查询出来
if(p1==null)
{
	Console.WriteLine("没有id=3的人");
}
else
{
	ctx.Persons.Remove(p1);//进行删除
}
ctx.SaveChanges();//删除后进行保存

批量删除:怎么批量删除,比如删除Id>3的?
查询出来一个个Remove。性能坑爹。如果操作不频繁或者数据量不大不用考虑性能,如果需要考虑性能就直接执行sql语句

修改

先查询出来要修改的数据,然后修改,然后SaveChanges()

MyDbContext ctx = new MyDbContext();
var ps = ctx.Persons.Where(p => p.Id > 3);
foreach(var p in ps)
{
	p.CreateDateTime = p.CreateDateTime.AddDays(3);
	p.Name = "haha";
}
ctx.SaveChanges();

因为DbSet实现了IQueryable接口,而IQueryable接口继承了IEnumerable接口,所以可以使用所有的linq、lambda操作。给表增加一个Age字段,然后举例orderby、groupby、where操作、分页等。一样一样的。
查询order by的一个细节:EF调用Skip之前必须调用OrderBy:
如下调用

var items = ctx.Persons.Skip(3).Take(5); //会报错“The method 'OrderBy' must be called before the method 'Skip'.)”,

要改成:

var items = ctx.Persons.OrderBy(p=>p.CreateDateTime).Skip(3).Take(5);

这也是一个好习惯,因为以前就发生过(写原始sql):
分页查询的时候没有指定排序规则,以为默认是按照Id排序,其实有的时候不是,就造成数据混乱。写原始SQL的时候也要注意一定要指定排序规则。

原理及SQL监控

EF会自动把Where()、OrderBy()、Select()等这些编译成“表达式树(Expression Tree)”,然后会把表达式树翻译成SQL语句去执行。(编译原理,AST)因此不是“把数据都取到内存中,然后使用集合的方法进行数据过滤”,因此性能不会低。但是如果这个操作不能被翻译成SQL语句,则或者报错,或者被放到内存中操作,性能就会非常低。
怎么查看真正执行的SQL是什么样呢?

DbContext有一个Database属性,其中的Log属性,是Action委托类型,也就是可以指向一个void A(string s)方法,其中的参数就是执行的SQL语句,每次EF执行SQL语句的时候都会执行Log。因此就可以知道执行了什么SQL。

EF的查询是“延迟执行”的,只有遍历结果集的时候才执行select查询,ToList()内部也是遍历结果集形成List。(如果要立刻开始执行,可以在后面加上ToList(),因为它会遍历集合)

查看Update操作,会发现只更新了修改的字段。
观察一下前面学学习时候执行的SQL是什么样的。Skip().Take()被翻译成了?Count()被翻译成了?

var result = ctx.Persons.Where(p => p.Name.StartsWith("rupeng"));//看看翻译成了什么? like语句
var result = ctx.Persons.Where(p => p.Name.Contains("com"));//呢? %com%
var result = ctx.Persons.Where(p => p.Name.Length>5); //呢? 
var result = ctx.Persons.Where(p => p.CreateDateTime>DateTime.Now);// 呢?
long[] ids = { 2,5,6};
//不要写成int[]
var result = ctx.Persons.Where(p => ids.Contains(p.Id));

EF中还可以多次指定where来实现动态的复合检索:

//必须写成IQueryable<Person>,如果写成IEnumerable就会在内存中取后续数据
IQueryable<Person> items = ctx.Persons;//为什么把IQueryable<Person>换成var会编译出错
items = items.Where(p=>p.Name=="rupeng");
items = items.Where(p=>p.Id>5);

EF是跨数据库的,如果迁移到MYSQL上,就会翻译成MYSQL的语法。要配置对应数据库的Entity Framework Provider。

细节:
每次开始执行的__MigrationHistory等这些SQL语句是什么?
是DBMigration用的,也就是由EF帮我们建数据库,现在我们用不到,用下面的代码禁用:
Database.SetInitializer(null);//XXXDbContext就是项目DbContext的类名。一般建议放到XXXDbContext构造函数中。

注意这里的Database是System.Data.Entity下的类,不是DbContext的Database属性。如果写到DbContext中,最好用上全名,防止出错。

执行原生SQL。

执行非查询语句,调用DbContext 的Database属性的ExecuteSqlCommand方法,可以通过占位符的方式传递参数:

ctx.Database.ExecuteSqlCommand("update T_Persons set Name={0},CreateDateTime=GetDate()", "rupeng.com");

占位符的方式不是字符串拼接,经过观察生成的SQL语句,发现仍然是参数化查询,因此不会有SQL注入漏洞。示例代码:

var q1 = ctx.Database.SqlQuery<Item1>("select Name,Count(*) Count from T_Persons where Id>{0} and CreateDateTime<={1} group by Name",2, DateTime.Now);

//返回值是DbRawSqlQuery 类型,也是实现了IEnumerable接口

foreach(var item in q1)
{
	Console.WriteLine(item.Name+":"+item.Count);
}
class Item1
{
	public string Name { get; set; }
	public int Count { get; set; }
}

类似于ExecuteScalar的操作比较麻烦:

int c = ctx.Database.SqlQuery<int>("select count(*) from T_Persons").SingleOrDefault();

不是所有lambda写法都能被支持

下面想把Id转换为字符串比较一下是否为"3"(别管为什么):

var result = ctx.Persons.Where(p => Convert.ToString(p.Id)=="3");

运行会报错(也许高版本支持了就不报错了),这是一个语法、逻辑上合法的写法,但是EF目前无法把他解析为一个SQL语句。

出现“System.NotSupportedException”异常一般就说明你的写法无法翻译成SQL语句。
想获取创建日期早于当前时间一小时以上的数据:

var result = ctx.Persons.Where(p => (DateTime.Now - p.CreateDateTime).TotalHours>1);

同样也可能会报错。
怎么解决?尝试其他替代方案(没有依据,只能乱试):

var result = ctx.Persons.Where(p => p.Id==3);

EF中提供了一个SQLServer专用的类SqlFunctions,对于EF不支持的函数提供了支持,比如:

var result = ctx.Persons.Where(p=>SqlFunctions.DateDiff("hour",p.CreateDateTime,DateTime.Now)>1);

对象的状态

简介

为什么查询出来的对象Remove()、再SaveChanges()就会把数据删除。而自己new一个Person()对象,然后Remove()不行?

为什么查询出来的对象修改属性值后、再SaveChanges()就会把数据库中的数据修改。因为EF会跟踪对象状态的改变。

五个状态

Detached(游离态,脱离态)、Unchanged(未改变)、Added(新增)、Deleted(删除)、Modified(被修改)。

状态转换

Add()、Remove()修改对象的状态。所有状态之间几乎都可以通过:Entry§.State=xxx的方式 进行强制状态转换。状态改变都是依赖于Id的(Added除外)

应用

当SavaChanged()方法执行期间,会查看当前对象的EntityState的值,决定是去新增(Added)、修改(Modified)、删除(Deleted)或者什么也不做(UnChanged)。
下面的做法不推荐,在旧版本中一些写法不被支持,到新版EF中可能也会不支持。
ObjectStateManager
1,不先查询再修改保存,而是直接更新部分字段的方法:

var p = new Person();
p.Id = 2;
ctx.Entry(p).State = System.Data.Entity.EntityState.Unchanged;
p.Name = "adfad";
ctx.SaveChanges();

也可以:

var p = new Person();
p.Id = 5;
p.Name = "yzk";
ctx.Persons.Attach(p);//等价于ctx.Entry(p).State = System.Data.Entity.EntityState.Unchanged;
ctx.Entry(p).Property(a => a.Name).IsModified = true;
ctx.SaveChanges();

2,不先查询再Remove再保存,而是直接根据Id删除的方法:

var p = new Person();
p.Id = 2;
ctx.Entry(p).State = System.Data.Entity.EntityState.Deleted;
ctx.SaveChanges();

注意下面的做法并不会删除所有Name=“rupeng.com” 的,因为更新、删除等都是根据Id进行的:

var p = new Person();
p.Name = "rupeng.com";
ctx.Entry(p).State = System.Data.Entity.EntityState.Deleted;
ctx.SaveChanges();

如果查询出来的对象只是供显示使用,不会修改、删除后保存,那么可以使用AsNoTracking()来使得查询出来的对象是Detached状态,这样对对象的修改也还是Detached状态,EF不再跟踪这个对象状态的改变,能够提升性能。示例代码:
      原来的:

var p1 = ctx.Persons.Where(p => p.Name == "rupeng.com").FirstOrDefault();
Console.WriteLine(ctx.Entry(p1).State);

修改为:

var p1 = ctx.Persons.AsNoTracking().Where(p => p.Name == "rupeng.com").FirstOrDefault();
Console.WriteLine(ctx.Entry(p1).State);

因为AsNoTracking()是DbQuery类(DbSet的父类)的方法,所以要先在DbSet后调用AsNoTracking()。

Fluent API

基本EF配置只要配置实体类和表、字段的对应关系、表间关联关系即可。
如果利用EF的高级配置,可以达到更多效果:
如果数据错误(比如字段不能为空、字符串超长等),会在EF层就会报错,而不会被提交给数据库服务器再报错;如果使用自动生成数据库,也能帮助EF生成更完美的数据库表。
这些配置方法无论是DataAnnotations、FluentAPI都支持,下面讲FluentAPI的用法

1, HasMaxLength设定字段的最大长度

public PersonConfig()
{
	this.ToTable("T_Persons");
	this.Property(p => p.Name).HasMaxLength(50);//长度为50
}

如果插入一个Person对象,Name属性的值非常长,保存的时候就会报DbEntityValidationException异常,这个异常的Message中看不到详细的报错消息,要看EntityValidationErrors属性的值。

var p = new Person();
p.Name = "非常长的字符串";
ctx.Persons.Add(p);
try
{
	ctx.SaveChanges();
}
catch(DbEntityValidationException ex)
{
	StringBuilder sb = new StringBuilder();
	foreach(var ve in ex.EntityValidationErrors.SelectMany(eve=>eve.ValidationErrors))
	{
		sb.AppendLine(ve.PropertyName+":"+ve.ErrorMessage);
	}
	Console.WriteLine(sb);
}    

2, (有用)字段是否可空:

this.Property(p => p.Name).IsRequired() //属性不能为空;
this.Property(p => p.Name).IsOptional() //属性可以为空;

默认规则是“主键属性不允许为空,引用类型允许为空,可空的值类型long?等允许为空,值类型不允许为空。

”基于“尽量少配置”的原则:如果属性是值类型并且允许为null,就声明成long?等,否则声明成long等;

如果属性属性值是引用类型,只有不允许为空的时候设置IsRequired()。

3, 其他一般不用设置的(了解即可)

a) 主键:this.HasKey(p => p.Id);
b) 某个字段不参与映射数据库:this.Ignore(p => p.Name1);
c) this.Property(p => p.Name).IsFixedLength();   //是否对应固定长度
d) this.Property(p => p.Name).IsUnicode(false)   //对应的数据库类型是varchar类型,而不是nvarchar
e) this.Property(p => p.Id).HasColumnName(“Id”);   //Id列对应数据库中名字为Id的字段
f) this.Property(p =>p.Id).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)   //指定字段是自动增长类型。

因为ToTable()、Property()、IsRequired()等方法的还是配置对象本身,因此可以实现类似于StringBuilder的链式编程,这就是“Fluent”一词的含义
下面的写法可以被简化:

public PersonConfig()
{
    this.ToTable("T_Persons");
    this.HasKey(p => p.Id);
    this.Ignore(p => p.Name2);
    this.Property(p => p.Name).HasMaxLength(50);
    this.Property(p => p.Name).IsRequired();
    this.Property(p => p.CreateDateTime).HasColumnName("CreateDateTime");
    this.Property(p => p.Name).IsRequired();
}

可以被简化为:

public PersonConfig()
{
    this.ToTable("T_Persons").HasKey(p => p.Id).Ignore(p => p.Name2);
    this.Property(p => p.Name).HasMaxLength(50).IsRequired();
    this.Property(p => p.CreateDateTime).HasColumnName("CreateDateTime").IsRequired();
}

延迟加载

如果public virtual Class Class { get; set; }把virtual去掉,那么下面的代码就会报空引用异常

var s = ctx.Students.First();
Console.WriteLine(s.Class.Name);

强调:如果要使用延迟加载,类必须是public,关联属性必须是virtual。

延迟加载(LazyLoad)的优点:

用到的时候才加载,没用到的时候不加载,因此避免了一次性加载所有数据,提高了加载的速度。
缺点:

如果不用延迟加载,就可以一次数据库查询就可以把所有数据都取出来(使用join实现),用了延迟加载就要多次执行数据库操作,提高了数据库服务器的压力。

因此:如果关联的属性几乎都要读取到,那么就不要用延迟加载;如果关联的属性只有较小的概率(比如年龄大于7岁的学生显示班级名字,否则就不显示)则可以启用延迟加载。
这个概率到底是多少是没有一个固定的值,和数据、业务、技术架构的特点都有关系,这是需要经验和直觉,也需要测试和平衡的。
注意:启用延迟加载的时候拿到的对象是动态生成类的对象,是不可序列化的,因此不能直接放到进程外Session、Redis等中,要转换成DTO(后面讲)再保存。

实体类的继承

所有实体类都会有一些公共属性,可以把这些属性定义到一个父类中。比如:

public abstract class BaseEntity
{
    public long Id { get; set; } //主键
    public bool IsDeleted { get; set; } = false; //软删除
    public DateTime CreateDateTime { get; set; } = DateTime.Now;//创建时间
    public DateTime DeleteDateTime { get; set; } //删除时间
}

使用公共父类的好处不仅是写实体类简单了,而且可以提供一个公共的Entity操作类:

class BaseDAO<T> where T : BaseEntity
{
    private MyDbContext ctx;//不自己维护MyDbContext而是由调用者传递,因为调用者可以要执行很多操作,由调用者决定什么时候销毁。
    public BaseDAO(MyDbContext ctx)
    {
        this.ctx = ctx;
    }
    public IQueryable<T> GetAll()//获得所有数据(不要软删除的)
    {
        return ctx.Set<T>().Where(t => t.IsDeleted == false);//这样自动处理软删除,避免了忘了过滤软删除的数据
    }
}

EO、DTO、ViewModel

EO(Entity Object,实体对象)就是EF中的实体类,对EO的操作会对数据库产生影响。EO不应该传递到其他层。

DTO(Data Transfer Object,数据传输对象),用于在各个层之间传递数据的普通类。

DTO有哪些属性取决于其他层要什么数据。DTO一般是“扁平类”,也就是没有关联属性,都是普通类型属性。

一些复杂项目中,数据访问层(DAL)和业务逻辑层(BLL)直接传递用一个DTO类,UI层和BLL层之间用一个新的DTO类。简单的项目共用同一个DTO。DTO类似于三层架构中的Model。

ViewModel(视图模型),用来组合来自其他层的数据显示到UI层。简单的数据可能可以直接把DTO交给界面显示,一些复杂的数据可以要从新转换为ViewModel对象。

ef-orm A Simple OR-Mapping framework on multiple databases. 使用手册(中文)http://geequery.github.io/ef-orm/manual/EF-ORM-user-guide.docx  使用示例工程 https://github.com/GeeQuery/ef-orm/tree/master/orm-tutorial EF-ORM是一个轻量,便捷的Java ORM框架。并且具备若干企业级的应用特性,如分库分表、JTA事务等。 代码生成插件for eclipse(请在eclipse中Help/Install new software后输入地址并安装)http://geequery.github.io/plugins/1.3.x/特点一 EF的设计的一个主要目的是提高开发效率,减少编码工作,让开发者“零配置”“少编码”的操作数据库大部分功能。 例如:数据库查询条件的传入问题是所有ORM框架都不能回避的一个问题,所以我经常在想——既然我们可以用向DAO传入一个Entity来实现插入操作,为什么就不能用同样的方法来描述一个不以主键为条件的update/select/delete操作?为什么DAO的接口参数老是变来变去?为什么很多应用中,自行设计开发类来描述各种业务查询条件才能传入DAO?为什么我们不能在数据访问层上花费更少的时间和精力?   JPA1.0和早期的H框架,其思想是将关系型数据库抽象为对象池,这极大的限制了本来非常灵活的SQL语句的发挥空间。而本质上,当我们调用某H框架的session.get、session.load、session.delete时,我们是想传递一个以对象形式表达的数据库操作请求。只不过某H框架要求(并且限制)我们将其视作纯粹的“单个”对象而已。JPA 2.0为了弥补JPA1.0的不足,才将这种Query的思想引入为框架中的另一套查询体系——Criteria API。事实上针对单个对象的get/load/persist/save/update/merge/saveOrUpdate API和Criteria API本来就为一体,只不过是历史的原因被人为割裂成为两套数据库操作API罢了。   因此,对于关系型数据库而言——Entity和Query是一体两面的事物,所谓Query,可以包含各种复杂的查询条件,甚至可以作为一个完整的SQL操作请求的描述。为此,EF彻底将Entity和Query绑在了一起。这种思想,使得—— 开发人员需要编写的类更少。开发人员无需编写其他类来描述复杂的SQL查询条件。也无需编写代码将这些查询条件转换为SQL/HQL/JPQL。DAO层也不会有老要改来改去的接口和API,几乎可以做到零编码。 对单个对象进行CRUD的操作API现在和Criteria API合并在一起。Session对象可以直接提供原本要Criteria API才能提供实现的功能。API大大简化。 IQueryableEntity允许你将一个实体直接变化为一个查询(Query),在很多时候可以用来完成复杂条件下的数据查询。比如 ‘in (?,?,?)’, ‘Between 1 and 10’之类的条件。 xxQL有着拼装语句可读性差、编译器无法检查、变更维护困难等问题,但是却广受开发人员欢迎。这多少有历史原因,也有Criteria API设计上过于复杂的因素。两者一方是极端灵活但维护困难,一方是严谨强大而学习和编写繁琐,两边都是极端。事实上JPA的几种数据查询方式存在青黄不接的问题。选择查询语言xxQL,项目面临后续维护困难,跨数据库移植性差;选择Criteria API,代码臃肿,操作繁琐,很多人望而却步。EF的设计思想是使人早日摆脱拼装SQL/HQL/JPQL的困扰,而是用(更精简易用的)Criteria API来操作数据库。 基于轻量级Criteria API的操作方式,使得对数据库的变更和重构变得非常轻松,解决了SQL语句多对软件维护和移植造成产生的不利影响。 阅读推荐:第3、4章 特点二,将SQL的使用发挥到极致,解决SQL拼凑问题、数据库移植问题 大部分OLTP应用系统到最后都不免要使用SQL/JPQL,然而没有一个很好的方法解决SQL在多种数据库下兼容性的问题。 EF-ORM中采用了独特的SQL解析和改写技术,能够主动检查并确保SQL语句或者SQL片段在各个数据库上的兼容性。 EF中除了Criteria API以外,可以直接使用“SQL语句”或者“SQL片段”。但是这些SQL语句并不是直接传送给JDBC驱动的,而是 有着一个数据库方言层,经过方言层处理的SQL语句,就具备了在当前数据库上正确操作的能力。这相当于提供了一种能跨数据库操作的SQL语言。(E-SQL) E-SQL不但解决了异构数据库的语法问题、函数问题、特殊的写法问题,还解决了动态SQL问题、绑定变量扩展等特性。 对于各种常用SQL函数和运算符,都可以自动转换为当前数据库支持的方言来操作。其函数支持也要多于HQL支持的函数。 阅读推荐:第7、8章 特点三,可能是业界最快的ORM框架. 得益于ASM的动态代码生成技术,部分耗时操作通过动态代码固化为硬编码实现,EF-ORM的大部分操作性能要超过已知的其他框架。 实际性能测试表明,EF的大部分操作都要快于Hiberante和MyBatis, 部分操作速度甚至数十倍于上述框架EF在极限插入模式下,甚至刷新了每秒10万条写入的记录。远远超过了其他框架。 一个初步的性能测试:测试代码:http://geequery.github.io/ef-orm/manual/performance-test.rar 测试报告:http://geequery.github.io/ef-orm/manual/performance-compare.docx 阅读推荐:第9、17章 特点四,分库分表 开发过程中参照了Hibernate Shards、Alibaba TDDL、Cobar等框架,也是基于词法分析器来提取SQL参数,并计算路由。 能支持分库维度含糊等场景下的分库分表。以及包括多库多表下的 order by , distinct, group by, having等操作。 阅读推荐:第10章 特点五,常用DDL操作的封装 从数据库元数据访问,到建表,创建约束,创建sequence等各种DDL操作进行了封装,用户无需编写各种SQL,可以直接通过API操作数据库结构。 尤其是ALTER TABLE等修改数据库的语句,各种不同的RDBMS都有较大语法差异。特点六、解决各种跨RDBMS的移植问题 1、DML操作、自增值处理与返回、查询这些不同数据库操作差异很大的东西,都了统一的封装。 2、DDL操作、建表、删表、trunacte,Sequence创建和TABLE模拟Sequence等,都做了支持。 3、对SQL语法操作和函数的改写与支持。其他特性轻量 该框架对应用环境、连接池、 是否为J2EE应用等没有特殊要求。可以和EJB集成,也可与Spring集成,也可以单独使用。整个框架只有两个JAR包,模块和功能都较为轻量。依赖少 整个框架只有三个jar库。间接依赖仅有commons-lang, slf4j等7个通用库,作为一个ORM框架,对第三方依赖极小。简单直接的API 框架的API设计直接面向数据库操作,不绕弯子,开发者只需要数据库基本知识,不必学习大量新的操作概念即可使用API完成各种DDL/DML操作。 最大限度利用编译器减少编码错误的可能性 API设计和元数据模型(meta-model)的使用,使得常规的数据库查询都可以直接通过Criteria API来完成,无需使用任何JPQL/HQL/SQL。可以让避免用户犯一些语法、拼写等错误。JPA2规范兼容 使用JPA 2.0规范的标准注解方式来定义和操作对象。(但整个ORM不是完整的JPA兼容实现)更高的性能 依赖于ASM等静态字节码技术而不是CGlib,使得改善了代理性能;依赖于动态反射框架,内部数据处理上的开销几乎可以忽略。操作性能接近JDBC水平。对比某H开头的框架,在写入操作上大约领先30%,在大量数据读取上领先50%以上。更多的性能调优手段 debug模式下提供了大量性能日志,帮您分析性能瓶颈所在。同时每个查询都可以针对batch、fetchSize、maxResult、缓存、级联操作类型等进行调整和开关,可以将性能调到最优。可在主流数据库之间任意切换 支持Oracle、MySQL、Postgres、MSSQL、GBase、SQLite、HSQL、Derby等数据库。除了API方式下的操作能兼容各个数据库之外,就连SQL的本地化查询也能使之兼容。JMX动态调节 可以用JMX查看框架运行统计。框架的debug开关和其他参数都可以使用JMX动态调整。动态表支持 表结构元数据的API也向用户开放,同时支持在使用过程中,灵活调整映射关系,因此用户可以用API动态的创建表结构的模型,从而实现各种动态类型和表的映射(例如POJO中包含一个Map,用于映射各种动态扩展的字段)企业级特性支持 SQL分析,性能统计,分库分表,Oracle RAC支持,读写分离支持 标签:eform
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sufengmarket

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值