Autogenerate=true doesn't set the id of inserted entity in Room - insert

I'm inserting entities with my RoomDAO and I can get the autogenerated id back:
#Insert(onConflict = OnConflictStrategy.ABORT)
long insert(Category category);
But Category.id is not automatically set after insert() call:
Category cat = new Category();
long newId = dao().insert(cat);
// newId is a new id, 63 for instance
// cat.id is still 0
I must explicitely load the entity again via the Dao to see a not null id:
cat = dao().getCategoryById(newId);
// cat.id is 63
The model:
#Entity(tableName = "category")
public class Category {
#PrimaryKey(autoGenerate = true)
public int id;
...
Is it possible to instruct Room to populate cat.id immediately? Or am I using a wrong pattern?

Related

Use join fetch by default with Spring Data

I have a simple schema "Posts have messages that have users" and I want get messages with User data.
#Entity
class User {
#Id
var id: Int = 0
var name: String = ""
var usenrame: String = ""
}
#Entity
class Messages {
#Id
var id: Int = 0
var text: String = ""
#ManyToOne()
var user = User()
}
class Post {
var id: Int = 0
#OneToMany()
#JoinColum()
var messsages: List<Messages> = ArrayList()
}
This works, but, Spring Data / Hibernate, instead of create a single query with join to get message and user, use two queries
I can use a custom repository method to get directly a message and it works fine
#Query("SELECT m FROM Message m LEFT JOIN FECTH m.user WHERE id =:id")
fun findMessageById(id: Int)
But... I don't get any way to get a Post with all messages and users...
I understand that each Post need a query to get all messages, but, I want that query get too user and not get another query for each message to get the user...
Any ideas?

java jpa SqlResultSetMapping issue

I have a table form_header with 3 records
There are more fields in the table decided not to add it here in the post since most are irrelevant. I created a class/entity to get the count with distinct for each status in sql.
#Entity()
#Table(name = "Form_Header")
#SqlResultSetMapping(name = "myMapping",
entities = {#EntityResult(
entityClass = FormSummary.class,
fields = {#FieldResult(name = "status", column = "status"),
#FieldResult(name = "id", column = "header_id")})})
public class FormSummary {
#Id()
private Long id;
private String status;
<getter and setter>
with entity manager
List<FormSummary> results = entityManager.createNativeQuery("select DISTINCT(status), COUNT(header_id) as header_id from Form_Header where is_deleted = 0 group by status order by status", "myMapping").getResultList();
for (FormSummary x : results) {
System.out.println("ABC " + x.getId());
System.out.println("ABC " + x.getStatus());
}
Issue is the sysout is showing this
instead of this
status header_id
APPROVE 1
DRAFT 1
SUBMITTED 1
Whats even weird is if I add an extra record in the table with the same status
I will get the correct data in my jpa
Am I missing something in my code or a possible bug with SqlResultSetMapping?

how to transfer parameter to sub query

i just use userIdx of findAllregions's parameter to findFavoriteStore
this is my query
#Select("SELECT * FROM region")
#Results(value = {
#Result(property = "regionIdx", column = "regionIdx"),
#Result(property = "stores", javaType = Store.class, column = "storeIdx",
many = #Many(select = "com.travely.travely.mapper.StoreMapper.findFavoriteStore", fetchType = FetchType.LAZY))
})
List<Region> findAllRegions(#Param("userIdx") final Long useridx);
#Select("SELECT s.* FROM store as s NATURAL JOIN favorite as f WHERE f.userIdx=#{userIdx} AND f.isFavorite = 1")
List<Store> findFavoriteStore(#Param("userIdx") final long userIdx);
it works to select region of 'findAllRegions'
but doesn't work to select store of 'findFavoriteStore'
The query does not work because the column attribute is configured incorrectly.
Here is relevant part of the column attribute documentation:
The column name from the database, or the aliased column label that
holds the value that will be passed to the nested statement as an
input parameter.
As you see only the column from the main query result can be used.
It means that you either need to include the artificial column to the query and use it in the #Result.column like this:
#Select("SELECT #{userIdx} userIdx, r.* FROM region r")
#Results(value = {
#Result(property = "regionIdx", column = "regionIdx"),
#Result(
property = "stores", javaType = Store.class, column = "userIdx",
many = #Many(
select = "com.travely.travely.mapper.StoreMapper.findFavoriteStore",
fetchType = FetchType.LAZY))
})
List<Region> findAllRegions(#Param("userIdx") final Long useridx);
Alternatively if java 8+ is used you can use default interface methods to fetch associations/collections like this:
interfact MyMapper {
#Select("SELECT * FROM region")
#Results(value = {
#Result(property = "regionIdx", column = "regionIdx")
})
List<Region> findAllRegions();
default List<Region> findAllRegions(Long userIdx) {
List<Region> regions = findAllRegions();
List<Strore> favoriteStores = findFavoriteStore(userIdx);
for(Region region:regions) {
region.setStores(favoriteStores);
}
return regions;
}
#Select("SELECT s.* FROM store as s NATURAL JOIN favorite as f WHERE f.userIdx=#{userIdx} AND f.isFavorite = 1")
List<Store> findFavoriteStore(#Param("userIdx") final long userIdx);
}
Note that this does not use lazy fetching of the favourite stores. It seems that this is not needed as in the contexts that favorite stores are not needed the query without userIdx can (and should0 be used.

Map new column from Spring Native query to entity

I have a case statement in my Native query where I am attempting to override a field in my entity.
SELECT i.id, i.ONE_TO_ONE_ID, i.ANOTHER, CASE(WHEN condition THEN 'YES' WHEN another_condition THEN 'NO' ELSE 'MAYBE' END) as word ....
I am using this with JpaRepository as a native query, with pagination.
When I run the native query against my db directly, the result set looks as though I expect.
| id_value | MAPPED_ENTITY_ID_value | another value | word_value (YES) |
When I run the native query from my JpaRepository, everything works there, except word is always null. I cant' seem to figure out how to map the additional String word result to a field in my Entity.
Is there a way to get this to map? Or will I have to create an entire #SqlResultSetMapping() for all of my fields coupled with a native query? (hoping not)
UPDATE: 1
I was generalizing above. Here is my Query.
#Query(
name = "listPagedMapping",
value = "SELECT DISTINCT i.ID, i.INSTANCE_ID, i.REGION, i.CNAME_STACK_ID, i.INSTANCE_STATE, i.IP_ADDRESS, i.EC2_ROLE_NAME, i.INSTANCE_OWNER, i.IS_MASTER, i.EC2_MASTER_ID, i.CNAME, i.EC2_START_TIMESTAMP, i.PRIVATE_DNS, i.INSTANCE_NAME, i.AUTO_TERMINATE, i.AUTO_TERMINATE_DATE, i.TERMINATION_ZONE, i.ADMIN_GROUP_AD_LDAP_ID, i.USER_GROUP_AD_LDAP_ID, (CASE WHEN i.INSTANCE_OWNER=:username THEN 'OWNER' WHEN i.ADMIN_GROUP_AD_LDAP_ID IN (g.AD_LDAP_ID) THEN 'ADMIN' WHEN i.USER_GROUP_AD_LDAP_ID IN (g.AD_LDAP_ID) THEN 'USER' END) as PERMISSION FROM USER u, USER_ACCESS_GROUPS g, EC2_PROVISIONING i WHERE i.INSTANCE_OWNER=:username and i.INSTANCE_STATE in (:instanceStates) or u.username=:username and i.INSTANCE_STATE in (:instanceStates) and g.USER_ID=u.USER_ID and (i.ADMIN_GROUP_AD_LDAP_ID IN (g.AD_LDAP_ID) or i.USER_GROUP_AD_LDAP_ID IN (g.AD_LDAP_ID))",
countQuery = "SELECT count(*) FROM (SELECT DISTINCT i.* FROM USER u, USER_ACCESS_GROUPS g, EC2_PROVISIONING i WHERE i.INSTANCE_OWNER=:username and i.INSTANCE_STATE in (:instanceStates) or u.username=:username and i.INSTANCE_STATE in (:instanceStates) and g.USER_ID=u.USER_ID and (i.ADMIN_GROUP_AD_LDAP_ID IN (g.AD_LDAP_ID) or i.USER_GROUP_AD_LDAP_ID IN (g.AD_LDAP_ID))) as ug",
nativeQuery = true)
Page<Ec2Instance> findAllByPermissionUserAdminOrOwnerAndInstanceStateIn(
#Param("username")final String username,
#Param("instanceStates") final Set<String> instanceStates,
final Pageable pageable);
}
Obviously a bit more complex.
I can get it to map to the entity field with using a named query, but then I loose all the default mappings:
#JsonInclude(JsonInclude.Include.NON_NULL)
#SuppressWarnings("unused")
#Data
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode(exclude={"masterNode", "workers", "associatedBuckets"})
#Entity
#Table(name = "EC2_PROVISIONING")
#SqlResultSetMapping(
name="listPagedMapping",
columns = {
#ColumnResult(name = "permission", type = String.class)
}
)
#NamedNativeQuery(
name = "listAccessibleInstances",
query = ACCESSIBLE_QUERY,
resultSetMapping = "listPagedMapping"
)
public class Ec2Instance {
....
private String permission;
#column(name = "INSTANCE_ID")
private String instanceId;
#ManyToOne
#JoinColumn(name = "EC2_MASTER_ID")
private Ec2Instance masterNode;
#Setter(AccessLevel.NONE)
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "WORKER_EC2_NODES", joinColumns = { #JoinColumn(name = "EC2_MASTER_ID") }, inverseJoinColumns = {
#JoinColumn(name = "ID") })
private Set<Ec2Instance> workers = new HashSet<>();
... More fields ..
}
I guess, I am hoping there is a way to provide a single mapping on-top of the default mapping that is done by ORM. The above code results in only a pageable of Content PERMISSION, rather than the whole entity + permission.
UPDATE: 2
Ok, so I am getting closer... Seems by removing the #ColumnResult I do get the default mapping, plus the PERMISSION field mapped over! Looks like this:
#SqlResultSetMapping(
name="listPagedMapping"
)
The last issue is it does not accept my CountQuery, and causes my tests to fail whenever a Pagination Query results with multiple pages. Looks like Spring try's to come up with its own CountQuery, which is not correct.
UPDATE: 3
To finish this off, looks like I can provide the Count Query as described here: Spring Data - Why it's not possible to have paging with native query
I will give this a go and update back.
I never got this to work quite how I wanted. I am sure I could by mapping my entire entity, but, that would have been painstaking. I ended up solving this by using NamedNativeQueries, with mapping for the additional Column as a result of my Case statement. My entity class is now annotated like:
#JsonInclude(JsonInclude.Include.NON_NULL)
#SuppressWarnings("unused")
#Data
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode(callSuper = false)
#Entity
#Table(name = "EC2_PROVISIONING")
#SqlResultSetMappings({
#SqlResultSetMapping(
name = "listPagedMapping",
entities = {
#EntityResult(
entityClass = Ec2Instance.class
)
},
columns = {#ColumnResult(name = "permission", type = String.class)}
),
#SqlResultSetMapping(name = "listPagedMapping.count", columns = #ColumnResult(name = "cnt"))
})
#NamedNativeQueries({
#NamedNativeQuery(
name = "Ec2Instance.listAccessibleInstances",
query = ACCESSIBLE_QUERY,
resultClass = Ec2Instance.class,
resultSetMapping = "listPagedMapping"
),
#NamedNativeQuery(
name = "Ec2Instance.listAccessibleInstances.count",
resultSetMapping = "listPagedMapping.count",
query = ACCESSIBLE_QUERY_COUNT
)
})
We also dont need the permission field in this entity anymore. I removed that.
Then in my Repository:
Page<Object[]> listAccessibleInstances(
#Param("username")final String username,
#Param("instanceStates") final Set<String> instanceStates,
final Pageable pageable);
Thats it! Now the result of my case statement is returned with each entity.
Object[0] = original, default mapped entity.
Object[1] = permission

How to get a recordid from OrientDB on insert?

OrientDB question...
Does anyone know how I can get the recordId after an insert:
db.save(person)
I tried below on the Person POJO:
#Id
private Object id;
but the id field was null after the save. I've googled and googled to no avail. I just need to insert an object, then get the recordid that orientdb generates.
Define field in pojo:
#Id
private Object rid;
public Object getRid() {
return rid;
}
When save:
YourClass proxy = db.save(yourClassInstance);
Object rid = proxy.getRid();
I got it to work using ODocuments instead of POJOs (which works for my project). Code sample:
ODatabaseDocumentTx db = null;
ODocument doc = null;
db = new ODatabaseDocumentTx("local:" + System.getProperty("user.home") + "/testDB");
db.create();
doc = new ODocument("Person");
doc.field("name", "Peter");
doc.save();
String rid = doc.getIdentity().toString();
List<ODocument> results = db.query(new OSQLSynchQuery<ODocument>("select from " + rid));
for (ODocument aDoc : results) {
System.out.println(aDoc.field("name"));
}
db.close();
It's just simple here is the code:
//insertquery will be the sql statement you want to insert
ODocument result=db.command(new OCommandSQL(insertquery)).execute();
System.out.println(result.field("#rid"));
Alternatively you can make use of getRecordByUserObject() of OObjectDatabaseTx,
OObjectDatabaseTx db = new OObjectDatabaseTx("local:" + System.getProperty("user.home") + "/testDB");
ODocument oDocument = db.getRecordByUserObject( person, true );
oDocument.save();
String rid = oDocument.getIdentity().toString();
If you already have access to your proxy object from the save, you can totally do a cool cast on it to get the underlying ODocument object which has a record ID (Identity).
Person proxyPerson = db.save(person);
ODocument oDocument = ((OObjectProxyMethodHandler)((ProxyObject)proxyPerson).getHandler()).getDoc();
person.setId(oDocument.getIdentity().toString());

Resources