I've very much enjoyed being able to shorten my code by using reference in my Table objects as well as referencedOn and referrersOn in my Entities. But as I've gotten further with my project, I've realized that I might have to undo all that work and recreate these processes manually in the event that the rows being referenced are deleted.
Is there any way of keeping these without risking IllegalStateExceptions (like being able to provide a default foreign key you know exists), or will I have to give it all up for manual reference methods?
Here is a minimal example:
fun main(args: Array<String>) {
/* Connecting to DB here */
transaction {
val timesheets = TimeSheet.all()
println("Printing time sheet list:")
timesheets.forEach { sheet ->
println("Time Sheet Object for employee ${sheet.employee}\n$sheet")
}
}
/* Disconnecting to DB */
}
object EmployeeTable : IdTable<UUID>("employeeTable") {
override val id = uuid("EmployeeUID").entityId().primaryKey()
}
object TimeSheetTable : IdTable<Int>("timeSheetTable") {
override val id = integer("JobUID").primaryKey().autoIncrement().entityId()
val employee = reference("EmployeeUID", EmployeeTable)
}
class Employee(id: EntityID<UUID>) : UUIDEntity(id) {
companion object : UUIDEntityClass<Employee>(EmployeeTable)
}
class TimeSheet(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<TimeSheet>(TimeSheetTable)
var employee by Employee referencedOn TimeSheetTable.employee
}
Once the list of time sheets tries to print a row where a Employee UID doesn't match any Employee rows, it'll throw:
Exception in thread "main" java.lang.IllegalStateException: Cannot find employeeTable WHERE id=some-invalid-id-string
Related
My app was running smoothly but I am getting this error now.I am getting an error in Kapt Debug Kotlin. I have update versions of dependencies in gradle file. still facing this issue. How it can be resolved? I saw somewhere to see your room database , dao and data class. still not able to figure out what is the issue.
The error is showing this file
ROOM DATABASE
#Database(entities = [Transaction::class], version = 1, exportSchema = false)
abstract class MoneyDatabase : RoomDatabase(){
abstract fun transactionListDao():transactionDetailDao
companion object {
// Singleton prevents multiple instances of database opening at the
// same time.
#Volatile
private var INSTANCE: MoneyDatabase? = null
fun getDatabase(context: Context): MoneyDatabase {
// if the INSTANCE is not null, then return it,
// if it is, then create the database
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
MoneyDatabase::class.java,
"transaction_database"
).build()
INSTANCE = instance
// return instance
instance
}
}
}
}
DAO
#Dao
interface transactionDetailDao {
#Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(transaction : Transaction)
#Delete
suspend fun delete(transaction : Transaction)
#Update
suspend fun update(transaction: Transaction)
#Query("SELECT * FROM transaction_table ORDER BY id ASC")
fun getalltransaction(): LiveData<List<Transaction>>
}
DATA CLASS
enum class Transaction_type(){
Cash , debit , Credit
}
enum class Type(){
Income, Expense
}
#Entity(tableName = "transaction_table")
data class Transaction(
val name : String,
val amount : Float,
val day : Int,
val month : Int,
val year : Int,
val comment: String,
val datePicker: String,
val transaction_type : String,
val category : String,
val recurring_from : String,
val recurring_to : String
){
#PrimaryKey(autoGenerate = true) var id :Long=0
}
The error is resolved. I was using the kotlin version 1.6.0. I reduced it to 1.4.32. As far as I understood, above(latest) version of Kotlin along with Room and coroutines doesn’t work well.
I believe that your issue is due to the use of an incorrect class being inadvertently used, a likely culprit being Transaction as that it also a Room class
Perhaps in transactionDetailDao (although it might be elsewhere)
See if you have import androidx.room.Transaction? (or any other imports with Transaction)?
If so delete or comment out the import
As an example with, and :-
And with the import commented out :-
Imported from github, had a play issue definitely appears to be with co-routines. commented out suspends in the Dao :-
#Dao
interface transactionDetailDao {
#Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(transaction : Transaction)
#Delete
suspend fun delete(transaction : Transaction)
#Update
suspend fun update(transaction: Transaction)
#Query("SELECT * FROM transaction_table ORDER BY id ASC")
fun getalltransaction(): LiveData<List<Transaction>>
}
Compiled ok and ran and had a play e.g. :-
I am using a lead map where the first id represents an Account ID and the List resembles a list of leads linked to that account such as: Map<id, List<Id> > leadMap = new Map< id, List<id> >();
My question stands as following: Knowing a Lead's Id how do I get the related Account's Id from the map. My code looks something like this, The problems is on the commented out line.
for (Lead l : leads){
Lead newLead = new Lead(id=l.id);
if (l.Company != null) {
// newLead.Account__c = leadMap.keySet().get(l.id);
leads_to_update.add(newLead);
}
}
You could put all lead id and mapping company id in the trigger then get the company id
Map<string,string> LeadAccountMapping = new Map<string,string>();//key is Lead id ,Company id
for(Lead l:trigger.new)
{
LeadAccountMapping.put(l.id,l.Company);
}
//put the code you want to get the company id
string companyid= LeadAccountMapping.get(l.id);
Let me make sure I understand your problem.
Currently you have a map that uses the Account ID as the key to a value of a List of Lead IDs - So the map is -> List. Correct?
Your goal is to go from Lead ID to the Account ID.
If this is correct, then you are in a bad way, because your current structure requires a very slow, iterative search. The correct code would look like this (replace your commented line with this code):
for( ID actID : leadMap.keySet() ) {
for( ID leadID : leadMap.get( actId ) ) {
if( newLead.id == leadID ) {
newLead.Account__c = actId;
leads_to_update.add(newLead);
break;
}
}
}
I don't like this solution because it requires iterating over a Map and then over each of the lists in each of the values. It is slow.
If this isn't bulkified code, you could do a Select Query and get the Account__c value from the existing Lead by doing:
newLead.Account__c = [ SELECT Account__c FROM Lead WHERE Id = :l.id LIMIT 1];
However, this relies on your code not looping over this line and hitting a governor limit.
Or you could re-write your code soe that your Map is actually:
Map<ID, List<Leads>> leadMap = Map<ID, List<Leads>>();
Then in your query where you build the map you ensure that your Lead also includes the Account__c field.
Any of these options should work, it all depends on how this code snippet in being executed and where.
Good luck!
So I have SQLite database using Slick and I want to add and remove tables from it.
Here is what I have now:
Here is the database element class:
class Data(tag: Tag)
extends Table[(Int, String)](tag, "myDB") {
// This is the primary key column:
def id = column[Int]("ID", O.PrimaryKey)
def name = column[String]("NAME")
// Every table needs a * projection with the same type as the table's type parameter
def * : ProvenShape[(Int, String)] = (id, name)
}
I need to be able to create multiple tables using the class above. Something like this:
def addTable(name:String){
db withSession { implicit session =>
val newTable = TableQuery[Data]
newTable.ddl.create
}
}
Problem is that I cant create new table because one already exists with name "myDB". I tried to add a parameter for the name of the Table in the class Data like so:
class Data(tag: Tag,tableName:String)
But then I couldn't create a table at all and got an error
unspecified value parameter tableName
And how can I query a specific table from the database given the table name?
I tried to Implement this using Map with table name pointing to a table, but it doesnt work because the Map is not saved anywhere and is reset everytime the program starts.
This is what I had for querying a table:
def getDataFromTable(tableName:String)
{
var res = ""
db withSession { implicit session =>
tables(tableName) foreach{
case (id,name)=>
res += id + " " + name + " "
}
}
res
}
Any help is appreciated!
Thanks!
Definition
class Data(tag: Tag, tableName: String)
extends Table[(Int, String)](tag, tableName){
...
Usage
(new TableQuery(Data(_,"table_1"))).ddl.create
(new TableQuery(Data(_,"table_2"))).ddl.create
...
I have the following one to many relationship setup between two tables (Case and Recipient)
Case table has one primary key which is identified as an identity (auto-increment) tblRecipient has one primary key which is identified as an identity (auto-increment),and a foreign key relationship with Case.
Case table and related tblRecipient data is pulled:
function searchCases(caseID, caseNum)
{
var qPredicate;
if (caseID != '')
{
qPredicate = new breeze.Predicate("pkCaseID", "==", parseInt(caseID));
}
else if (caseNum != null)
{
qPredicate = new breeze.Predicate("CaseNumber", "Contains", caseNum);
}
var query = breeze.EntityQuery
.from("case")
.where(qPredicate)
.expand("tblRecipients")
return manager.executeQuery(query);
}
When a button is pressed to add a new recipient, the following code is used to create a new recipient entity:
function createNewRecipient(CaseID)
{
var recipientEntityType = manager.metadataStore.getEntityType("tblRecipient");
var newRecipient = manager.createEntity(recipientEntityType, {fkCaseID: CaseID});
return newRecipient;
}
This code returns this error:
Error: Cannot attach an object to an EntityManager without first setting its key or setting its entityType 'AutoGeneratedKeyType' property to something other than 'None'
The AutoGeneratedKeyType in the metadata shows None instead of Identity as in the Case table. We are not sure what we need to change or how to really debug this. Any help would be appreciated.
I have two custom objects, listing and transaction. Initially I had a Lookup field on the transaction object to the listing object. I had Apex Managed Sharing set up to share the transaction with several user lookup fields on the related listing object. It was working perfectly.
I changed the field type of the listing field on the transaction object to Master-Detail and now I get the following error every time I try to save a new transaction:
"TransactionApexSharing: execution of AfterInsert caused by: line 1, column 1: trigger body is invalid and failed recompilation: Entity is not org-accessible"
Transaction and Listing objects are both set to Private and I can't find any typos in the code. The trigger hasn't been changed since it was working with the Lookup field.
Here is my code:
trigger TransactionApexSharing on Transaction__c (after insert, after update) {
if(trigger.isInsert || trigger.isUpdate){
Set<id> triggerIds = trigger.newMap.keyset();
List<Transaction__c> listWithParentData = [select Listing__r.Listing_Agent_1__r.id, Listing__r.Listing_Agent_2__r.id, Listing__r.Listing_Agent_3__r.id from Transaction__c where id in :triggerIds];
List<Transaction__Share> tranShrs = new List<Transaction__Share>();
Transaction__Share laShr;
Transaction__Share la2Shr;
Transaction__Share la3Shr;
Transaction__Share saShr;
Transaction__Share sa2Shr;
Transaction__Share sa3Shr;
for(Transaction__c atransaction : listWithParentData){
laShr = new Transaction__Share();
la2Shr = new Transaction__Share();
la3Shr = new Transaction__Share();
laShr.ParentId = atransaction.Id;
la2Shr.ParentId = atransaction.Id;
la3Shr.ParentId = atransaction.Id;
if (atransaction.Listing__r.Listing_Agent_1__c != null)
{
// Set the ID of user or group being granted access
laShr.UserOrGroupId = atransaction.Listing__r.Listing_Agent_1__c;
// Set the access level
laShr.AccessLevel = 'edit';
// Set the Apex sharing reason
laShr.RowCause = Schema.Transaction__Share.RowCause.Share_Transaction_with_Agency_Agents__c;
// Add objects to list for insert
tranShrs.add(laShr);
}
if (atransaction.Listing__r.Listing_Agent_2__c != null)
{
la2Shr.UserOrGroupId = atransaction.Listing__r.Listing_Agent_2__c;
la2Shr.AccessLevel = 'edit';
la2Shr.RowCause = Schema.Transaction__Share.RowCause.Share_Transaction_with_Agency_Agents__c;
tranShrs.add(la2Shr);
}
if (atransaction.Listing__r.Listing_Agent_3__c != null)
{
la3Shr.UserOrGroupId = atransaction.Listing__r.Listing_Agent_3__c;
la3Shr.AccessLevel = 'edit';
la3Shr.RowCause = Schema.Transaction__Share.RowCause.Share_Transaction_with_Agency_Agents__c;
tranShrs.add(la3Shr);
}
}
for(Transaction__c mytransaction : trigger.new){
// Instantiate the sharing objects
saShr = new Transaction__Share();
sa2Shr = new Transaction__Share();
sa3Shr = new Transaction__Share();
// Set the ID of record being shared
saShr.ParentId = mytransaction.Id;
sa2Shr.ParentId = mytransaction.Id;
sa3Shr.ParentId = mytransaction.Id;
if (mytransaction.Selling_Agent_1_User__c != null)
{
saShr.UserOrGroupId = mytransaction.Selling_Agent_1_User__c;
saShr.AccessLevel = 'edit';
saShr.RowCause = Schema.Transaction__Share.RowCause.Share_Transaction_with_Agency_Agents__c;
tranShrs.add(saShr);
}
if (mytransaction.Selling_Agent_2_User__c != null)
{
sa2Shr.UserOrGroupId = mytransaction.Selling_Agent_2_User__c;
sa2Shr.AccessLevel = 'edit';
sa2Shr.RowCause = Schema.Transaction__Share.RowCause.Share_Transaction_with_Agency_Agents__c;
tranShrs.add(sa2Shr);
}
if (mytransaction.Selling_Agent_3_User__c != null)
{
sa3Shr.UserOrGroupId = mytransaction.Selling_Agent_3_User__c;
sa3Shr.AccessLevel = 'edit';
sa3Shr.RowCause = Schema.Transaction__Share.RowCause.Share_Transaction_with_Agency_Agents__c;
tranShrs.add(sa3Shr);
}
}
// Insert sharing records and capture save result
// The false parameter allows for partial processing if multiple records are passed
// into the operation
Database.SaveResult[] lsr = Database.insert(tranShrs,false);
// Create counter
Integer i=0;
// Process the save results
for(Database.SaveResult sr : lsr){
if(!sr.isSuccess()){
// Get the first save result error
Database.Error err = sr.getErrors()[0];
// Check if the error is related to a trivial access level
// Access levels equal or more permissive than the object's default
// access level are not allowed.
// These sharing records are not required and thus an insert exception is
// acceptable.
if(!(err.getStatusCode() == StatusCode.FIELD_FILTER_VALIDATION_EXCEPTION
&& err.getMessage().contains('AccessLevel'))){
// Throw an error when the error is not related to trivial access level.
trigger.newMap.get(tranShrs[i].ParentId).
addError(
'Unable to grant sharing access due to following exception: '
+ err.getMessage());
}
}
i++;
}
}
}
When you flip the relationship from lookup to master detail a lot changes.
You lose fine grained access to details, that's it. Whatever rights user has to the master - he has them for the details as well (OK, excluding stuff like 'Read/Create/Edit/Delete' which is in Profiles, I'm talking about access to particular record, not general rights).
On the detail "OwnerId" will disappear (can't be queried, described etc)
Which has some finer points if you ever:
need approval process on detail and suddenly you can't use queues, you need to specify users directly.
users ask "what happened to My Transactions" in list views or reports
Last but not least - Detail__Share table disappears as well.
Try editing your trigger in the sandbox (just add 1 space or something), it will probably complain that there's no such table Transaction_Share.
You can either make sure that Agents have edit rights to parent listing (but that means they can edit ANY related transaction) and ditch the trigger or undo the M-D. It's really a case of going back to your users and asking for business logic ;)
Why did you flip it to M-D? Cascade delete, rollups etc. could be done with a bit of code if it turns out you can't afford losing this fine-grained edit access to each transaction.
But after a quick look at your code it seems to me you'll be fine with controlling the access on Listing level and not on the details?