I have a primary key value and I want to fetch a record for updating, so at the moment I write:
AccountRecord account = db.selectFrom(ACCOUNT).
where(ACCOUNT.ID.eq(identity.getAccountId())).fetchSingle();
JOOQ knows about the primary keys of my tables (such that it generates onKey() methods etc.) - so I was hoping for something like:
AccountRecord account = db.fetchByKey(ACCOUNT, identity.getAccountId())
But that doesn't seem to be a thing.
Is there a more concise way of using the JOOQ API to do what I want?
You can use DSLContext.fetchSingle(Table, Condition):
AccountRecord account = db.fetchSingle(ACCOUNT, ACCOUNT.ID.eq(identity.getAccountId()));
The generated ACCOUNT reference does not have a type reference to the key type, so the syntax you suggested is not possible. You could of course extend the code generator to produce a method that takes the primary key value and produces a Condition:
class Account {
..
public Condition byKey(Long accountId) {
return ID.eq(accountId);
}
public AccountRecord fetchByKey(DSLContext ctx, Long accountId) {
return ctx.fetchSingle(this, byKey(accountId));
}
}
And now use the above:
AccountRecord account = ACCOUNT.fetchByKey(db, identity.getAccountId());
Related
If the id attribute in an object is of type Integer, how do I filter the query to return all objects whose id contains part of a query. i.e. what is the equivalent Realm contains filter operator for Integers?
e.g. object1 has id:1234. If I query 123 then it should return object1. The only filter available for Integers that comes close is equalTo but for this to work I would have to pass 1234 to the query.
Probably you need to add helper field that represent your id as String and query contains() based on this field.
You need to change your Integer field to String and then you can filter from Realm in this way
realm.where(YourRealmModel.class).contains("id","123").findAll()
It appears you're trying to access an object via it's a primary key of id. You can access that object directly with the following code without a query. Assuming we have a DogClass:
class DogClass: Object {
#objc dynamic var id = NSUUID().uuidString
#objc dynamic var dog_name = ""
override static func primaryKey() -> String? {
return "id"
}
}
let realm = try! Realm()
let primaryKey = 123
guard let thisDog = realm.object(ofType: DogClass.self, forPrimaryKey: primaryKey) else { return }
print(thisDog.dog_name)
I'm implementing the PUT Methods for a REST-API.
I have a POJO similar to the following:
public class Brand implements Serializable {
#Column(columnDefinition = "serial")
#Generated(GenerationTime.INSERT)
#JsonIgnore
private Integer id;
#Id
#JsonProperty("brand")
private String brand;
.
.
.
}
Within the postgresql database the brand table has the following columns:
a database-internal id (SERIAL) which shall not be visible to the outside.(This is used manly for joining tables)
a brand (TEXT) which is the primary key
My Service method looks like this:
public Brand updateBrand(String brand, Brand update) {
Brand b = brandRepository.findBrandByBrand(brand);
if(b == null) { //If not exists create new one
b = new Brand(null, brand);
}
else { //If exists keep id, delete old one and create new entry
if(update != null && update.getBrand() != null) {
brandRepository.delete(b);
}
ServiceUtils.copyProperties(update, b); //This is within the if clause, because brand is the only value
}
return brandRepository.save(b);
}
And the controller would have something like this:
#PutMapping(value = "/brand/{brand}")
public ResponseEntity<Brand> updateBrand(#PathVariable("brand") String brand,
#RequestBody Brand update) {
Brand updated = articleNumberService.updateBrand(brand, update);
if(updated == null) {
throw new EntryCreationFailedException(brand); //self made exception
}
return new ResponseEntity<>(updated, HttpStatus.OK);
}
Now my following problem is, that when calling PUT ../brand/stackoverflow
with body:
{"brand":"StackOverflow")
it deletes the old stackoverflow brand (which had id=1, for example) and creates a new one called StackOverflow. But when checking the database the id column is incremented (so now it has id=2).
I checked and this is caused by hibernate still calling:
insert
into
brand
(brand)
values
(?)
This definitly is what I want when id is null. Which happens when creating a new Brand for example. But when only overriding the brand and id is not null I want hibernate to call this:
insert
into
brand
(id, brand)
values
(?, ?)
I know this would be possible by creating a own save method and in an emergency override the query. BUT I'm quite optimistic that this should be possible without. Bt I can't realy find fitting answers to this. I already had problems finding the proper annotations for the postgresql-specific serial behavior.
P.S: I know that some will shout "why would you have Brand as the primary key and not id!?" But this only is a simple class/part of the database. There are more complex classes which use exactly the same way for the internal database id, (and actually need it), but have multiple primary keys etc. So this rather is a very simple representation for my problem.
Since the Postgres use the serial as the auto_increment, it's not included the id insertion in sql statement. Behind the scene, it create the sequence for it. So you should use the Generation type IDENTITY for it.
If it was a field with #Id you would be able to write a custom #GenericGenerator that fetched the sequence if the value was null, but as it's not a primary key, I think you would end up having to have a separate parent Entity with its own generated Id.
I know I can't use DELETE in a query (that is a shame by the way), I will get the following error:
<i>Error:error: Observable query return type (LiveData, Flowable etc) can only be used with SELECT queries that directly or indirectly (via #Relation, for example) access at least one table.</i>
But I can't use #Delete(WHERE... xxx)
So how do I delete a specific row by a parameter?
Actually, you can use #Query to perform a delete.
#Query("DELETE FROM users WHERE user_id = :userId")
abstract void deleteByUserId(long userId);
Extracted from Query javadoc:
UPDATE or DELETE queries can return void or int. If it is an int, the
value is the number of rows affected by this query.
The beauty of room is, we play with the objects. As per requirement you can use
for kotlin:
#Delete
fun delete(model: LanguageModel)
for Java:
#Delete
void delete(LanguageModel model)
it will delete the exact object which is stored in the db with the same values. LanguageModel is my model class and it works perfectly.
You can use below method to delete by ID
#Query("DELETE FROM yourDatabaseTable WHERE id = :id")
void deleteById(int id);
for delete all rows
#Query("DELETE FROM yourDatabaseTable")
void delete();
ROOM database provides easy way to INSERT, UPDATE and DELETE an object in the database. To perform thus operation just needed to annotate #Delete. The DELETE operation returns the Int when deletion of the single object is successful returns 1 else returns 0 if the DELETE operation is unsuccessful, Adding the return type is a good practice.
KotlinEG.kt
#Dao
interface EntityLocalDAO {
#Delete
fun deleteData(entityObject: EntityObject) : Int
}
javaEG.java
#Dao
interface EntityLocalDAO {
#Delete
int deleteData(EntityObject entityObject);
}
You can now delete using only partial data.
Per the documentation:
#Entity
data class Playlist (
#PrimaryKey
val playlistId: Long,
val ownerId: Long,
val name: String,
#ColumnInfo(defaultValue = "normal")
val category: String
)
data class OwnerIdAndCategory (
val ownerId: Long,
val category: String
)
#Dao
public interface PlaylistDao {
#Delete(entity = Playlist::class)
fun deleteByOwnerIdAndCategory(varargs idCategory: OwnerIdAndCategory)
}
In this example you can see that they are deleting the Playlist using only the ownerId and the category. You do not even need to use the primary key (playlistId).
The key is to use the #Delete(entity = Playlist::class) annotation.
First of all, I'm not taking about the primary id of the record. I'm talking about an field that is used by users to identify the record that's automatically generated but changeable by the user, not sequential and not a UUID. For example, starting with an account entity:
#Entity
#Data
class Account {
#Id
#GeneratedValue
private int id;
#Column(unique=true)
#NotNull
private String slug;
#Column
private String name;
}
and then I simply create a record:
#Autowired
private AccountRepository accountRepository;
Account account = new Account();
account.setName("ACME");
accountRepository.saveAndFlush(account);
At that point, the slug should have been generated, either completely randomly, or by doing something based on the name. How should that be done?
I know without locking the whole table it's impossible to ensure that the insertion won't result in an exception due to the uniqueness constrain being violated. I'm actually OK blocking the whole table or even letting the exception happen (you need a lot of requests per second fora conflict to happen between the check for availability and the insert).
If you separate the slug from the Account table and put it in a (id, slug) table by itself, you can generate the slug first (retrying until you succeed) and then persist the Account with a link to the just generated slug id.
You can't achieve this in a #PrePersist method, so your service needs to create the slug whenever you're creating an new Account. However it does simplify things on the application side (e.g. you don't need to wonder which constraint was violated when persisting an Account).
Depending on your other code, you can also get around locking the Account table and even the Slug table if you go for the optimistic approach.
A pseudo-code example of a service method that creates a new account (providing new Slug() creates the random slug):
#Autowired SlugRepository slugRepository;
#Autowired AccountRepository accountRepository;
public void createAccount(Account a) {
Slug s = null;
while(s == null) {
try {
s = slugRepository.save(new Slug());
} catch(Exception e) {
}
}
a.setSlug(s);
accountRepository.save(a);
}
I can think of JPA callbacks to generate the slug. In your case #PrePersist could be useful.
That said, why you need to make sure the value is available with a select before inserting the record, so the window for a collision to occur is tiny? You do have unique constraint on the column, right?
Update
Personally I would prefer to address it like this:
Use JPA callback #PrePersist when generating the the slug. Use to random UUID or timestamp to minimise the possibility of collision. No checking for collision as chances are minimal.
When updating the Account for user generated slug, always check first using query for collision. This check will offcourse happen in service update method itself.
This way I can be DB agnostic and also don't have to use repository/service in entity or listener classes.
I will do something like a separate Bean, helper or service class like this.
public class SlugService {
public String generateSlug(String slug)
{
if (accountRepo.getBySlug(slug) != null){ //check if it is already
return slug
} else {
slug.append("-"); //whatever the syntax
generateSlug();
}
}
public String makeSlug()
{
String slug = split by " ", replace by "_"(accountObject.getName);
generateSlug(slug)
}
}
Call the makeSlug(); method.
My class is not Entity there is code fragment
#SequenceGenerator(name="seqUniqueKeyGenerator",sequenceName="SEQ_UNIQUE_KEY",allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE,generator="seqUniqueKeyGenerator")
#Id
private Integer sequenceId;
public Integer getSequenceId() {
return sequenceId;
}
public void setSequenceId(Integer sequenceId) {
this.sequenceId = sequenceId;
}
public static void main(String[] args) {
UniqueKeyGenerator uniqueKeyGenerator = new UniqueKeyGenerator();
System.out.println(uniqueKeyGenerator.getSequenceId());
}
I want retrieve nextVal like this, is it possible?
You can consume nextVal as mentioned in this thread but you have to consider it is consumed by means of a SQL sentence, which means this is a solution coupled to database.
I don't know a way to consume nextVal in such way you are asking above.
We all know the default behaviour of Hibernate when using #SequenceGenerator - it increases real database sequence by one, multiple this value by 50 (default allocationSize value) - and then uses this value as entity ID.
From G. Demecki
This means that the ID id not generated in your Java-Program. It is created in your database. So you can't read it before it is acutally generated in the database. You can guess it using the formula described by G.Demecki but that is certainly not the way to go.
If you want the id of an entity just save it and read the id from the return value of save what should be the saved entity itself.