📜 ⬆️ ⬇️

ORM in Android using ORMLite

Currently, there are several solutions for the Android platform that allow implementing the ORM-approach for working with the database, but there are two main ones. This is ORMLite and GreenDAO .

For beginners, it is worth making a digression and telling what an ORM is. ORM - object-ralational mapping. Object-relational mapping means that it is much more convenient for a programmer to operate with objects that he uses in his application, rather than with tables that store data in relational databases . For the connection of relational DBMS with the object model of the application, ORM technologies are used. So for solving the problems of object-relational mapping in Android use one of the third-party frameworks. GreenDAO and ORMLite are open source libraries.


GreenDAO

A framework from German developers that they use in their applications.
')
Benefits:

Does not use reflection ( Reflection ) to work
Top Performance for Android ORM
Minimum memory usage
The small size of the library does not have a big impact on the Android project.
Disadvantages:



ORMLite

A popular Java framework with an adapted version for Android.

Benefits:


Disadvantages:

... hmm ... I did not find them) everything that seems impossible, is realized with a deeper study of the docks.

I use OrmLite in several projects and am familiar with it quite closely. Therefore, just sit about it.

Simple annotation of classes


Each class mapping to the database (stored in the database) of which we want to do must be annotated. ( class-configuration can be used, but this is a completely different story) It is worth noting that the class must have a constructor without arguments. Annotation example:

@DatabaseTable(tableName = "goals") public class Goal{ public final static String GOAL_NAME_FIELD_NAME = "name"; @DatabaseField(generatedId = true) private int Id; @DatabaseField(canBeNull = false, dataType = DataType.STRING, columnName = GOAL_NAME_FIELD_NAME) private String name; @DatabaseField(dataType = DataType.DATE) private Date lastEditDate; @DatabaseField() private String notes; public Goal(){ scheduleList = new ArrayList<Shedule>(); priorities = new ArrayList<PrioritySchedule>(); } } 


The first annotation @DatabaseTable(tableName = "goals") indicates the name of the table where objects of this class will be mapped.
Before each field there should be an annotation @DatabaseField with different arguments. (you can also leave no arguments - everything will be set by default, and the name of the column in the table will be the same as the name of the field). The name field has three arguments. canBeNull = false means that this column cannot be empty in a table. dataType = DataType.STRING column type to a String type. (available types can be found here ). columnName = GOAL_NAME_FIELD_NAME - enforcing the name of a column in a table will help us later when building select queries to retrieve data from the database.
Any field can serve as the primary key - it is enough to specify id = true , but it is recommended to make the auto- generatedId = true id value and set it to generatedId = true . ORMLite will assign it a unique number.
For indexing a column in the database, index = true specified index = true
A full description of all available annotation field arguments can be found here.
Also, in addition to ORMLite annotations, you can use familiar annotations to some javax.persistence

Connect to SQLite in android


There are several ways to access database data using ORMLite on Android. You can inherit each activity from ORMLiteBaseActivity and then we will not have to follow the life cycle of the database connection, but we will not be able to get access from other classes. ( see examples )
Much more preferable is the approach discussed below.

You need to create a class that will instantiate an assistant in creating and working with the database:

 public class HelperFactory{ private static DatabaseHelper databaseHelper; public static DatabaseHelper getHelper(){ return databaseHelper; } public static void setHelper(Context context){ databaseHelper = OpenHelperManager.getHelper(context, DatabaseHelper.class); } public static void releaseHelper(){ OpenHelperManager.releaseHelper(); databaseHelper = null; } } 


Appeal to it will occur during the beginning and end of the life of the application:
This will prevent memory leaks due to an unclosed database connection.

 public class MyApplication extends Application{ @Override public void onCreate() { super.onCreate(); HelperFactory.setHelper(getApplicationContext()); } @Override public void onTerminate() { HelperFactory.releaseHelper(); super.onTerminate(); } } 


Just do not forget to describe our class successor Application in the manifest:

  <application android:name=".MyApplication" android:icon="@drawable/ic_launcher" android:label="@string/app_name" > 


Let's create the DataBaseHelper class, which will be responsible for creating the database and getting references to the DAO:

 public class DatabaseHelper extends OrmLiteSqliteOpenHelper{ private static final String TAG = DatabaseHelper.class.getSimpleName(); //        /data/data/APPNAME/DATABASE_NAME.db private static final String DATABASE_NAME ="myappname.db"; //   ,            onUpgrade(); private static final int DATABASE_VERSION = 1; //  DAO  ,    private GoalDAO goalDao = null; private RoleDAO roleDao = null; public DatabaseHelper(Context context){ super(context,DATABASE_NAME, null, DATABASE_VERSION); } //,         @Override public void onCreate(SQLiteDatabase db, ConnectionSource connectionSource){ try { TableUtils.createTable(connectionSource, Goal.class); TableUtils.createTable(connectionSource, Role.class); } catch (SQLException e){ Log.e(TAG, "error creating DB " + DATABASE_NAME); throw new RuntimeException(e); } } //,        @Override public void onUpgrade(SQLiteDatabase db, ConnectionSource connectionSource, int oldVer, int newVer){ try{ //  ,         TableUtils.dropTable(connectionSource, Goal.class, true); TableUtils.dropTable(connectionSource, Role.class, true); onCreate(db, connectionSource); } catch (SQLException e){ Log.e(TAG,"error upgrading db "+DATABASE_NAME+"from ver "+oldVer); throw new RuntimeException(e); } } //  GoalDAO public GoalDAO getGoalDAO() throws SQLException{ if(goalDao == null){ goalDao = new GoalDAO(getConnectionSource(), Goal.class); } return goalDao; } /  RoleDAO public RoleDAO getRoleDAO() throws SQLException{ if(roleDao == null){ roleDao = new RoleDAO(getConnectionSource(), Role.class); } return roleDao; } //    @Override public void close(){ super.close(); goalDao = null; roleDao = null; } } 


As you can see in the example, you need to describe the creation of fields and tables, as well as to obtain a reference to a singleton with a DAO object. Now consider the DAO itself.

DAO


The simplest implementation is the following class.

 public class RoleDAO extends BaseDaoImpl<Role, Integer>{ protected RoleDAO(ConnectionSource connectionSource, Class<Role> dataClass) throws SQLException{ super(connectionSource, dataClass); } public List<Role> getAllRoles() throws SQLException{ return this.queryForAll(); } } 


Here, only one additional method is created to obtain a collection of all objects of the Role class.
The main methods create, update, delete are implemented in the ancestor BaseDaoImpl.
To access the RoleDao class method in any application class, it suffices to contact the factory class:
RoleDAO roleDao = HelperFactory.GetHelper().getRoleDAO();

Creating queries


To build specific queries, you can create your own methods in the DAO class.

 public List<Goal> getGoalByName(String name) throws SQLException{ QueryBuilder<Goal, String> queryBuilder = queryBuilder(); queryBuilder.where().eq(Goal.GOAL_NAME_FIELD_NAME, "First goal"); PreparedQuery<Goal> preparedQuery = queryBuilder.prepare(); List<Goal> goalList =query(preparedQuery); return goalList; } 


As you can see, there was a search for objects with the corresponding name field.
When building more complex queries, in addition to eq (which means equals) there are others like gt (greater), ge (greater and equals) and the rest corresponding to the standard where-clauses in SQL (see the full list here )
To build a complex query, you can add and :
queryBuilder.where().eq(Goal.GOAL_NAME_FIELD_NAME, "First goal").and().eq(Goal.GOAL_NOTES_NAME_FIELD_NAME,”aaa”);

Accordingly, in addition to queries from the database in this way, you can delete and update tables .

Work with nested entities



Consider the relationship of a lot to one. Suppose a Goal object has a field pointing to Role.
Accordingly, in the class Goal field must be annotated
 @DatabaseField(foreign = true) private Role role; public void setRole(Role value){ this.role = value; } public Role getRole(){ return role; } 


Now we can save our objects in the database

  Goal g = new Goal(); g.setName(“asd”); Role r = new Role(); g.setRole(r); HelperFactory.getHelper.getRoleDAO().create(r); HelperFactory.getHelper.getGoalDAO().create(g); 


To access an object of the Role class that is associated with an object of type Goal:

  Goal g = HelperFactory.getHelper.getRoleDAO().getGoalByName(“asd”); Role r = g.getRole(); HelperFactory.getHelper.getRolelDAO().refresh(g); 


refresh() must be performed so that r gets all the fields from the database corresponding to this object.

When storing links to a collection, the approach is slightly different:

in class Goal:

  @ForeignCollectionField(eager = true) private Collection<Role> roleList; public addRole(Role value){ value.setGoal(this); HelperFactory.GetHelper().getRoleDAO().create(value); roleList.add(value); } public void removeRole(Role value){ roleList.remove(value); HelperFactory.GetHelper().getRoleDAO().delete(value); } 


The argument in the eager annotation means that all the objects from the roleList will be obtained from the database along with the retrieval of a Goal object. A reference to Goal in the Role object is required. And you also need to separately save each of the Role objects using RoleDAO.
Accordingly, in the class Role should be abstract:

  @DatabaseField(foreign = true, foreignAutoRefresh = true) private Goal goal; 


If you do not set eager=true , then lazy-initialization will be performed, i.e. when the Goal object is requested, the objects of the corresponding roleList collection will not be retrieved. To extract them, you will need to iterate them:

  Iterator<Role> iter = state.goal.getRoleList().iterator(); while (iter.hasNext()) { Role r = iter.next(); } 


Link to the library .
Link to tutorials .

Source: https://habr.com/ru/post/143431/


All Articles