Is it possible using String as PrimaryKey in Android Room - java

I use uuid in my java backend server. So I need to use that uuid in room android to make sure entities are sync'd properly.
I am aware of Is it possible to apply primary key on the text fields in android database and text as primarykey in android. I want to create something like this
#Entity(tableName = CrewColumns.TABLE_NAME)
#TypeConverters(BigDecimalConverter::class)
#JsonIgnoreProperties(ignoreUnknown = true)
class Crew() {
constructor (uuid: String) : this() {
this.uuid = uuid;
}
/**
* The unique ID of the item.
*/
#PrimaryKey
#ColumnInfo(name = CrewColumns.UUID)
var uuid: String = ""
#ColumnInfo(name = CrewColumns.NAME)
var name: String = ""
}
Will it be a problem with Room (DAO etc)? Thank you.

Yes, you can use a String as a #PrimaryKey.
Additionally, I also recommend making use of Kotlin's data class to simplify your entities. For example:
#Entity
data class Crew(#PrimaryKey val uuid: String, val name: String) {
// Put any functions or other members not initialized by the constructor here
}

Related

Android Room: How to use embedded with a custom query

I have a PostDAO, that looks like this.
#Dao
public interface PostDAO extends DAOTemplate<Post> {
#Query("SELECT * FROM posts order by time DESC")
LiveData<List<Post>> getPosts();
}
And the Post Pojo being.
#Keep
#Entity(tableName = "posts")
open class Post : Serializable, Cloneable {
#NonNull
#PrimaryKey
var id: String? = null
var text: String? = null
var time: Long = 0
var uid: String? = null
#Embedded
var user: User? = null
public override fun clone(): Post {
return super.clone() as Post
}
}
As you can see, the User object is #Embedded
And User's DAO
#Dao
public interface UserDAO extends DAOTemplate<User> {
#Query("SELECT *,(SELECT sound_time FROM sounds WHERE sound_uid =:id AND " +
"(sound_time < strftime('%s', 'now'))) AS hasNewMusic " +
"FROM users WHERE user_uid = :id")
LiveData<User> getUser(String id);
}
And User Pojo
#Keep
#IgnoreExtraProperties
#Entity(tableName = "users")
class User : ModelTemplate() {
#NonNull
#PrimaryKey
#ColumnInfo(name = "user_uid")
override var id: String? = null;
var name: String? = null
var icon: String? = null
var hasNewMusic: Boolean = false
}
Now, I want the Embedded user field on the Post Object, to have the field, hasNewMusic, populated with a subQuery.
Tried the above, not working and not sure about if this is how to go about this.
First of all there is no Boolean in SQLite and Room (as I've read) maps 1 and 0 INTEGER values to Boolean's true and false (see Hardcode Boolean Query In Room Database). By the name I can guess sound_time is not bounded in 1 and 0 values. So, try to convert sound_time to it.
Also, I think you misused #Embedded annotation. It's used to put columns of one table into classes that can be grouped by some reason. For example here (#Embedded) is written:
So if you have a query that returns street, latitude, longitude, Room
will properly construct an Address class.
So you should either modify PostDAO::getPosts() method to return columns of a User, through INNER JOIN plus your giant select. And I don't guarantee this will work as I haven't done that. Also It's bad for some reasons.
Or you can leave Post without User field and make an Entity called PostWithUser and use a #Relation annotation. Wich is better and recommended in documentation.
UPD:
To fetch two entities try this:
Remove User field from post.
Add ForeignKey to Post. I'll use userId, if uid is already for
that use it instead:
#Entity(tableName = "posts",
foreignKeys = [ForeignKey(
entity = Post::class,
parentColumns = ["user_id"], //from ColumnInfo of User class
childColumns = ["userId"],
onDelete = CASCADE)],
indices = [Index(value = ["userId"]]))
class Post {
#PrimaryKey
var id: String? = null
var userId: String? = null
//...else code....
}
Make PostWithUser class:
class PostWithUser {
#Embedded lateinit var post: Post
#Embedded lateinit var user: User
}
Make PostWithUserDao:
class PostWithUserDao {
#Query("select * from post, user where post.userId = user.user_id")
fun getPostsWithUsers(): List<PostWithUser>
}
And as I didn't manage to fit sound_time subquery here, I would do it in a second query, but if you figure out how to do it, I think it can work.
Also see this: Room database with one-to-one relation

Spring Data JPA select sequence for different bases, and set value to entity

I need implement next logic:
I have entity:
#Data
#Entity
#Table(name = "USERS")
public class User {
#Id
#Column(name = "GUID")
private String guid;
#Column(name = "MESSAGE_ID")
private String messageId;
#Column(name = "SOME_VALUE")
private String someValue;
And I need set to someValue generated value consisting of
"some prefix"+sequencefrom DB + "some suffix";
I can make select sequense from Db, generate vsomeValue and set it to entity, but maby Is there a way to make it easier? Because in my version I use two bases, and I have to write two native query for select a sequence and use the appropriate one depending on the profile.
I need somthig like this:
#Column(name = "SOME_VALUE")
#Value(MyGenerator.class)
private String someValue;
and in MyGenerator.class implement logic for generate someValue from prefix, sequence and suffix.
Instead of annotating the class members, annotate the getters and setters, and put your logic there.
Further reference in this question.
You are looking for Custom Sequence based ID generator.
This is a nice article on it which might help you.

ORMLite - OneToOne relation

I got next database structure with OneToOne relation:
[company]
company_id (PK)
company_name
[company_configuration]
company_configuration_id (Autoincrement, PK)
company_id (UNIQUE KEY,FK)
company_configuration_v
I have been using ORMlite and I have next classes for this two tables:
#DatabaseTable(tableName = "company")
public class Company {
public static final String ID_COMPANY = "company_id";
public static final String COMPANY_NAME = "company_name";
#DatabaseField(generatedId = true, columnName = ID_COMPANY)
private int idCompany;
#DatabaseField(columnName = COMPANY_NAME)
private String companyName;
#DatabaseTable(tableName = "company_configuration")
public class CompanyConfiguration {
public static final String COMPANY_CONFIGURATION_ID = "company_configuration_id";
public static final String COMPANY_ID = "company_id";
public static final String COMPANY_CONFIGURATION_V = "company_configuration_v";
#DatabaseField(generatedId = true, columnName = COMPANY_CONFIGURATION_ID)
private int idCompanyConfiguration;
#DatabaseField(foreign = true,foreignAutoRefresh = true, columnName = COMPANY_ID)
private Company companyId;
#DatabaseField(columnName = COMPANY_CONFIGURATION_V)
private String companyConfigurationV;
Here is OneToOne relation because I want to divide a table with many columns.
As you can see in the example above, there is not relation from Company class to CompanyConfiguration class.
I know that I can add this snippet of code(examle below) into Company class, but I don't need a #ForeignCollectionField becaues the collection will contain only one CompanyConfiguration object:
#ForeignCollectionField()
private ForeignCollection<CompanyConfiguration> companyConfigurations;
I need to add something like this (examle below) into Company class and will get the reference from Company class to CompanyConfiguration class:
#OneToOne(targetEntity = CompanyDbConfig.class)
#JoinTable(name = "company_configuration")
#JoinColumn(name = "id_company")
CompanyConfiguration companyConfiguration;
Shortly, I want to get Company object using ORMlite. See the example below. After fetching company from the database, I want to have and CompanyConfiguration object within company object.
Company company = daoCompany.queryForId(id); //daoCompany is an instance of ORMlite Dao class
Is it possible and how to do that using ORMlite?
I posted an OrmLite question myself so I looked through the unanswered questions to see if there was anything I could answer. Even though this is an old topic, I wanted to take a stab at it in case it could help someone.
I've read your post a few times and I think you're asking how to load the information from two tables into one model. You're separating a rather large table into two in the database but you want it to come back as one model. If that is correct, here's my take on the code. This assumes you want to use objects to build the query instead of passing in a query string.
public class CompanyResult
{
public long CompanyId { get; set; }
public long ConfigurationId { get; set; }
public string Name { get; set; }
public string ConfigurationV { get; set; }
}
var query = _db.From<Company>
.Join<CompanyConfiguration>((c, cc) => c.idCompany == cc.idCompany)
.Where(c => c.idCompany == companyId)
.Select<CompanyConfiguration>((c, cc) = new {
CompanyId = c.idCompany,
ConfigurationId = cc.idCompanyConfiguration,
Name = c.companyName,
ConfigurationV - cc.companyConfigurationV
});
var results = _db.Single<CompanyResult>(query);
You'd keep your existing models so they could be used as DTOs. You'd just be using the new model model above to pass back the exact properties you want.
*I wrote this in Notepad++, forgive any typos.

How to return an entity by property if it already exists?

#Entity
public class Language {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(length = 2)
private String code; //EN, DE, US
public Language(String code) {
this.code = code;
}
}
#Entity
public class ProductText {
#OneToOne(Cascade.ALL)
private Language lang;
}
ProductText text = new ProductText();
text.setLang(new Language("en")); //what if "en" exists?
dao.save(text);
Now, when I persist the ProductText, everytime a new Language object would be generated.
Can I prevent this, and in case a language table entry with code = 'en' exists this existing entity should be linked instead.
My initial goal is to not having to repeat the countryCodeString "EN" multiple times in my product-text table, but just reference the id. But does this really make sense? Should I rather just use the plain String without an extra table? (I later want to query a list of productTexts where lang = 'de').
Is the only change executing a select like dao.findByLang("en") before?
Or is there also some hibernate feature that would support this without explicit executing a query myself?
Do you process the value "en" further or do you display it directly? If only used for displaying purposes, I would just store the string, but if you want to reduce redundancy by using foreign key IDs you have to create an Entity containing the language string en which can be persisted via entity manager and which you have to obtain out of the entity manager before persisting to reuse it.
If there is only three different possible values for the language, you can also use an enum like thisĀ :
public enum Language {
EN("EN"),
DE("DE"),
US("US");
private String code; //EN, DE, US
public Language(String code) {
this.code = code;
}
// Getter...
}
#Entity
public class ProductText {
#Enumerated(EnumType.STRING)
// Or #Enumerated(EnumType.ORDINAL)
private Language lang;
}
EnumType.STRING will store the enum in the database as a String, while EnumType.ORDINAL will store it as an int. Int is maybe a little more efficient, but the mapping could change if you insert a new value in your enum. String is more flexible since it will use the names of your enum members.
In both case, you don't have to manage a separate entity and hibernate will not create an additional table, and it's more type-safe than using a plain string.
If the only value in Language is a 2 or 3 letter string, why not just have the string as a member? This will be quicker and more efficient.

Foreign Key update from JSON using ORMLite

I am using ORMLite within an Android application along side Gson and currently struggling with the following issue.
Within my app, there are multiple classes that make use of ORMLite/Gson, for simplicity I shall describe the issue using only two.
Say we have a class Product:
#SerializedName("product_id")
#DatabaseTable(tableName = "products")
public class Product {
public Product() {
}
#DatabaseField(id = true)
private int id;
// Generic stuff
#DatabaseField
#SerializedName("product_desc")
private String desc;
#DatabaseField
#SerializedName("in_stock")
private boolean inStock;
#DatabaseField(unique = true)
#SerializedName("product_name")
private String name;
// Issue occurs here
#DatabaseField(foreign = true, foreignAutoRefresh = true)
private Venue venue;
}
and we have a class "Venue`:
#DatabaseTable(tableName = "venues")
public class Venue {
public Venue() {
}
#SerializedName("venueid")
#DatabaseField(id = true)
private int id;
// Other Generic Junk
#DatabaseField
private String desc;
#DatabaseField
private String email;
#DatabaseField
private String fax;
#DatabaseField
#SerializedName("venue_name")
private String name;
#DatabaseField
private String phoneNumber;
}
I use Gson to deserialize Json from a pre-written API and ORMLite populate the database with this data. An issue occurs as the API returns the venue id of the venue each product is associated with (e.g venueid = 1) and not a Venue object.
However, the database is already populated with these venues so venueid = 1 refers to a real venue within the current database.
The trouble is getting ORMLite to understand this and update the Venue object within Product to be that of id = 1!
Can anyone think of a solution?
EDIT:
To better understand my issue, here is some sample Json:
[
{
"productid": 1,
"venueid": 4,
"product_name": "Jack Daniels",
"in_stock": true,
"orders_accepted": true,
}
...
]
As you can see, I get an int for venueid and NOT a Venue object. Is there an easy way to convert it to it's corresponding venue without the large overhead of multiple queries
This is an high level answer based on assumption that:
your API returns you also a Json string for every Venue object or
you can easily get a Json string for every Venue object you have in your DB (sorry I do not know ORMLite at all, even if from its name, I can image what it does ;) ).
So, somehow build a little dictionary like a Map<Integer, String> where the int key is your venue id and value is Json string for that Venue object.
Then, when you get Product Json from API, do a simple string replacement using a regexp.
For example, you should transform:
{
"productid": 1,
"venueid": 4,
"product_name": "Jack Daniels",
"in_stock": true,
"orders_accepted": true,
}
into:
{
"productid":1,
"venue":{
"venueid":4,
"desc":"aDesc",
"email":"aEmail",
"fax":"aFax",
"venue_name":"aname",
"phoneNumber":"aPhonenumber"
},
"product_name":"Jack Daniels",
"in_stock":true,
"orders_accepted":true
}
After string replacement you should be able to parse the updated Json string into your data structure without involving database anymore. Think this like something a "Json lookup".
If you can read Venue from DB, your query cost will be only a "select all" to fill the map, after that string replacement will occur in memory only.

Categories