当前位置:首页 > 安卓源码 > 技术博客 >

Enterlib for Android一些令人兴奋的功能的使用

时间:2018-12-25 22:45 来源:互联网 作者:源码搜藏 浏览: 收藏 挑错 推荐 打印

本文介绍了Enterlib for Android的一些令人兴奋的功能,如Object Relational Mapper和Dependency Injection Engine。

Enterlib for Android是一个框架,它有助于将应用程序的组件分离为独立的逻辑层,它们之间的通信是通过定义良好的接口或称为代码契约。框架的组件有助于编写可重用,健壮且可测试的代码片段,这些代码片段可以扩展,而对应用程序的其余部分影响很小。此外,该库还提供以下工具:

  • 数据绑定
  • 异步操作
  • 数据验证和转换
  • JSON序列化
  • 使用RESTful HTTP服务
  • SQLite ORM
  • 消息
  • 扩展视图
  • 的ViewModels

Maven存储库

该库托管在maven存储库中。因此,为了在项目中使用库,必须在主gradle配置文件中包含一个额外的存储库,如下所示:

buildscript {
    repositories {
        jcenter()
        maven { url "https://dl.bintray.com/ansel86castro/enterlib" }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.3'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        jcenter()
        maven { url "https://dl.bintray.com/ansel86castro/enterlib" }
    }
}

添加maven存储库后,您可以在模块中引用库,如下所示:

dependencies {
 ...... other dependencies

    compile 'com.cybtans:enterlib:2.0.0@aar'
 
 .......
}

使用代码

依赖注入

Enterlib通过DependencyContext实现IDependencyContext接口提供依赖注入引擎您可以使用此类注册单例或工厂等对象来创建对象实例。缓存机制由LifeType注册依赖项时指定控制您可以将多个IDependencyContext链接在一起创建一个上下文树,其中IDependencyContext对象的子项称为范围。根据LifeType规范,实例被缓存在请求它们的范围内IDependencyContext.dispose()完成范围后,应该调用该方法。dispose操作将释放缓存并处理实现com.enterlib.IClosablejava.io.Closable接口的所有对象

以下示例将显示创建根IDependencyContext和注册依赖项的最佳位置

class MainApp  extends Application {
 	
    IDependencyContext dependencyContext;

	@Override
 	public void onCreate() {
        super.onCreate();

        dependencyContext = new DependencyContext();
        DependencyContext.setContext(dependencyContext);

        //Register a  factory gives flexibility for creating instances. 
        //Other dependencies can be requested using the IServiceProvider parameter       
        
        dependencyContext.registerFactory(IEntityContext.class, new IDependencyFactory(){
                    @Override
                    public Object createInstance
                        (IServiceProvider serviceProvider, Class<?> requestType) {
                        EntityMapContext dbContext = new EntityMapContext(MainApp.this, "db.db3");
                        return dbContext;
                    }
                }, LifeType.Default);

         dependencyContext.registerType(SomeService.class, LifeType.Scope);
         dependencyContext.registerSingleton(Context.class, this);
         dependencyContext.registerTypes(IDomainService.class, MyDomainService.class, LifeType.Scope);
         
         // Creating a scope 
         IDependencyContext scope = dependencyContext.createScope();
         
         //Objects resolved from the scope that has LifeType.Scope 
         //when registered in the scope or in any of its parent's scopes are cached 
         //so the same instance is reused per scope request.                        
         IDomainService service = scope.getService(IDomainService.class);
         
         //Registering types for using in a scope only
         scope.registerTypes(IScopedDomainService.class, MyScopedDomainService.class, LifeType.Scope);
         
         //disposing a scope frees from the scope cache all objects with LifeType.Scope 
         scope.dispose();
  	}    
}

LifeType枚举表示依赖注入引擎将如何创建实例。它有以下选项:

  • 默认值:每次请求时都会创建对象。
  • 范围:每个范围请求只创建一次对象,请求之后将返回范围上的缓存实例。
  • Singleton:为每个请求返回相同的实例。如果单例是使用Factory注册的,那么它只会创建一次。

使用对象关系映射(ORM)进行数据访问

Enterlib为Android上的SQLite数据库提供了强大的对象关系映射器(ORM)。使用模型的类声明上的特定Java注释来驱动映射,如下所示:

@TableMap(name = "Accounts")
 public class Account{

     @ColumnMap(key = true)
     public int Id;

     @ColumnMap
     public String Name;

     @ColumnMap
     @ForeingKey(model = Currency.class)
     public int CurrencyId;

     @ExpressionColumn(expr = "CurrencyId.Name")
     public String CurrencyName;

     @ExpressionColumn(expr = "CurrencyId.CreateDate")
     public Date CurrencyCreateDate;

     @ColumnMap(column = "Id")
     @ForeingKey(model = Transaction.class, field = "AccountId")
     @ExpressionColumn(expr = "SUM(Transactions.Amount)")
     public double Balance;

     @ColumnMap(column = "Id", nonMapped = true)
     @ForeingKey(model = Transaction.class, field = "AccountId")
     @ExpressionColumn(expr = "Transactions.Description")
     public String Description;

     /// Definitions for navigation properties

     private Currency currency;
     public Currency getCurrency(){
         return currency;
     }
     public void setCurrency(Currency value){
         this.currency = value;
     }
 }

注释用于指定列映射,关系或计算列。最重要的注释是:

  • TableMap:可选,可用于标识映射表
  • ColumnMap:需要标识列字段映射,并且某些参数可用于添加其他信息,例如它是否可写,主键,列名或主键顺序。
  • ForeignKey:指定必须设置目标类和可选引用字段的外键关系。
  • ExpressionColumn:强大的机制,通过该机制,可以将当前类关键字关系字段包含在当前类声明中或使用聚合等计算列。您可以使用此批注来定义数据视图,方法与使用SQL视图的方式相同。

该示例的其余模型如下所示:

@TableMap(name = "Transactions")
 public class Transaction{

     @ColumnMap(key = true, writable = false)
     public int Id;

     @ColumnMap
     public String Description;

     @ColumnMap
     public double Amount;

     @ColumnMap
     @ForeingKey(model = Account.class)
     public int AccountId;

     @ExpressionColumn(expr = "AccountId.Name")
     public String AccountName;

     @ExpressionColumn(expr = "AccountId.CurrencyId.Name")
     public String CurrencyName;

     private Account account;

     public Account getAccount(){
         return account;
     }

     public void setAccount(Account value){
         this.account = value;
     }
 }

 @TableMap(name = "Currencies")
 public class Currency{

     @ColumnMap(key = true)
     public int Id;

     @ColumnMap(column = "Code")
     public String Name;

     @ColumnMap
     public Date CreateDate;
 }

 public class Category{

     @ColumnMap(key = true)
     public int Id;

     @ColumnMap
     public String Name;

     @ColumnMap(column = "Id", nonMapped = true)
     @ForeingKey(model = AccountCategory.class, field = "CategoryId")
     public ArrayList<AccountCategory> Accounts;
 }

 public class AccountCategory{

     @ColumnMap(key = true, order = 0)
     @ForeingKey(model = Account.class, field = "Id")
     public int AccountId;

     @ColumnMap(key = true, order = 1)
     @ForeingKey(model = Category.class, field = "Id")
     public int CategoryId;
 }

Enterlib Android还有助于部署sqlite数据库。如果您将SQLite数据库存储在应用程序资产目录中的文件(例如named)上db.db3,则可以使用EntityMapContext该类轻松部署它,如下所示:

EntityMapContext.deploy(this, "db.db3");

然后在部署数据库之后,下一步是创建一个实例IEntityContext此对象表示数据库连接,可用于检索IRepository<T>实例。

IEntityContext context = new EntityMapContext(MainApp.this, "db.db3");

另一方面,IRepository<T>实例用于查询或修改数据库。

IRepository<Transaction> map = context.getRepository(Transaction.class);
ArrayList<Transaction> list = map.query().toList();

在上面的示例中,查询将在计算为a时生成以下SQL语句List

SELECT t0.Description as "Description"
,t0.AccountId as "AccountId"
,t0.Amount as "Amount"
,t0.Id as "Id"
,t1.Name as "AccountName"
,t2.Code as "CurrencyName"
FROM "Transactions" t0
INNER JOIN "Accounts" t1 on t1.Id = t0.AccountId 
INNER JOIN "Currencies" t2 on t2.Id = t1.CurrencyId 

IRepository<T>还提供了用于创建,更新,删除的实体的方法,如下所示:

 //Creates a new entity in the persisting store
 transaction = new Transaction();
 map.create(transaction);

 //update the entity in the persisting store with new values
 map.update(transaction);

//delete the entity from the persisting store
 map.delete(transaction);

 //delete all entities satisfying the condition
 map.delete("Description = 'Abc'");

//returns the total count of entities
 map.query().count();

 //return the first element in the query
 transaction = map.query().first();

懒惰的评价

Enterlib Android的设计考虑到了性能优先,因此IEntityCursor<T>引入了它。IEntityCursor<T>是一种以更有效的方式迭代查询的机制。意义实体按需加载,优化内存使用,因此它是从查询中迭代大型结果集的推荐用法。

IRepository<Transaction> map = context.getRepository(Transaction.class); 
IEntityCursor<Transaction> cursor = map.query().toCursor();
for (Transaction t: cursor ) {  
      //do something with t
}
cursor.close();

使用游标的另一个示例,IEntityCursor<T>当没有更多元素要迭代时,隐式关闭游标

IRepository<Transaction> map = context.getRepository(Transaction.class); 

for (Transaction t: map.query() ) {  
      //do something with t
}

IEntityCursor<T>具有以下接口定义:

public interface IEntityCursor<T> extends IClosable, Iterable<T> {  
		//return the total of elements in the query
	   int getCount();  
	  
	  //return an element if at the specified position
	  T getItem(int position);  
  }

过滤表达式和函数

Enterlib的ORM for Android支持string表达式中的以下函数进行过滤或作为ExpressionColumn注释中的参数

  • sum(表达)
  • avg(表达)
  • count(表达)
  • max(表达)
  • min(表达)
  • concat(表达式):对于stringfields,返回值的连接
  • ifnullexp1exp2):exp2如果exp1是,则返回null
  • contains(表达)
  • exclude(表达)

例:

 IRepository<Account> map = context.getRepository(Account.class);
 IQuerable<Account> querable  = map.query()
                .include("Currency")
                .where("CurrencyId.Name = 'USD'")
                .where("AVG(Transactions.Amount) > 5")
                .orderBy("Name desc")
                .skip(5)
                .take(10);
                
ArrayList<Transaction> list = querable.toList();

该指令querable.toList()编译查询并生成以下SQL:

SELECT t1.CreateDate as "CurrencyCreateDate"
,t0.Id as "Id"
,total(t2.Amount) as "Balance"
,t0.CurrencyId as "CurrencyId"
,t0.Name as "Name"
,t1.Code as "CurrencyName"
,t1.Id as ".Currency.Id"
,t1.CreateDate as ".Currency.CreateDate"
,t1.Code as ".Currency.Name"
FROM "Accounts" t0
INNER JOIN "Currencies" t1 on t1.Id = t0.CurrencyId 
LEFT OUTER JOIN "Transactions" t2 on t2.AccountId = t0.Id 
WHERE t1.Code = 'USD'
GROUP BY t0.Id,t1.CreateDate,t0.Name,t1.Id,t0.CurrencyId,t1.Code
HAVING avg(t2.Amount) > 5
ORDER BY t0.Name DESC
LIMIT 10
OFFSET 5

这是使用该include方法的另一个例子include方法将相关实体添加到结果集中:

IRepository<Transaction> map = context.getRepository(Transaction.class);

IQuerable<Transaction> query  = map.query()
         .include("Account.Currency")
         .where("Account.Currency.Name = 'USD'");

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

因此,它将打印以下SQL语句:

SELECT t0.Id as "Id"
,t0.Description as "Description"
,t0.Amount as "Amount"
,t0.AccountId as "AccountId"
,t1.Name as "AccountName"
,t2.Code as "CurrencyName"
,t1.Id as ".Account.Id"
,t1.Name as ".Account.Name"
,t1.CurrencyId as ".Account.CurrencyId"
,t2.Code as ".Account.CurrencyName"
,t2.CreateDate as ".Account.CurrencyCreateDate"
,total(t3.Amount) as ".Account.Balance"
,t2.Id as ".Account.Currency.Id"
,t2.Code as ".Account.Currency.Name"
,t2.CreateDate as ".Account.Currency.CreateDate"
FROM "Transactions" t0
INNER JOIN "Accounts" t1 on t1.Id = t0.AccountId 
INNER JOIN "Currencies" t2 on t2.Id = t1.CurrencyId 
LEFT OUTER JOIN "Transactions" t3 on t3.AccountId = t1.Id 
WHERE t2.Code = 'USD'
GROUP BY t2.Code,t0.Description,t1.Name,t2.Id,t0.Amount,t0.Id,
         t1.CurrencyId,t1.Id,t0.AccountId,t2.CreateDate

表达式Account.Currency.NameAccountId.CurrencyId.Name传递where方法是等价的 - 它们产生相同的结果。Enterlib遵循一组约定,以便查找给定导航属性的外键。

另一方面,要使用该include方法,必须声明要使用相关对象注入的navigation属性。例如,下面定义AccountIdTransaction类中字段的导航属性按照惯例,它将AccountId, Accountid, Account_id在使用时查找选项include(Account)以查找导航属性的外键Account

public class Transaction{
   // other code ....

   private Account account;

   public Account getAccount(){
       return account;
   }

   public void setAccount(Account value){
       this.account = value;
   }

  // other code ....
}

在关联模型中使用字段的别名

ORM支持的表达式可以包含相关模型的字段的别名。别名只不过是用于引用属于关联模型的那些字段的简短形式。例如:

IRepository<Account> map = context.getRepository(Account.class);
IQuerable<Account> querable  = map.query()
                .where("CurrencyName = 'EUR'");

在前面的示例中,该字段CurrencyName是别名CurrencyId.Name您可以观察CurrencyName使用ExpressionColumn注释定义字段,如下所示:

@TableMap(name = "Accounts")
public class Account{
    // other code ....

    @ExpressionColumn(expr = "CurrencyId.Name")
    public String CurrencyName;

   // other code ....
}

生成的查询将编译为以下SQL语句:

SELECT t1.CreateDate as "CurrencyCreateDate"
,t0.Id as "Id"
,total(t2.Amount) as "Balance"
,t0.CurrencyId as "CurrencyId"
,t0.Name as "Name"
,t1.Code as "CurrencyName"
FROM "Accounts" t0
INNER JOIN "Currencies" t1 on t1.Id = t0.CurrencyId 
LEFT OUTER JOIN "Transactions" t2 on t2.AccountId = t0.Id 
WHERE t1.Code = 'EUR'
GROUP BY t0.Id,t1.CreateDate,t0.Name,t0.CurrencyId,t1.Code

提前过滤

Enterlib提供扩展的过滤功能,如ContainsExclude,请注意它们不区分大小写。例如,以下查询将检索与至少五个实体关联的所有Category对象Account

IRepository<Category> map = context.getRepository(Category.class);
IQuerable<Category> querable  = map.query()
                .where("CONTAINS(COUNT(Accounts.AccountId) > 5)");

查询计算时,它将生成以下SQL语句:

SELECT t0.Id as "Id"
,t0.Name as "Name"
FROM "Category" t0
WHERE t0.Id IN (SELECT t0.CategoryId as "CategoryId"
FROM "AccountCategory" t0
GROUP BY t0.CategoryId
HAVING count(t0.AccountId) > 5)

使用该exclude函数的方式相同,您可以查询所有与任何实体Category没有关联对象AccountCurrency'UYU'

IRepository<category> map = context.getRepository(Category.class);
IQuerable<category> querable  = map.query()
                .where("EXCLUDE(Accounts.AccountId.CurrencyId.Name = 'UYU')");
 SELECT t0.Id as "Id"
,t0.Name as "Name"
FROM "Category" t0
WHERE t0.Id NOT IN (SELECT t0.CategoryId as "CategoryId"
FROM "AccountCategory" t0
INNER JOIN "Accounts" t1 on t1.Id = t0.AccountId 
INNER JOIN "Currencies" t2 on t2.Id = t1.CurrencyId 
WHERE t2.Code = 'UYU') 

在本文中,我们学习了如何利用Enterlib提供的依赖注入引擎,以便为我们的应用程序创建更多的模块化和分离式架构。对象关系映射引擎显示了在使用脱机数据时节省时间的良好潜力。如前所示,它支持非常富有string表现力的表达式,用于过滤和映射配置。

Enterlib for Android一些令人兴奋的功能的使用 转载https://www.codesocang.com/appboke/39192.html

技术博客阅读排行

最新文章