foreign key constraint failed (code 787 sqlite_constraint foreign key) - java

I have been using the Android Studio App for about a month now,in order to create a project for a university subject.
Unfortunatelly,I came across a problem while I was trying to create a database using Room that consists of 3 tables.
Just to let you know,I have read all the threads reffering to this problem and unfortunatelly I couldn't solve it...
#Entity(tableName = "travelAgencies")
public class travelAgency {
#PrimaryKey
#ColumnInfo(name = "tACode")
private int agencyCode;
#ColumnInfo(name = "tAName")
private String agencyName;
#ColumnInfo(name = "tAAddress")
private String agencyAddress;
public int getAgencyCode() {
return agencyCode;
}
public void setAgencyCode(int agencyCode) {
this.agencyCode = agencyCode;
}
public String getAgencyName() {
return agencyName;
}
public void setAgencyName(String agencyName) {
this.agencyName = agencyName;
}
public String getAgencyAddress() {
return agencyAddress;
}
public void setAgencyAddress(String agencyAddress) {
this.agencyAddress = agencyAddress;
}
#Entity(tableName = "suggetedVacations")
public class suggestedVacation {
#PrimaryKey
#ColumnInfo(name = "sVCode")
private int suggestedVacationCode;
#ColumnInfo(name = "sVCity")
private String suggestedVacationCity;
#ColumnInfo(name = "sVCountry")
private String SuggestedVacationCountry;
#ColumnInfo(name = "sVDuration")
private String SuggestedVacationDuration;
#ColumnInfo(name = "sVType")
private String SuggestedVacationType;
public int getSuggestedVacationCode() {
return suggestedVacationCode;
}
public void setSuggestedVacationCode(int suggestedVacationCode) {
this.suggestedVacationCode = suggestedVacationCode;
}
public String getSuggestedVacationCity() {
return suggestedVacationCity;
}
public void setSuggestedVacationCity(String suggestedVacationCity) {
this.suggestedVacationCity = suggestedVacationCity;
}
public String getSuggestedVacationCountry() {
return SuggestedVacationCountry;
}
public void setSuggestedVacationCountry(String suggestedVacationCountry) {
SuggestedVacationCountry = suggestedVacationCountry;
}
public String getSuggestedVacationDuration() {
return SuggestedVacationDuration;
}
public void setSuggestedVacationDuration(String suggestedVacationDuration) {
SuggestedVacationDuration = suggestedVacationDuration;
}
public String getSuggestedVacationType() {
return SuggestedVacationType;
}
public void setSuggestedVacationType(String suggestedVacationType) {
SuggestedVacationType = suggestedVacationType;
}
}
#Entity(tableName ="theVacationPacket",
primaryKeys = {"tVPAgencyCode","tVPVacationCode","tVPCode"},
foreignKeys = {
#ForeignKey(entity = travelAgency.class,
parentColumns = "tACode",
childColumns = "tVPAgencyCode",
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE),
#ForeignKey(entity = suggestedVacation.class,
parentColumns = "sVCode",
childColumns = "tVPVacationCode",
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE)})
public class vacationPacket {
#ColumnInfo(name = "tVPCode") #NonNull
private int tableVacationPacketCode;
#ColumnInfo(name = "tVPAgencyCode") #NonNull
private int tableVacationPacketAgencyCode;
#ColumnInfo(name = "tVPVacationCode") #NonNull
private int tableVacationPacketVacationCode;
#ColumnInfo(name = "tVPDateOfDeparture")
private String tableVacationPacketDateOfDeparture;
#ColumnInfo(name = "tVPPrice")
private double tableVacationPacketPrice;
public int getTableVacationPacketCode() {
return tableVacationPacketCode;
}
public void setTableVacationPacketCode(int tableVacationPacketCode) {
this.tableVacationPacketCode = tableVacationPacketCode;
}
public int getTableVacationPacketAgencyCode() {
return tableVacationPacketAgencyCode;
}
public void setTableVacationPacketAgencyCode(int tableVacationPacketAgencyCode) {
this.tableVacationPacketAgencyCode = tableVacationPacketAgencyCode;
}
public int getTableVacationPacketVacationCode() {
return tableVacationPacketVacationCode;
}
public void setTableVacationPacketVacationCode(int tableVacationPacketVacationCode) {
this.tableVacationPacketVacationCode = tableVacationPacketVacationCode;
}
public String getTableVacationPacketDateOfDeparture() {
return tableVacationPacketDateOfDeparture;
}
public void setTableVacationPacketDateOfDeparture(String tableVacationPacketDateOfDeparture) {
this.tableVacationPacketDateOfDeparture = tableVacationPacketDateOfDeparture;
}
public double getTableVacationPacketPrice() {
return tableVacationPacketPrice;
}
public void setTableVacationPacketPrice(double tableVacationPacketPrice) {
this.tableVacationPacketPrice = tableVacationPacketPrice;
}
#Database(entities ={suggestedVacation.class,travelAgency.class,vacationPacket.class},version = 1)
public abstract class roomApiDatabase extends RoomDatabase {
public abstract RoomApiDao roomApiDaoVariable();
}
#Dao
public interface RoomApiDao {
#Insert
public void insertTravelAgency(travelAgency travelAgency);
#Update
public void updateTravelAgency(travelAgency travelAgency);
#Delete
public void deleteTravelAgency(travelAgency travelAgency);
#Insert
public void insertSuggestedVacation(suggestedVacation suggestedVacation);
#Update
public void updateSuggestedVacation(suggestedVacation suggestedVacation);
#Delete
public void deleteSuggestedVacation(suggestedVacation suggestedVacation);
#Insert
public void insertVacationPacket(vacationPacket vacationPacket);
#Update
public void updateVacationPacket(vacationPacket vacationPacket);
#Delete
public void deleteVacationPacket(vacationPacket vacationPacket);
#Query("select * from travelAgencies")
public List<travelAgency> getTravelAgency();
#Query("select * from suggetedVacations")
public List<suggestedVacation> getSuggestedVacations();
#Query("select * from theVacationPacket")
public List<vacationPacket> getVacationPackets();
}

Usually, Foreign key constraint failed happens when you try to constraint child column to an empty or null parent column, this usually happens when :
The child entity inserted to the database before the parent entity
The childColumns value doesn't match any parentColumns value

Related

could not serialize; nested exception is org.hibernate.type.SerializationException: could not serialize

I have spend way too much find finding the root cause of the below error:
org.springframework.orm.jpa.JpaSystemException: could not serialize; nested exception is org.hibernate.type.SerializationException: could not serialize
I am trying to save some value to db:
public void logFailure(Long objectID,Integer usLK){
StatusFailureDO failureDO = new StatusFailureDO(4,objectID, usLK);
failuresRepository.save(failureDO.getFailure());
}
#Repository
public interface FailuresRepository extends JpaRepository<GeneralFailure, Integer> {
GeneralFailure save(GeneralFailure aGeneralFailure);
void delete(GeneralFailure aGeneralFailure);
GeneralFailure findByObjectID(Long objectID);
}
There were many mapping errors and as such that I got pass now. I am trying to understand where in the process error occurs and what shall I look out for.
public class StatusFailureDO extends GeneralFailureDO implements Serializable
{
public StatusFailureDO(Integer failureTypeLK,Long objectID,
Integer usLK)
{
super(new StatusFailure(failureTypeLK,
"An exception occurred while trying to update an UploadStatus entry.",
objectID, usLK));
}
//more constructors and setters
}
public abstract class GeneralFailureDO implements ICISConstant, Serializable
{
private GeneralFailure mGeneralFailure;
//constructors and setters
}
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#Table(name = "GEN_FLR")
public class GeneralFailure implements Serializable,ICISConstant
{
#Column(name = "CRTN_TM")
private Date mCreationTime;
#Column(name = "TYP_LKP_ID")
private Integer failureTypeLK;
#Column(name = "STUS_LKP_ID")
private Integer mFailureStatusLK;
#Column(name="OBJ_ID")
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "native")
#GenericGenerator(name = "native", strategy = "native")
private Long objectID;
#Column(name = "DSCR")
private String mDescription;
public Date getCreationTime()
{
return mCreationTime;
}
public void setCreationTime(Date aCreationTime)
{
mCreationTime = aCreationTime;
}
public String getDescription()
{
return mDescription;
}
public void setDescription(String aDescription)
{
if (aDescription != null && aDescription.length() > MAX_DESCRIPTION_LENGTH)
{
mDescription = aDescription.substring(0, MAX_DESCRIPTION_LENGTH);
}
else
{
mDescription = aDescription;
}
}
public Long getObjectID()
{
return objectID;
}
public void setObjectID(Long aObjectID)
{
objectID = aObjectID;
}
public Integer getFailureTypeLK()
{
return failureTypeLK;
}
public void setFailureTypeLK(Integer aFailureTypeLK)
{
failureTypeLK = aFailureTypeLK;
}
public Integer getFailureStatusLK()
{
return mFailureStatusLK;
}
public void setFailureStatusLK(Integer aFailureStatusLK)
{
mFailureStatusLK = aFailureStatusLK;
}
}
#Entity
#Table(name="STUS_FLR")
public class StatusFailure extends GeneralFailure implements Serializable
{
#Column(name = "STUS_OBJ_ID")
private Long mStatusObjectID;
#Column(name = "STUS_LKP_ID")
private Integer mStatusLK;
#Column(name = "RQST_TYP_LKP_ID")
private Integer mRequestTypeLK;
#Column(name = "CODE")
private String mCode;
#Column(name = "PST_TM")
private Timestamp mPostTime;
#Column(name = "MSG_SZ")
private Integer mMessageSize;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Collection<StatusFailureError> StatusFailureErrorList;
#Column(name = "SMPL_FLG")
private boolean mSimple;
public Integer getStatusLK()
{
return mStatusLK;
}
public void setStatusLK(Integer statusLK)
{
mStatusLK = statusLK;
}
public Long getStatusObjectID()
{
return mStatusObjectID;
}
public void setStatusObjectID(Long statusObjectID)
{
mStatusObjectID = statusObjectID;
}
public String getCode()
{
return mCode;
}
public void setCode(String aCode)
{
mCode = aCode;
}
public Collection<StatusFailureError> getStatusFailureErrorList()
{
return mStatusFailureErrorList;
}
public void setStatusFailureErrorList(
Collection<StatusFailureError> aStatusFailureErrorList)
{
mStatusFailureErrorList = aStatusFailureErrorList;
}
public void setErrorList(Collection<String> aErrorList)
{
if (aErrorList != null && !aErrorList.isEmpty())
{
mStatusFailureErrorList = new ArrayList<StatusFailureError>();
for (Iterator<String> iter = aErrorList.iterator(); iter.hasNext();)
{
String error = (String) iter.next();
StatusFailureError failureError = new StatusFailureError(this, error, getPostTime());
mStatusFailureErrorList.add(failureError);
}
}
else
{
mStatusFailureErrorList = null;
}
}
public Integer getMessageSize()
{
return mMessageSize;
}
public void setMessageSize(Integer aMessageSize)
{
mMessageSize = aMessageSize;
}
public Timestamp getPostTime()
{
return mPostTime;
}
public void setPostTime(Timestamp aPostTime)
{
mPostTime = aPostTime;
}
public Integer getRequestTypeLK()
{
return mRequestTypeLK;
}
public void setRequestTypeLK(Integer aRequestTypeLK)
{
mRequestTypeLK = aRequestTypeLK;
}
public boolean isSimple()
{
return mSimple;
}
public void setSimple(boolean aSimple)
{
mSimple = aSimple;
}
}
Any help is really appreciated.
It is not obvious what the failureDO.getFailure() returns exactly because you did not provide a method definition for the StatusFailureDO.getFailure() method and so I will assume that that method returns an instance of a GeneralFailure class (or StatusFailure that extends it).
For hibernate to successfully save objects into the database, the #Entity classes that you are trying to save need to consist of "base" types. I see that you have an object of class CLRISCache defined in your GeneralFailure data class, that is most definitely not of a base type and not another #Entity. You can prevent a field from being persisted by marking it with the #Transient annotation, but really you should keep your data class pure.
You can find a list of "base" types here: https://docs.jboss.org/hibernate/orm/5.0/mappingGuide/en-US/html/ch03.html
Actually I found the reason. The General failure has dateCreation variable of Date type and in mu status failure I had it as a timestamp. I need to make it to Date and it worked.

Android: how to insert data in manyToMany relationship in Room (Java)

Guys I'm trying to create a database with a manyToMany relationship, I'm able to create the 2 tables of elements but I'm not able to populate the joining table. I don't know how should I insert datas.
This is Card.class:
#Entity
public class Card {
#PrimaryKey(autoGenerate = true)
private Long idCard;
#ColumnInfo(name = "title")
private String title;
#ColumnInfo(name = "taboo_word_1")
private String tabooWord1;
#ColumnInfo(name = "taboo_word_2")
private String tabooWord2;
#ColumnInfo(name = "taboo_word_3")
private String tabooWord3;
#ColumnInfo(name = "taboo_word_4")
private String tabooWord4;
#ColumnInfo(name = "taboo_word_5")
private String tabooWord5;
public Long getIdCard() {
return idCard;
}
public void setIdCard(Long idCard) {
this.idCard = idCard;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getTabooWord1() {
return tabooWord1;
}
public void setTabooWord1(String tabooWord1) {
this.tabooWord1 = tabooWord1;
}
public String getTabooWord2() {
return tabooWord2;
}
public void setTabooWord2(String tabooWord2) {
this.tabooWord2 = tabooWord2;
}
public String getTabooWord3() {
return tabooWord3;
}
public void setTabooWord3(String tabooWord3) {
this.tabooWord3 = tabooWord3;
}
public String getTabooWord4() {
return tabooWord4;
}
public void setTabooWord4(String tabooWord4) {
this.tabooWord4 = tabooWord4;
}
public String getTabooWord5() {
return tabooWord5;
}
public void setTabooWord5(String tabooWord5) {
this.tabooWord5 = tabooWord5;
}
}
Tag:
#Entity
public class Tag {
#PrimaryKey(autoGenerate = true)
private long idTag;
#ColumnInfo(name = "tag")
private String tag;
public Tag(String tag) {
this.tag = tag;
}
public long getIdTag() {
return idTag;
}
public void setIdTag(long idTag) {
this.idTag = idTag;
}
public String getTag() {
return tag;
}
public void setTag(String tag) {
this.tag = tag;
}
#Override
public String toString() {
return getTag();
}
}
This is DatabaseTaboom.class:
#Database(entities = {Card.class, Tag.class, CardTagCrossRef.class},
version = 1)
public abstract class DatabaseTaboom extends RoomDatabase {
public static final String DATABASE_NAME = "db_taboom-1";
public abstract CardDAO cardDao();
public static DatabaseTaboom db;
// Singleton pattern
public static DatabaseTaboom getDatabase(Context applicationContext) {
if (db == null) {
db = Room.databaseBuilder(applicationContext, DatabaseTaboom.class, DATABASE_NAME)
//.allowMainThreadQueries()
.build();
}
return db;
}
}
This is CardDAO.class:
#Dao
public interface CardDAO {
#Insert(onConflict = OnConflictStrategy.REPLACE)
public void insertCard(Card card);
#Insert(onConflict = OnConflictStrategy.REPLACE)
public void insertTag(Tag tag);
#Insert(onConflict = OnConflictStrategy.REPLACE)
public void insertCardWithTags(CardTagCrossRef cardTagCrossRef);
// If called on an item not present in the DB it won't do anything
#Update
public void updateCard(Card card);
#Delete
public void deleteCard(Card card);
// With a query method you can also perform complex inserts/updates/deletes
// Transaction needed for relational classes
#Transaction
#Query("SELECT * FROM Card")
LiveData<List<CardWithTags>> getAllCards();
}
This is CardTagCrossRef.class:
#Entity(primaryKeys = {"idCard", "idTag"})
public class CardTagCrossRef {
public long idCard;
public long idTag;
}
CardWithTags:
public class CardWithTags {
#Embedded private Card card;
#Relation(
parentColumn = "idCard",
entityColumn = "idTag",
associateBy = #Junction(CardTagCrossRef.class)
)
private List<Tag> tagList;
public CardWithTags() {
}
public CardWithTags(Card card, List<Tag> tagList) {
this.card = card;
this.tagList = tagList;
}
public Card getCard() {
return card;
}
public void setCard(Card card) {
this.card = card;
}
public List<Tag> getTagList() {
return tagList;
}
public void setTagList(List<Tag> tagList) {
this.tagList = tagList;
}
#Override
public String toString() {
String s = getCard().toString();
s += ", TAG[";
for (Tag t: getTagList()) {
s += t + "";
}
s+="]";
return s;
}
}
And this is the method that I wrote to insert a card:
public void insertCard(CardWithTags card) {
Log.d(TAG, ">>insertCard(): " + card);
executor.execute(() -> {
cardDAO.insertCard(card.getCard());
for (Tag t: card.getTagList()) {
cardDAO.insertTag(t);
CardTagCrossRef cardTagCrossRef = new CardTagCrossRef();
cardTagCrossRef.idCard = card.getCard().getIdCard();
cardTagCrossRef.idTag = t.getIdTag();
Log.d(TAG, "CardCrossRef:" + cardTagCrossRef.idCard + cardTagCrossRef.idTag);
cardDAO.insertCardWithTags(cardTagCrossRef);
}
// Check if tags already exists
cardListIsUpdatedWithDb = false;
});
}
First you should amend the Dao's so that they return the id of the inserted row enabling you to ascertain the actual id of the inserted rows. So :-
#Insert(onConflict = OnConflictStrategy.REPLACE)
public long insertCard(Card card);
#Insert(onConflict = OnConflictStrategy.REPLACE)
public long insertTag(Tag tag);
#Insert(onConflict = OnConflictStrategy.REPLACE)
public long insertCardWithTags(CardTagCrossRef cardTagCrossRef);
This allows you to retrieve the respective id when you insert either a Card or a Tag (note that for a CardTagCrossRef insertion this will be the rowid, a normally hidden row).
So you could then have use long cardId = cardDAO.insertCard(card.getCard()); and not then need to attempt to use cardTagCrossRef.idCard = card.getCard().getIdCard(); where the card DOES NOT have the id of the inserted card (part of the issue you are facing).
And likewise for the Tag.
So you could use :-
long cardId = cardDAO.insertCard(card.getCard());
for (Tag t: card.getTagList()) {
long tagId = cardDAO.insertTag(t);
CardTagCrossRef cardTagCrossRef = new CardTagCrossRef();
cardTagCrossRef.idCard = cardId;
cardTagCrossRef.idTag = tagId;
Log.d(TAG, "CardCrossRef:" + cardTagCrossRef.idCard + cardTagCrossRef.idTag);
cardDAO.insertCardWithTags(cardTagCrossRef);
}
However, with a few changes I believe that that can make things far more flexible and have an insert that effectively does what you want within the Dao's.
SO perhaps consider the following that culminates in a working DEMO
Card
#Entity
public class Card {
#PrimaryKey/*(autoGenerate = true) SUGGESTED suppression of autogenerate as will still autogenerate but more efficiently */
private Long idCard;
#ColumnInfo(name = "title")
private String title;
#ColumnInfo(name = "taboo_word_1")
private String tabooWord1;
#ColumnInfo(name = "taboo_word_2")
private String tabooWord2;
#ColumnInfo(name = "taboo_word_3")
private String tabooWord3;
#ColumnInfo(name = "taboo_word_4")
private String tabooWord4;
#ColumnInfo(name = "taboo_word_5")
private String tabooWord5;
/* Constructors added */
public Card(){}
#Ignore
public Card(Long idCard,String title, String tabooWord1, String tabooWord2, String tabooWord3, String tabooWord4, String tabooWord5) {
this.idCard = idCard;
this.title = title;
this.tabooWord1 = tabooWord1;
this.tabooWord2 = tabooWord2;
this.tabooWord3 = tabooWord3;
this.tabooWord4 = tabooWord4;
this.tabooWord5 = tabooWord5;
}
public Long getIdCard() {
return idCard;
}
public void setIdCard(Long idCard) {
this.idCard = idCard;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getTabooWord1() {
return tabooWord1;
}
public void setTabooWord1(String tabooWord1) {
this.tabooWord1 = tabooWord1;
}
public String getTabooWord2() {
return tabooWord2;
}
public void setTabooWord2(String tabooWord2) {
this.tabooWord2 = tabooWord2;
}
public String getTabooWord3() {
return tabooWord3;
}
public void setTabooWord3(String tabooWord3) {
this.tabooWord3 = tabooWord3;
}
public String getTabooWord4() {
return tabooWord4;
}
public void setTabooWord4(String tabooWord4) {
this.tabooWord4 = tabooWord4;
}
public String getTabooWord5() {
return tabooWord5;
}
public void setTabooWord5(String tabooWord5) {
this.tabooWord5 = tabooWord5;
}
}
2 changes an more flexible constructor and not using autogenerate = true (but that does automatically generate id's BUT without the overheads of the SQLite AUTOINCREMENT which is what autogenerate = true adds).
Tag (similar changes)
#Entity
public class Tag {
#PrimaryKey/*(autoGenerate = true) SUGGESTED suppression of autogenerate*/
private Long idTag;
#ColumnInfo(name = "tag")
private String tag;
public Tag(){}
#Ignore
public Tag(Long idTag, String tag) {
this.idTag = idTag;
this.tag = tag;
}
#Ignore
public Tag(String tag) {
this.tag = tag;
}
public Long getIdTag() {
return idTag;
}
public void setIdTag(Long idTag) {
this.idTag = idTag;
}
public String getTag() {
return tag;
}
public void setTag(String tag) {
this.tag = tag;
}
#Override
public String toString() {
return getTag();
}
}
CardTagCrossRef (added ForeignKey constraints to enforce/manage referential integrity)
#Entity(
primaryKeys = {"idCard", "idTag"}
/* SUGGESTED */
, foreignKeys = {
#ForeignKey(
entity = Card.class,
parentColumns = "idCard",
childColumns = "idCard",
/* SUGGESTED with ForeignKey */
onDelete = CASCADE,
onUpdate = CASCADE
),
#ForeignKey(
entity = Tag.class,
parentColumns = "idTag",
childColumns = "idTag",
/* SUGGESTED with ForeignKey */
onDelete = CASCADE,
onUpdate = CASCADE
)
}
)
public class CardTagCrossRef {
public long idCard;
#ColumnInfo(index = true) /* SUGGESTED */
public long idTag;
public CardTagCrossRef(){}
#Ignore
public CardTagCrossRef(long idCard, long idTag) {
this.idCard = idCard;
this.idTag = idTag;
}
}
CardWithTags
identical other than #Ignore annotation on the CardWithTags(Card card, List<Tag> tagList) constructor to supress warnings about multiple good consctructors.
i.e.
....
#Ignore /*<<<<< SUGGESTED */
public CardWithTags(Card card, List<Tag> tagList) {
this.card = card;
this.tagList = tagList;
}
....
CardDAO (new INSERT + return values)
#Dao
abstract class CardDAO {
/* public interface CardDAO { CHANGED TO abstract class to allow functions with bodies */
#Insert(onConflict = OnConflictStrategy.REPLACE)
abstract long insertCard(Card card); /* Returns long (inserted row id) */
#Insert(onConflict = OnConflictStrategy.REPLACE)
abstract long insertTag(Tag tag); /* Returns long (inserted row id) */
#Insert(onConflict = OnConflictStrategy.REPLACE)
abstract long insertCardWithTags(CardTagCrossRef cardTagCrossRef); /* Returns long (inserted row id) */
/* NEW INSERT */
#Query("")
#Transaction
long[] insert(Card card, List<Tag> tags) {
long[] rv = new long[tags.size() + 1];
int ix = 0;
rv[ix++] = insertCard(card);
if (rv[ix-1] > -1) {
for (Tag t : tags) {
rv[ix++] = insertTag(t);
if (rv[ix-1] > -1) {
insertCardWithTags(new CardTagCrossRef(rv[0],rv[ix-1]));
}
}
}
return rv;
}
// If called on an item not present in the DB it won't do anything
#Update
abstract int updateCard(Card card); /* returns number of updated rows */
#Delete
abstract int deleteCard(Card card); /* returns number of deleted rows */
// With a query method you can also perform complex inserts/updates/deletes
// Transaction needed for relational classes
#Transaction
#Query("SELECT * FROM Card")
/* abstract LiveData<List<CardWithTags>> getAllCards(); COMMENTED OUT to allow demo to run on main thread */
abstract List<CardWithTags> getAllCards(); /* same but not with LiveData */
}
DatabaseTaboom (allow main thread + exportSchema = false to suppress warning)
#Database(entities = {Card.class, Tag.class, CardTagCrossRef.class},
version = 1/* SUGGESTED */ , exportSchema = false)
public abstract class DatabaseTaboom extends RoomDatabase {
public static final String DATABASE_NAME = "db_taboom-1";
abstract CardDAO cardDao();
public static DatabaseTaboom db;
// Singleton pattern
public static DatabaseTaboom getDatabase(Context applicationContext) {
if (db == null) {
db = Room.databaseBuilder(applicationContext, DatabaseTaboom.class, DATABASE_NAME)
.allowMainThreadQueries() /* uncommented for testing */
.build();
}
return db;
}
}
Finally the DEMO MainActivity with some examples of inserting Cards, Tags and CardTagCrossRef's followed by extracting them all outputting the results to the log.
public class MainActivity extends AppCompatActivity {
DatabaseTaboom db;
CardDAO dao;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = DatabaseTaboom.getDatabase(this);
dao = db.cardDao();
/* Simple but long winded */
long c1id = dao.insertCard(new Card(null,"Card1","tw1","tw2","tw3","tw4","tw5"));
long t1id = dao.insertTag(new Tag(null,"TAG1"));
CardTagCrossRef ctcr1 = new CardTagCrossRef();
ctcr1.idCard = c1id;
ctcr1.idTag = t1id ;
dao.insertCardWithTags(ctcr1);
/* Using additional constructor for CardTagCrossRef */
long t2id = dao.insertTag(new Tag("TAG2"));
dao.insertCardWithTags(new CardTagCrossRef(c1id,t2id));
/* More dynamic BUT don't know the actual inserted id's of the Card and Tag */
dao.insertCardWithTags(
new CardTagCrossRef(dao.insertCard(new Card(100l,"Card2","c2tw1","c2tw2","c2tw3","c2tw4","c2tw5")),dao.insertTag(new Tag(null,"TAG3"))));
CardWithTags cwt = new CardWithTags(
new Card(null,"CARD3","c3tw1","c3tw2","c3tw3","c3tw4","c3tw5"),
Arrays.asList(
new Tag(null,"TAG4"), new Tag("TAG5"), new Tag("TAG6")
)
);
/* Amended insert function */
insertCard(cwt,dao);
/* Using new insert funciotn */
dao.insert(
new Card(1000l,"CARD4","c4tw1","c4tw2","c4tw3","c4tw4","c4tw5"),
Arrays.asList(
new Tag(null,"TAG7"), new Tag(500l,"TAG8"),new Tag(null,"TAG9")
)
);
/* Extract the results and output to the log */
for(CardWithTags cwtlist: dao.getAllCards()) {
Log.d("CWTINFO","Card is " + cwtlist.getCard().getTitle() + " TabooWord1 is " + cwtlist.getCard().getTabooWord1() + " it has " + cwtlist.getTagList().size() + " tags. They are:-");
for(Tag t: cwtlist.getTagList()) {
Log.d("CWTINFO_TAG","\tTAG is " + t.getTag());
}
}
}
public void insertCard(CardWithTags card, CardDAO cardDAO) {
final String TAG = "INSERTCARDINFO";
Log.d(TAG, ">>insertCard(): " + card);
/*
executor.execute(() -> {
*/
long currentCardId = cardDAO.insertCard(card.getCard());
for (Tag t: card.getTagList()) {
long currentTagId = cardDAO.insertTag(t);
CardTagCrossRef cardTagCrossRef = new CardTagCrossRef();
cardDAO.insertCardWithTags(new CardTagCrossRef(currentCardId,currentTagId));
/*
cardTagCrossRef.idCard = card.getCard().getIdCard();
cardTagCrossRef.idTag = t.getIdTag();
*/
/*
OR with new Contsructor
CardTagCrossRef ctcr = new CardTagCrossRef(currentCardId,currentTagId);
*/
/* AND THEN cardDAO.insertCardWithTags(cardTagCrossRef); */
Log.d(TAG, "CardCrossRef:" + cardTagCrossRef.idCard + cardTagCrossRef.idTag);
}
/*
// Check if tags already exists
cardListIsUpdatedWithDb = false;
*/
/*})*/;
}
}
When run (after new install as only designed to run the once) the Log includes:-
2022-02-04 13:29:10.569D/INSERTCARDINFO: >>insertCard(): a.a.so70979022javaroom.Card#d751e5e, TAG[TAG4TAG5TAG6]
2022-02-04 13:29:10.573D/INSERTCARDINFO: CardCrossRef:00
2022-02-04 13:29:10.578I/chatty: uid=10194(a.a.so70979022javaroom) identical 1 line
2022-02-04 13:29:10.581D/INSERTCARDINFO: CardCrossRef:00
2022-02-04 13:29:10.600D/CWTINFO: Card is Card1 TabooWord1 is tw1 it has 2 tags. They are:-
2022-02-04 13:29:10.600D/CWTINFO_TAG: TAG is TAG1
2022-02-04 13:29:10.601D/CWTINFO_TAG: TAG is TAG2
2022-02-04 13:29:10.601D/CWTINFO: Card is Card2 TabooWord1 is c2tw1 it has 1 tags. They are:-
2022-02-04 13:29:10.601D/CWTINFO_TAG: TAG is TAG3
2022-02-04 13:29:10.601D/CWTINFO: Card is CARD3 TabooWord1 is c3tw1 it has 3 tags. They are:-
2022-02-04 13:29:10.601D/CWTINFO_TAG: TAG is TAG4
2022-02-04 13:29:10.601D/CWTINFO_TAG: TAG is TAG5
2022-02-04 13:29:10.601D/CWTINFO_TAG: TAG is TAG6
2022-02-04 13:29:10.601D/CWTINFO: Card is CARD4 TabooWord1 is c4tw1 it has 3 tags. They are:-
2022-02-04 13:29:10.601D/CWTINFO_TAG: TAG is TAG7
2022-02-04 13:29:10.601D/CWTINFO_TAG: TAG is TAG8
2022-02-04 13:29:10.601D/CWTINFO_TAG: TAG is TAG9
Via App Inspection then :-
and :-
and :-

Spring Data JPA - Get the values of a non-entity column of a custom native query

I am using Spring Boot/MVC.
I have a custom query using JpaRepository:
public interface WorkOrderRepository extends JpaRepository<WorkOrder, Integer> {
#Query(value = "SELECT * FROM (SELECT * FROM workorder) Sub1 INNER JOIN (SELECT wo_number, GROUP_CONCAT(service_type SEPARATOR ', ') AS 'service_types' FROM service_type GROUP BY wo_number) Sub2 ON Sub1.wo_number=Sub2.wo_number WHERE fleet_company_id=?1 AND (order_status='On-Bidding' OR order_status='Draft')", nativeQuery = true)
Collection<WorkOrder> findWorkOrdersByFleet(Long fleetCompanyID);
}
It returns the following table:
http://imgur.com/Ylkc6U0
As you can see it has service_types columns which is a result of Concat, it's not part of the entity class. My problem is how can I get the value of that column. Some said I can use a separate DTO to map the service_types column? Or I can use 'new' keyword? Maybe you have other worked on me. I also tried to make a transient column service_types but it didn't work.
This is my entity class:
#Entity
#Table(name="workorder")
public class WorkOrder {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="wo_number")
private Long woNumber;
#ManyToOne(optional=false, cascade=CascadeType.ALL)
#JoinColumn(name = "vehicle_id")
private Vehicle vehicle;
#ManyToOne(optional=false, cascade=CascadeType.ALL)
#JoinColumn(name = "fleet_company_id")
private FleetCompany fleetCompany;
#Column(name="order_title")
private String orderTitle;
#Column(name="order_date")
private String orderDate;
#Column(name="order_time")
private String orderTime;
#Column(name="order_status")
private String orderStatus;
#Column(name="ref_number")
private String refNumber;
#Column(name="proposals")
private int proposals;
//#Column(name="serviceTypes")
#Transient
private int serviceTypes;
public WorkOrder() {
super();
}
public Long getWoNumber() {
return woNumber;
}
public void setWoNumber(Long woNumber) {
this.woNumber = woNumber;
}
public String getOrderTitle() {
return orderTitle;
}
public void setOrderTitle(String orderTitle) {
this.orderTitle = orderTitle;
}
public String getOrderDate() {
return orderDate;
}
public void setOrderDate(String orderDate) {
this.orderDate = orderDate;
}
public String getOrderTime() {
return orderTime;
}
public void setOrderTime(String orderTime) {
this.orderTime = orderTime;
}
public String getOrderStatus() {
return orderStatus;
}
public void setOrderStatus(String orderStatus) {
this.orderStatus = orderStatus;
}
public String getRefNumber() {
return refNumber;
}
public void setRefNumber(String refNumber) {
this.refNumber = refNumber;
}
public int getProposals() {
return proposals;
}
public void setProposals(int proposals) {
this.proposals = proposals;
}
public Vehicle getVehicle() {
return vehicle;
}
public void setVehicle(Vehicle vehicle) {
this.vehicle = vehicle;
}
public FleetCompany getFleetCompany() {
return fleetCompany;
}
public void setFleetCompany(FleetCompany fleetCompany) {
this.fleetCompany = fleetCompany;
}
public int getServiceTypes() {
return serviceTypes;
}
public void setServiceTypes(int serviceTypes) {
this.serviceTypes = serviceTypes;
}
}
Some people told me to make a DTO:
public class WorkOrderDTO extends WorkOrder {
private String service_types;
public WorkOrderDTO() {
super();
}
public WorkOrderDTO(String service_types) {
this.service_types = service_types;
}
public String getService_types() {
return service_types;
}
public void setService_types(String service_types) {
this.service_types = service_types;
}
}
and add make the repository replaced from WorkOrder to WorkOrderDTO.
public interface WorkOrderRepository extends JpaRepository<WorkOrderDTO, Integer>
but when I do that I have autowiring problems.
I solved my own problem, finally!!!
I used #SqlResultMapping
SqlResultSetMapping(
name="workorder",
classes={
#ConstructorResult(
targetClass=WorkOrderDTO.class,
columns={
#ColumnResult(name="wo_number", type = Long.class),
#ColumnResult(name="service_types", type = String.class),
#ColumnResult(name="order_title", type = String.class)
}
)
}
)
And I created a new POJO that is not an entity named WorkOrderDTO.
#PersistenceContext
private EntityManager em;
#Override
public Collection<WorkOrderDTO> getWork() {
Query query = em.createNativeQuery(
"SELECT Sub1.wo_number, Sub2.service_types, Sub1.order_title FROM (SELECT * FROM workorder) Sub1 INNER JOIN (SELECT wo_number, GROUP_CONCAT(service_type SEPARATOR ', ') AS 'service_types' FROM service_type GROUP BY wo_number) Sub2 ON Sub1.wo_number=Sub2.wo_number WHERE fleet_company_id=4 AND (order_status='On-Bidding' OR order_status='Draft')", "workorder");
#SuppressWarnings("unchecked")
Collection<WorkOrderDTO> dto = query.getResultList();
Iterable<WorkOrderDTO> itr = dto;
return (Collection<WorkOrderDTO>)itr;
}
At last, the users who hated me for posting the same problem won't be annoyed anymore.

Spring + hibernate : Expected type: java.util.SortedSet, actual value: org.hibernate.collection.internal.PersistentSet

I have a Client.class which has a OneToMany relation with Posto.class.
#Entity
#Table(name = "client", catalog = "SMARTPARK")
public class Client implements java.io.Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private int idClient;
private String nomeClient;
private int numPosti;
private int numLuci;
private String currentIp;
private boolean online;
private String prop;
private SortedSet<Posto> posti = new TreeSet<>();
private SortedSet<Luce> luci = new TreeSet<>();
public Client() {
}
public Client(int idClient, String nomeClient, int numPosti, int numLuci,
String currentIp, boolean online, String prop,
SortedSet<Posto> posti, SortedSet<Luce> luci) {
this.idClient = idClient;
this.nomeClient = nomeClient;
this.numPosti = numPosti;
this.numLuci = numLuci;
this.currentIp = currentIp;
this.prop = prop;
this.online = online;
this.posti = posti;
this.luci = luci;
}
#Id
#Column(name = "id_client", unique = true, nullable = false)
public int getIdClient() {
return this.idClient;
}
public void setIdClient(int idClient) {
this.idClient = idClient;
}
#Column(name = "nome_client", nullable = false, length = 65535)
public String getNomeClient() {
return this.nomeClient;
}
public void setNomeClient(String nomeClient) {
this.nomeClient = nomeClient;
}
#Transient
public int getNumPosti() {
return this.numPosti;
}
public void setNumPosti(int numPosti) {
this.numPosti = numPosti;
}
#Transient
public int getNumLuci() {
return this.numLuci;
}
public void setNumLuci(int numLuci) {
this.numLuci = numLuci;
}
#Column(name = "client_ip", nullable=true)
public String getCurrentIp() {
return currentIp;
}
public void setCurrentIp(String currentIp) {
this.currentIp = currentIp;
}
#Column(name="online")
public boolean isOnline() {
return online;
}
public void setOnline(boolean online) {
this.online = online;
}
#Column(name="prop")
public String getProp() {
return prop;
}
public void setProp(String prop) {
this.prop = prop;
}
#OneToMany(fetch = FetchType.EAGER, cascade=CascadeType.ALL, mappedBy = "client", orphanRemoval=true)
#OrderBy("numeroPosto ASC")
public Set<Posto> getPosti() {
return posti;
}
public void setPosti(SortedSet<Posto> posti) {
this.posti = posti;
}
#OneToMany(fetch = FetchType.EAGER, cascade=CascadeType.ALL, mappedBy = "client", orphanRemoval=true)
#OrderBy("numeroLuce ASC")
public SortedSet<Luce> getLuci() {
return luci;
}
public void setLuci(SortedSet<Luce> luci) {
this.luci = luci;
}
This was made because a use the Set in a responseEntity Controller and I need to preserve the order of how postiis displayed in the Json output.
So in Posto.class I implemented Comparable interface, overrding the compareTo method
#Override
public int compareTo(Posto o) {
if(this.numeroPosto == o.numeroPosto){
return 0;
} else {
return this.numeroPosto > o.numeroPosto ? 1 : -1;
}
Now, when calling my controller, i got this error from Hibernate:
2016-03-30 16:18:07.486 ERROR [http-nio-8080-exec-6]: HHH000123: IllegalArgumentException in class: it.besmart.models.Client, setter method of property: posti
2016-03-30 16:18:07.486 ERROR [http-nio-8080-exec-6]: HHH000091: Expected type: java.util.SortedSet, actual value: org.hibernate.collection.internal.PersistentSet
How can i solve it? Hibernate changes my SortedSet in a PersistentSet, do I have to use this one to set my posti with the order i want?
The problem is you defined your posti and Luci as concrete SortSet. Hibernate PersistentSet implements generic Set interface. All you need to do is changing SortSet to generic Set and modify getters, setters accordingly.
private Set<Posto> posti;
private Set<Luce> luci;

JPQL createQuery is incompatible with query return type Collection

I want to get my profile in class Tournee (profil_tournee) list based on my tours ("tournee"). However, I have an exception. Can anyone help me?
Exception in thread "AWT-EventQueue-0"
java.lang.IllegalArgumentException: Type specified for TypedQuery
[fr.galettedebroons.domain.Profil] is incompatible with query return
type [interface java.util.Collection]
Request:
List<List<Profil>> listProfil = Arrays.asList(manager_.createQuery("select t.profil_tournee "
+ "FROM Tournee t WHERE t.nom LIKE :tournee", Profil.class)
.setParameter("tournee", tournee)
.getResultList());
Model :
#Entity
public class Tournee {
private int id;
private String nom;
private boolean lundi = false;
private boolean mardi = false;
private boolean mercredi = false;
private boolean jeudi = false;
private boolean vendredi = false;
private boolean samedi = false;
private boolean dimanche = false;
private List<Profil> profil_tournee;
public Tournee(){}
public Tournee(String nom, boolean lundi, boolean mardi, boolean mercredi, boolean jeudi,
boolean vendredi, boolean samedi, boolean dimanche, List<Profil> profil_tournee) {
this.nom = nom;
this.lundi = lundi;
this.mardi = mardi;
this.mercredi = mercredi;
this.jeudi = jeudi;
this.vendredi = vendredi;
this.samedi = samedi;
this.dimanche = dimanche;
this.profil_tournee = profil_tournee;
}
public Tournee(String nom, boolean lundi, boolean mardi, boolean mercredi, boolean jeudi,
boolean vendredi, boolean samedi, boolean dimanche) {
this.nom = nom;
this.lundi = lundi;
this.mardi = mardi;
this.mercredi = mercredi;
this.jeudi = jeudi;
this.vendredi = vendredi;
this.samedi = samedi;
this.dimanche = dimanche;
}
#Id #GeneratedValue(strategy = GenerationType.AUTO)
public int getId() {
return id;
}
public void setId(int id_tournee) {
this.id = id_tournee;
}
public String getNom() {
return nom;
}
public void setNom(String nom) {
this.nom = nom;
}
public boolean isLundi() {
return lundi;
}
public void setLundi(boolean lundi) {
this.lundi = lundi;
}
public boolean isMardi() {
return mardi;
}
public void setMardi(boolean mardi) {
this.mardi = mardi;
}
public boolean isMercredi() {
return mercredi;
}
public void setMercredi(boolean mercredi) {
this.mercredi = mercredi;
}
public boolean isJeudi() {
return jeudi;
}
public void setJeudi(boolean jeudi) {
this.jeudi = jeudi;
}
public boolean isVendredi() {
return vendredi;
}
public void setVendredi(boolean vendredi) {
this.vendredi = vendredi;
}
public boolean isSamedi() {
return samedi;
}
public void setSamedi(boolean samedi) {
this.samedi = samedi;
}
public boolean isDimanche() {
return dimanche;
}
public void setDimanche(boolean dimanche) {
this.dimanche = dimanche;
}
#OneToMany(mappedBy="profil_tournee", cascade=CascadeType.PERSIST)
public List<Profil> getProfil_tournee() {
return profil_tournee;
}
public void setProfil_tournee(List<Profil> profil_tournee) {
this.profil_tournee = profil_tournee;
}
}
#Entity
public class Profil {
private String code_client;
private Client client_profil;
private Gamme gamme_profil;
private List<Livraison> livraison_profil;
private Boolean actif;
private Tournee profil_tournee;
private List<MargeLivraison> marge_profil;
private List<Prevision> prevision_profil;
public Profil(){}
public Profil(Gamme code_gamme, List<Livraison> livraison, Boolean actif) {
this.gamme_profil = code_gamme;
this.livraison_profil = livraison;
this.actif = actif;
}
#Id
public String getCode_client() {
return code_client;
}
public void setCode_client(String code_client) {
this.code_client = code_client;
}
public Boolean getActif() {
return actif;
}
public void setActif(Boolean actif) {
this.actif = actif;
}
#ManyToOne
public Gamme getGamme_profil() {
return gamme_profil;
}
public void setGamme_profil(Gamme gamme_profil) {
this.gamme_profil = gamme_profil;
}
#OneToMany(mappedBy="livraison_profil", cascade=CascadeType.PERSIST)
public List<Livraison> getLivraison_profil() {
return livraison_profil;
}
public void setLivraison_profil(List<Livraison> livraison_profil) {
this.livraison_profil = livraison_profil;
}
#ManyToOne
public Client getClient_profil() {
return client_profil;
}
public void setClient_profil(Client client) {
this.client_profil = client;
}
#ManyToOne
public Tournee getProfil_tournee() {
return profil_tournee;
}
public void setProfil_tournee(Tournee profil_tournee) {
this.profil_tournee = profil_tournee;
}
#OneToMany(mappedBy="marge_profil", cascade=CascadeType.PERSIST)
public List<MargeLivraison> getMarge_profil() {
return marge_profil;
}
public void setMarge_profil(List<MargeLivraison> marge_profil) {
this.marge_profil = marge_profil;
}
#OneToMany(mappedBy="prevision_profil", cascade=CascadeType.PERSIST)
public List<Prevision> getPrevision_profil() {
return prevision_profil;
}
public void setPrevision_profil(List<Prevision> prevision_profil) {
this.prevision_profil = prevision_profil;
}
Your expected result list will contain elements that are list of profiles, not profiles.
I would replace Profil.class by List.class for the Query creation :
List<List<Profil>> listProfil = Arrays.asList(manager_.createQuery("select t.profil_tournee "
+ "FROM Tournee t WHERE t.nom LIKE :tournee", List.class)
.setParameter("tournee", tournee)
.getResultList());
Your error gives you a hint that the returning type should be consistent with declared type when invoking EntityManager.createQuery(query, Type) method:
List<SomeType> em.createQuery("SELECT s FROM SomeType", SomeType.class);
However your real problem is that your query is illegal. In JPA collection-valued expressions cannot be part of SELECT clause. Please see another answer of mine https://stackoverflow.com/a/25890863/3796586.
The solution in your case would be to reverse the query like this:
List<Profil> result = em.createQuery("SELECT p FROM Profil p WHERE" +
"p.profil_tournee.norm LIKE :tournee", Profil.class)
.setParameter("tournee", tournee)
.getResultList());

Categories