Room Android Entities and POJOs must have a usable public constructor - java

I'm trying to insert data into database but always get compile time error mentioned below is there anything I'm missing ?
Error i'm getting
error: Entities and POJOs must have a usable public constructor. You can have an empty constructor or a constructor whose parameters match the fields (by name and type). - java.util.List
User Response Model class
#Entity(tableName = "RoomDemo")
data class UserResponse(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "code")
#SerializedName("code")
var code: Int,
#Embedded
#SerializedName("data")
var `data`: Data,
#Embedded
#SerializedName("errors")
var errors: Errors,
#ColumnInfo(name = "message")
#SerializedName("message")
var message: String
)
Data model class
data class Data(
#ColumnInfo(name = "avater")
#SerializedName("avater")
var avater: String,
#Embedded(prefix = "avater")
#SerializedName("user_info")
var userInfo: UserInfo
)
User Info Model class
data class UserInfo(
#ColumnInfo(name = "location")
#SerializedName("location")
var location: String,
#Embedded
#SerializedName("mediafiles")
var mediafiles: List<Mediafile>)
Dao Interface
#Dao
interface CrushDao {
#Insert(onConflict = REPLACE)
fun insert(userResponse: UserResponse)
}
My Database
#Database(entities = [UserResponse::class], version = 2)
abstract class CrushDataBase : RoomDatabase()
{
abstract fun crushDao():CrushDao
companion object{
private var INSTANCE: CrushDataBase? = null
fun getDatabase(context: Context): CrushDataBase? {
if (INSTANCE == null) {
synchronized(CrushDataBase::class) {
INSTANCE = Room.databaseBuilder(
context.getApplicationContext(),
CrushDataBase::class.java, "crushDemo.db"
).build()
}
}
return INSTANCE
}
}
}

#Embedded
#SerializedName("mediafiles")
var mediafiles: List<Mediafile>
AFAIK, you cannot use #Embedded for arbitrary things, which includes lists.
Either change this to use relations or use #TypeConverter and #TypeConverters to convert your List<Mediafile> into some valid column type, such as a String.

Related

Jpa - How to "findBy" two columns while both of them have the same name

I have a Character-in-Category based Jpa relationship here:
// Character.kt
#Entity
class Character (
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long? = null,
var name: String, //This one
#ManyToOne #JoinColumn(name = "category_name", referencedColumnName = "name")
var category: Category? = null,
var gender: Gender? = null
): Serializable {
enum class Gender {
MALE, FEMALE, OTHER
}
}
// Category.kt
#Entity
class Category (
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long? = null,
var name: String? = null, //Also this one
#OneToMany(mappedBy = "category") #JsonIgnore
var characters: MutableSet<Character> = mutableSetOf()
) : Serializable
I have done some "findBy" queries by multiple columns before, but this is the first time that both of them have the same name and i suppose its not working because of that, how can i reach this without changing any of their property names?
// CharacterRepository.kt
#Repository
interface CharacterRepository : JpaRepository<Character, Long> {
fun findByNameAndCategory_Name(name: String, categoryName: String): Character
}
Edit: What is not working is that findBYNameAndCategory_Name, always returning Result must not be null as a EmptyResultDataAccessException despite the data actually exists in database.
Spring Data follows the path from the root entity in the implements (JpaRepository<Character, Long>): this should work:
Optional<Character> findByNameAndCategoryName(String name, String categoryName)
I don't use Kotlin, so this is Java syntax, but the same should apply for Kotlin: the method name matters.

How do I set up a Foreign Key

What I have setup are two tables, one for a user created account, and the other that lets the user buy a product.
I have both tables set up like so
Customer Table
#PrimaryKey(autoGenerate = true)
private int custId;
#ColumnInfo(name = "user_name")
private String userName;
#ColumnInfo(name = "password")
private String password;
#ColumnInfo(name = "first_name")
private String firstName;
#ColumnInfo(name = "last_name")
private String lastName;
#ColumnInfo(name = "address")
private String address;
#ColumnInfo(name = "city")
private String city;
#ColumnInfo(name = "postal_code")
private String postalCode;
#ColumnInfo(name = "country")
private String country;
Phone Table
#PrimaryKey(autoGenerate = true)
private int productId;
private String phoneMake;
private String phoneModel;
private String phoneColor;
private String storageCapacity;
private Float price;
What I have set up are two foreign keys, one in each table. My last table is for ordering the phones, which requires using both Primary Keys from each table. What I feel like I need is a ForeignKey, similar in vein to the PrimaryKey already created. The problem is that I am unsure how to implement that into the program. Everything I try doing is not working. I have looked at the documentation, but nothing clicks. I hope you can help me with the correct screenshot. If more is needed let me know (This code is written in Java code)
If you simply want a Customer to have 1 phone, then you have have a single column (member variable) for the relationship that will store the phone's product id.
e.g.
private int mapToPhone; //<<<<< ADDED no need for #ColumnInfo the column name will be as per the variable name.
Obviously you set the value to an appropriate value.
To then get the Customer with the phone's details then you have a POJO that embeds the parent (Customer) using the #Embedded annotation has the child (Phone) using the #Relation annotation.
e.g. :-
class CustomerWithPhoneDetails {
#Embedded
Customer customer;
#Relation(
entity = Phone.class,
parentColumn = "mapToPhone",
entityColumn = "productId"
)
Phone phoneDetails;
}
You can then have a method in the #Dao annotated interface/abstract class which queries the parent table BUT returns the POJO or list/array of the POJO e.g. :-
#Query("SELECT * FROM Customer")
abstract List<CustomerWithPhoneDetails> getAllCustomersWithPhoneDeytails();
Example
Based upon your code, and the additional example code along with an #Database annotated abstract class :-
#Database(entities = {Customer.class,Phone.class}, version = 1, exportSchema = false)
abstract class TheDatabase extends RoomDatabase {
abstract AllDao getAllDao();
private static volatile TheDatabase instance = null;
public static TheDatabase getInstance(Context context) {
if (instance == null) {
instance = Room.databaseBuilder(context,TheDatabase.class,"the_database.db")
.allowMainThreadQueries()
.build();
}
return instance;
}
}
and an Activity e.g. :-
public class MainActivity extends AppCompatActivity {
TheDatabase db;
AllDao dao;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = TheDatabase.getInstance(this);
dao = db.getAllDao();
long phone01ProductId = dao.insert(new Phone("PhoneMaker001","Model001","Color001","100Mb",111.11F));
long phone02ProductId = dao.insert(new Phone("PhoneMaker002","Model002","Color002","200Mb",222.22F));
dao.insert(new Customer("c001","password001","firstname001","lastname001","address001","city001","country001","postcode001",(int) phone01ProductId));
dao.insert(new Customer("c002","password002","firstname002","lastname002","address002","city002","country002","postcode002",(int) phone02ProductId));
for(CustomerWithPhoneDetails cwpd: dao.getAllCustomersWithPhoneDeytails()) {
Log.d("DBINFO","Customer is " + cwpd.customer.getUserName() + " etc. Phone is " + cwpd.phoneDetails.getProductId() + " etc." );
}
}
}
Note that suitable constructors have been coded in both the Phone and Customer class (default/empty constructor and one, annotated with #Ignore annotation that allows all values bar the id to be passed as used in the example below)
Note that ideally long rather than int should be used for the id columns.
Results
The Log :-
D/DBINFO: Customer is c001 etc. Phone is 1 etc.
D/DBINFO: Customer is c002 etc. Phone is 2 etc.
App Inspection :-
and :-

Convert Database Entity from Java to Kotlin

I am currently converting my projects to Kotlin and I have an app with Room database using Java.
My Entity in Java
#Entity(tableName = "store")
#Fts4
public class Store {
#PrimaryKey
#ColumnInfo(name = "rowid")
private Long identification;
#NonNull
#ColumnInfo(name = "name")
private String name;
#ColumnInfo(name = "location")
private String location;
#ColumnInfo(name = "days_open")
private int daysOpen;
public Store(Long identification, #NonNull String name, String location, int daysOpen) {
this.identification = identification;
this.name = name;
this.location = location;
this.daysOpen = daysOpen
}
public Long getIdentification() {
return identification;
}
#NonNull
public String getName() {
return name;
}
public String getLocation() {
return location;
}
public int getDaysOpen() {
return daysOpen;
}
}
I convert it this way to Kotlin
#Entity(tableName = "store")
#Fts4
data class Store(
#PrimaryKey #ColumnInfo(name = "rowid")
val identification: Long?,
#ColumnInfo(name = "name")
val name: String,
#ColumnInfo(name = "location")
val location: String?
#ColumnInfo(name = "days_open")
val daysOpen: Int?
)
Now I am having this error
java.lang.IllegalStateException: Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. You can simply fix this by increasing the version number.
Do we really need to do migration in this? Or I am wrong converting things. I am using Room 2.3.0.
implementation "androidx.room:room-ktx:2.3.0"
kapt "androidx.room:room-compiler:2.3.0"
When I updated the database version, this is the error
java.lang.IllegalStateException: A migration from 1 to 2 was required but not found. Please provide the necessary Migration path via RoomDatabase.Builder.addMigration(Migration ...) or allow for destructive migrations via one of the RoomDatabase.Builder.fallbackToDestructiveMigration* methods.
I added this code to my database
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("
// put changes here
")}
}
I don't know what to put inside of migrate function. Any idea?
Exception message seems to be quite clear. you need to update the version of your room database.
Go to the class that extends RoomDatabase and increment the value of version attribute in #Database annotation.
#Database(entities = [A::class, B::class], version = 2)
abstract class YourRoomDatabase: RoomDatabase()
Already got the solution.
The problem is my int in Java Entity is different w/ Int in Kotlin. Thus, I need to update my schema by Migration. My solution is referenced
here.

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

Creating custom user types for jsonb columns in hibernate postgresql

I am trying to migrate my spring mvc project database from mongodb to postgresql.
Spring version 4.3.8.RELEASE
Hibernate version 5.2.10.Final
Postgresql db version is 9.6
I am trying to implement create, read, update operations for the following entity.
BeneficiaryData.java
#Document(collection = "beneficiary_data")
public class BeneficiaryData extends BaseEntity {
#Id
private String id;
private Integer beneficiaryId;
private Map<Integer, Object> customData;
private Map<Integer, List<Integer>> customMultipleData;
private Date lastUpdatedOn;
public BeneficiaryData() {
}
}
In mongodb this entity was stored as a json document and the conversion from json to object for the fields customData, customMultipleData, rcfData were handled by spring data.
For storing this in postgresql I thought of using the following table structure making use of postgresqls jsonb type.
CREATE TABLE ebbin.beneficiary_data
(
id integer NOT NULL DEFAULT nextval('bms_v21.beneficiary_data_id_seq'::regclass),
beneficiary_id integer NOT NULL,
custom_data jsonb,
custom_multiple_data jsonb,
last_updated_on timestamp(3) without time zone,
CONSTRAINT beneficiary_data2_pkey PRIMARY KEY (id)
)
WITH (
OIDS = FALSE
)
TABLESPACE pg_default;
For handling the two jsonb types I implemented two cutom hibernate UserTypes
JSONBCustomDataUserType.java
public class JSONBCustomDataUserType extends CollectionUserType implements ParameterizedType {
private static final String JSONB_TYPE = "jsonbCustomData";
public static final String CLASS = "CLASS";
#Override
public Class<Object> returnedClass() {
return Object.class;
}
#Override
public int[] sqlTypes() {
return new int[] { Types.JAVA_OBJECT };
}
// ... implementations for nullSafeGet, nullSafeSet ...
}
and JSONBCustomMultipleDataUserType.java
public class JSONBCustomMultipleDataUserType extends CollectionUserType implements ParameterizedType {
private static final String JSONB_TYPE = "jsonbCustomData";
public static final String CLASS = "CLASS";
#Override
public Class<Object> returnedClass() {
return Object.class;
}
#Override
public int[] sqlTypes() {
return new int[] { Types.JAVA_OBJECT };
}
// ... implementations for nullSafeGet, nullSafeSet ...
}
My entity for postgresql is now
BeneficiaryData.java
#TypeDefs({
#TypeDef(name = "jsonbCustomData", typeClass = JSONBCustomDataUserType.class,
parameters = {#Parameter(name = JSONBCustomDataUserType.CLASS, value = "java.util.Map")}),
#TypeDef(name = "jsonbCustomMultipleData", typeClass = JSONBCustomMultipleDataUserType.class,
parameters = {#Parameter(name = JSONBCustomMultipleDataUserType.CLASS, value = "java.util.Map")})
})
#Entity
#Table(name = "beneficiary_data")
public class BeneficiaryData extends BaseEntity {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Integer id;
#Column(name = "beneficiary_id", nullable = false)
private Integer beneficiaryId;
#Type(type = "jsonbCustomData")
#Column(name = "custom_data", nullable = true)
private Map<Integer, Object> customData;
#Type(type = "jsonbCustomMultipleData")
#Column(name = "custom_multiple_data", nullable = true)
private Map<Integer, List<Integer>> customMultipleData;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "last_updated_on", length = 19)
private Date lastUpdatedOn;
public BeneficiaryData() {
}
// ...
}
Also created a custom postgresql dialect so that the new user types can be registered.
CustomPostgreSqlDialect.java
public class CustomPostgreSqlDialect extends PostgreSQL95Dialect {
public CustomPostgreSqlDialect() {
super();
this.registerColumnType(Types.JAVA_OBJECT, "jsonbCustomData");
this.registerColumnType(Types.JAVA_OBJECT, "jsonbCustomMultipleData");
}
}
However when I try to save an entity using session.save() I get the following error.
org.springframework.dao.InvalidDataAccessResourceUsageException: could not insert: [com.tcs.bms.persistence.entity.BeneficiaryData];
.
.
.
Caused by: org.postgresql.util.PSQLException: Unknown type jsonbCustomData.
I am guessing this has something to do with me registering column type JAVA_OBJECT to both jsonbCustomData and jsonbCustomMultipleData
I am saying this because when I tried with just the jsonbCustomData user type (after commenting out the customMultipleData field in the entity and removing from the table), I was able to successfully insert, update and fetch data.
Any idea on what I am doing wrong ?
Might be a little late to the party, but without knowing the exact solution to the problem, I'd have two suggestions:
Based on your PSQLException of Unknown type, your dialect might need to look like this:
public class CustomPostgreSqlDialect extends PostgreSQL95Dialect {
public CustomPostgreSqlDialect() {
super();
this.registerColumnType(Types.JAVA_OBJECT, "jsonb");
}
}
...as the according javadoc states, that the second attribute needs to be 'The database type name' & PSQL won't know anything about a type named "jsonbCustom.."
Don't even write a custom dialect! I don't know the content of your extended CollectionUserType, but I got a working project with a pretty similar setup (Spring 4.3.14 / Hibernate 5.2.12 / PSQL 9.5) and all I had to do was creating custom UserTypes & registering/annotate those types to the class & their according fields. If you prefer to I'm happy to share the code.

Categories