I'm using org.springframework.cloud:spring-cloud-gcp-starter-data-datastore with Kotlin.
The code look like this:
#Entity(name = "books")
data class Book(
#Reference val writer: Writer,
var name: String,
#Id val id: Key? = null, //I leave the key as NULL so it that can be autogenerated
)
#Entity(name = "writers")
data class Writer(
var name: String,
#Id val id: Key? = null
)
//Also with Repositories
When I save a Book entity, with a reference to a saved Writer, when I retrieve it, it should be retrieved automatically right?
Sample code:
var w = Writer("Shakespeare")
w = writerRepo.save(w)
var book = Book(w, "Macbeth")
book = bookRepo.save(book)
books = bookRepo.findByWriter(w) //Error happen here
The code above will throw error failed to instantiate Book with NULL Writer. Any idea why this happen?
I find the answer is not because the relationship is not persisted but because The Repository set the relationship Entity after instantiation. The Repository tries to instantiate the Entity first, assign NULL on relationship (annotated with #References) attribute.
Therefore, the Entity should be like this:
#Entity(name = "books")
data class Book(
#Reference var writer: Writer?, //Accepting NULL values
var name: String,
#Id val id: Key? = null
)
And all works well.
Related
Is there any chance to make the #Id property non-nullable for Mongo-related entities in Kotlin?
Referring to this post there seems to be a solution for SQL by setting the ID initially to 0. However, that seems like a hack, and seems to work only when using sequence generators.
My original intent is obviously to avoid nullable IDs like I have right now:
#Document class MyEntity( #Id var id: String? = null )
What I'd prefer if possible:
#Document class MyEntity( #Id val id: String )
You can use lateinit, but this may lead to a runtime error:
#Document
class File : Serializable {
#Id
lateinit var id: ObjectId
}
#Document(collection = COLLECTION_MY_ENTITY)
data class MyEntity #PersistenceConstructor constructor (
#Id val id: String,
#Field(FIELD_NAME) someField: String
)
I have a OneToMany in my model, I would like (if possible) to access the field without query. Or at least, understand why the list is empty in some cases
#Entity
class Product extends Model {
#Id
var id : Long = -1
#Column
var price : Float = _
#Column
#OneToMany(mappedBy = "product")
var productImages : java.util.List[ProductImage] = _
#Column
#OneToMany(mappedBy = "product")
var categoryProduct : java.util.List[CategoryProduct] = _
}
and
#Entity
class ProductImage extends Model {
#ManyToOne
var product : Product = _
#Column
var id : Long = _
}
In my scala template, I can #product.productImages.get(0), it works.
But if I access another field in the template like #product.price then product.productImages is generally an empty list, (some products, 1 or 2 about 20, get their images)
Why do the fetching of association depends on accessing other fields of the entity ?
Why some product still fetch their images ?
PS : I found out that the association is fetched when I query this way :
CategoryProduct.find.query().where().eq("category", cat).setMaxRows(5).findList.asScala.toList
.map {_.product}
Not when I query products directly like this : Product.find.query().setMaxRows(20).findList.asScala.toList
It's explained here :
https://github.com/ebean-orm/ebean/issues/1381
I need to add an #Id field to my child beans.
I have the model like following
#CompoundIndexes(value = {
#CompoundIndex(name = "catalog_idx", def = "{'code' : 1, 'brand' : 1}", unique = true) })
#Document(collection = Catalog.ENTITY)
public class Catalog extends AbstractModel<String> {
private static final long serialVersionUID = 1L;
public static final String ENTITY = "catalog";
#NotNull(message = "Code is required")
#Field("code")
private String code;
#NotNull(message = "Brand is required")
#DBRef(lazy = true)
#Field("brand")
private Brand brand;
}
When i do save with mongoTemplate.save(object); i see only 2 objects created in DB instead of 6. Just before save my debug lines for objects to be saved.
Catalog [code=StagedCatalog, brand=Brand [code=Brand_3]]
Catalog [code=StagedCatalog, brand=Brand [code=Brand_2]]
Catalog [code=StagedCatalog, brand=Brand [code=Brand_1]]
Catalog [code=OnlineCatalog, brand=Brand [code=Brand_2]]
Catalog [code=OnlineCatalog, brand=Brand [code=Brand_1]]
Catalog [code=OnlineCatalog, brand=Brand [code=Brand_3]]
Any ideas why ? I feel the Index unique thing is not working somehow. I want code and brand to be unique combination.
public abstract class AbstractModel<ID extends Serializable> implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private ID id;
}
You have set a unique index. It means that you will be unable to have 2 documents with the same code and brand.
Now you have set the ID column to ID object. The fact that you have 2 insert instead of 6 means that you use the same ID for 3 insert, something like :
for (code: {"StagedCatalog","OnlineCatalog"} ) {
ID id=new ID(...);
for (brand: {1, 2, 3}){
Catalog cat=new Catalog();
cat.setId(id); // <<== this is wrong, you reuse the same id, you will insert first brand, then update to brand2 and brand3.
cat.setCode(code);
cat.setBrand(brand);
mongoTemplate.persist(cat);
}
}
To prevent that, you need to:
Catalog cat=new Catalog();
ID id=new ID(realUniqueId); // RealuniqueId can be code+brand for instance
cat.setId(id);
...
db.collection.save()
Updates an existing document or inserts a new document, depending on its document parameter.
Document Structure
I've searched for quite a while trying to figure this out. I am using JPA with EclipseLink (Oracle DB). I have a lookup table full of values. I have another table that has a FK relationship to that table. I can insert data fine, but when I try to update the table with a different value, I get an exception. I've tried setting the CASCADE_TYPE but that doesn't have any impact. I thought this would be simple, but maybe I'm missing something.
Lookup table:
public class SomeType implements Serializable {
#Id
#Column(name = "ID")
private Short id;
#Column(name = "TYPE")
private String type;
:
(getters & setters)
}
Contents:
ID Type
------------
1 Type1
2 Type2
3 Type3
: :
Person table (I've left out the Sequencing stuff for brevity):
public class Person implements Serializable {
#Id
#Column(name = "ID")
private Short id;
#JoinColumn(name = "SOME_TYPE", referencedColumnName = "ID")
#ManyToOne(optional = false)
private SomeType someType;
:
(getters & setters)
}
Inserting works fine:
EntityManager em;
:
Person p = new Person();
p.setSomeType(new SomeType(1));
em.persist(p);
That results in:
ID SOME_TYPE
------------------
1 1
But if I want to update Person to change the type:
EntityManager em;
:
Person p = em.find(1);
SomeType newtype = new SomeType(2);
p.setSomeType(newtype);
em.merge(p);
I see the following exception:
Exception [EclipseLink-7251] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.ValidationException
Exception Description: The attribute [id] of class [SomeType] is mapped to a primary key column in the database. Updates are not allowed.
All I want is the value in the Person table to be updated, like:
UPDATE PERSON set SOME_TYPE = 2 where ID = 1;
Any assistance would be greatly appreciated. Thanks.
Thanks to Chris for answering this:
The update is possible if you use a reference to the managed instance of the object that you want to refer to, not create a new instance.
Person p = em.find(1);
p.setSomeType(em.find(SomeType.class, 2));
em.merge(p);
I have an #Entity class which holds an #ElementCollection:
#Entity
public class Skill extends JpaEntity {
#ElementCollection(targetClass = SkillName.class)
#CollectionTable(joinColumns = #JoinColumn(name = "SKILL_ID"))
private Set<SkillName> names = new HashSet<>();
...
Those elements are defined in a nested #Embeddable class without ID:
#Embeddable
#Immutable
#Table(uniqueConstraints = #UniqueConstraint(columnNames = "NAME"))
public static class SkillName extends ValueObject {
private boolean selectable;
#Column(unique = true, nullable = false)
#Size(max = 64)
#NotEmpty
private String name;
...
I try to get some specific elements of that element-collection via Querydsl:
QSkill skill = QSkill.skill;
QSkill_SkillName skillName = QSkill_SkillName.skillName;
List<SkillName> foundSkillNames = from(skill)
.innerJoin(skill.names, skillName).where(...)
.list(skillName);
This gives me a MySQLSyntaxErrorException: Unknown column 'names1_.id' in 'field list' since the resulting query looks like:
select names1_.id as col_0_0_ from Skill skill0_ inner join Skill_names names1_ on ...
which is obviously wrong since SkillName has no id
If I replace .list(skillName) with .list(skillName.name) everything works fine, but I get a list of Strings instead of a list of SkillNames.
So the question is:
What can I do to get a list of #Embeddables of an #ElementCollection via Querydsl?
since you are looking for Embeddable objects inside an entity, you might navigate from the entity to the requested Embeddable (in your case "SkillName") - therefor your query should be changed to list(skill) - the entity:
List<Skill> list =
from(skill).innerJoin(skill.names, skillName).
where(skillName.name.like(str)).
list(skill);
for (Skill skill : list) {
// do something with
Set<SkillNames> skillNames = skill.getNames();
}
HTH
You cannot project Embeddable instances directly, but alternatively you can use
Projections.bean(SkillName.class, ...) to populate them or
Projections.tuple(...) to get the skillName properties as a Tuple instance