FundOperationItem.java
#Entity
#Table(name = "OPERATION_ITEMS")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "D_TYPE", discriminatorType = DiscriminatorType.INTEGER)
public abstract class FundOperationItem implements Serializable {
#ManyToOne(fetch = FetchType.LAZY, optional=false)
#JoinColumn(name = "PARENT_OPERATION_ID", nullable=false)
private FundOperation operation;
public FundOperation getOperation() {
return this.operation;
}
public void setOperation(final FundOperation operation) {
this.operation = operation;
}
}
ExchangeOperationItem.java
#Entity
#Table(name = "EXCHANGE_OPERATION_ITEMS")
#DiscriminatorValue(value="2")
public class ExchangeOperationItem extends FundOperationItem {
}
SimpleOperationItem.java
#Entity
#Table(name = "SIMPLE_OPERATION_ITEMS")
#DiscriminatorValue(value="1")
public class SimpleOperationItem extends FundOperationItem {
}
FundOperation.java
#Entity
#Table(name = "OPERATIONS")
public class FundOperation implements java.io.Serializable{
#OneToMany(cascade = CascadeType.ALL, mappedBy = "operation", fetch = FetchType.LAZY)
private List<FundOperationItem> operationItems = new ArrayList<FundOperationItem>();
public List<FundOperationItem> getOperationItems() {
return this.operationItems;
}
public void setOperationItems(final List<FundOperationItem> operationItems) {
this.operationItems = operationItems;
}
}
Using this in such manner:
#Test
public void test(){
FundOperation oper = operationRepository.findById(1L);
System.out.println(oper.getOperationItems().size());
}
Got such exception:
org.apache.openjpa.persistence.ArgumentException: Cannot instantiate abstract class of type "rba.pm.persistency.operation.FundOperationItem" with object id "rba.pm.persistency.operation.FundOperationItem-1"; this may indicate that the inheritance discriminator for the class is not configured correctly.
DB content:
Insert into OPERATIONS (OPERATION_ID) values (1);
Insert into OPERATIONS (OPERATION_ID) values (2);
Insert into OPERATIONS (OPERATION_ID) values (3);
Insert into OPERATION_ITEMS (OPERATION_ITEM_ID,PARENT_OPERATION_ID,D_TYPE) values (1,1,1);
Insert into OPERATION_ITEMS (OPERATION_ITEM_ID,PARENT_OPERATION_ID,D_TYPE) values (2,2,1);
Insert into OPERATION_ITEMS (OPERATION_ITEM_ID,PARENT_OPERATION_ID,D_TYPE) values (3,3,1);
Insert into SIMPLE_OPERATION_ITEMS (OPERATION_ITEM_ID) values (1);
Insert into SIMPLE_OPERATION_ITEMS (OPERATION_ITEM_ID) values (2);
Insert into SIMPLE_OPERATION_ITEMS (OPERATION_ITEM_ID) values (3);
Have I made something wrong?
Update:
**There is an solution, if add to the test above a new line
SimpleOperationItem sio = new SimpleOperationItem();
it works
#Test
public void test(){
SimpleOperationItem sio = new SimpleOperationItem();
FundOperation oper = operationRepository.findById(1L);
System.out.println(oper.getOperationItems().size());
}
Note: Object 'sio' does not have any relation to 'oper'.
Any idea, what is going on?
Is this a problem with classloader?
Is this a known problem?
Not tested this, but try the following in FundOperationItem:
#DiscriminatorValue(value="0")
Related
Note: for simplyfication i have changed some variables names and get rid of unnecessary code to show my issue.
I have two repositories:
#Repository
public interface CFolderRepository extends CrudRepository<CFolder, Long>, QuerydslPredicateExecutor<CFolder> {}
#Repository
public interface CRepository extends JpaRepository<C, Long>, CFinder, QuerydslPredicateExecutor<C> {}
The class C is:
#FilterDef(name = "INS_COMPANY_FILTER", parameters = {#ParamDef(name = "insCompanies", type = "string")})
#Filter(name = "INS_COMPANY_FILTER", condition = " INS_COMPANY in (:insCompanies) ")
#NoArgsConstructor
#AllArgsConstructor
#Audited
#AuditOverrides({#AuditOverride(forClass = EntityLog.class),
#AuditOverride(forClass = MultitenantEntityBase.class)})
#Entity
#Table(name = "INS_C")
#Getter
public class C extends MultitenantEntityBase {
#OneToOne(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "C_FOLDER_ID")
private CFolder cFolder;
public void addFolder(List<String> clsUrl){
this.cFolder = CFolder.createFolder(clsUrl);
}
}
CFolder is:
#Getter
#NoArgsConstructor
#Audited
#AuditOverride(forClass = EntityLog.class)
#Entity
#Table(name = "C_FOLDER")
#AllArgsConstructor
public class CFolder extends EntityBase {
#Column(name = "CREATION_FOLDER_DATE_TIME", nullable = false)
private LocalDateTime creationFolderDateTime;
#Column(name = "UPDATED_FOLDER_DATE_TIME")
private LocalDateTime updatedFolderDateTime;
#Column(name = "FOLDER_CREATED_BY", nullable = false)
private String folderCreatedBy;
#Column(name = "FOLDER_UPDATED_BY")
private String folderUpdatedBy;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "cFolder", fetch = FetchType.EAGER)
#NotAudited
private Set<FolderDocument> folderDocuments = new HashSet<>();
public static CFolder createFolder(List<String> clsUrl){
CFolder cFolder = new CFolder(LocalDateTime.now(), null, SecurityHelper.getUsernameOfAuthenticatedUser(), null, new HashSet<>());
createFolderDocuments(clsUrl, cFolder);
return cFolder;
}
public void updateFolder(List<String> clsUrl){
this.updatedFolderDateTime = LocalDateTime.now();
this.folderUpdatedBy = SecurityHelper.getUsernameOfAuthenticatedUser();
this.folderDocuments.clear();
createFolderDocuments(clsUrl, this);
}
private static void createFolderDocuments(List<String> clsUrl, CFolder cFolder) {
int documentNumber = 0;
for (String url : clsUrl) {
documentNumber++;
cFolder.folderDocuments.add(new FolderDocument(cFolder, documentNumber, url));
}
}
}
FolderDocument is:
#Getter
#NoArgsConstructor
#AllArgsConstructor
#Audited
#AuditOverride(forClass = EntityLog.class)
#Entity
#Table(name = "FOLDER_DOCUMENT")
public class FolderDocument extends EntityBase {
#ManyToOne
#JoinColumn(name = "C_FOLDER_ID", nullable = false)
private CFolder cFolder;
#Column(name = "DOCUMENT_NUMBER", nullable = false)
private int documentNumber;
#Column(name = "URL", nullable = false)
private String url;
}
And finally we have a service in which i use these entities and try to save/load them to/from database:
#Service
#AllArgsConstructor(onConstructor = #__(#Autowired))
public class CFolderService {
private final CRepository cRepository;
private final CommunicationClServiceClient communicationServiceClient;
private final CFolderRepository cFolderRepository;
public List<ClDocumentDto> getClCaseFolder(Long cId) {
C insCase = cRepository.findCById(cId);
List<ClDocumentDto> clDocumentsDto = getClDocuments(insCase.getCNumber()); // here, the object has one cFolder, but many FolderDocument inside of it
return clDocumentsDto;
}
#Transactional
public void updateCFolder(Long cId) {
C insC = cRepository.findCById(cId);
List<ClDocumentDto> clDocumentsDto = getClDocuments(insC.getCNumber());
List<String> clsUrl = clDocumentsDto.stream().filter(c -> "ACTIVE".equals(c.getCommunicationStatus())).map(ClDocumentDto::getUrl).collect(Collectors.toList());
if (Objects.isNull(insC.getCFolder())) {
insC.addFolder(clsUrl);
} else {
insC.getCFolder().updateFolder(clsUrl);
}
cFolderRepository.save(insC.getCFolder()); // here it saves additional FolderDocument instead of updateing it
cRepository.save(insC); // need second save, so can get these collection in getClaimCaseFolder successfully
}
}
I have two issues inside. In the example i was trying to clear the objects that i found from DataBase and create new ones.
1)
First is that i have to make two save operation to successfully restore the object in getClCaseFolder method (outside transactional).
2)
Second is that everytime i am saving - i get additional FolderDocument object pinned to CFolder object inside C object. I want to clear this collection and save new one.
I am not sure why hibernate does not update this object?
EDIT:
I think that i do sth like:
cRepository.save(insC);
instead of this.folderDocuments.clear();
i can do:
for(Iterator<FolderDocument> featureIterator = this.folderDocuments.iterator();
featureIterator.hasNext(); ) {
FolderDocument feature = featureIterator .next();
feature.setCFolder(null);
featureIterator.remove();
}
But i get eager fetching, why lazy wont work? There is an error using it.
Check whether you are setting ID in that Entity or not.
If ID is present/set in entity and that ID is also present in DB table then hibernate will update that record, But if ID is not present/set in Entity object the Hibernate always treat that object as a new record and add new record to the table instead of Updating.
I have four classes which should put in one db table.
First class represents basic information.
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "TYPE")
#Entity
#JsonInclude(JsonInclude.Include.NON_EMPTY)
public class A {
#Id
private String id;
private LocalDateTime date;
private String someString;
}
The second class extends class a and has some extra properties.
#Inheritance
#MappedSuperclass
#JsonInclude(JsonInclude.Include.NON_EMPTY)
public class B extends A {
private String extraProperty;
}
Finally there are two parallel classes with concrete type information.
#Entity
#DiscriminatorValue(value = "C1")
#JsonInclude(JsonInclude.Include.NON_EMPTY)
public class C1 extends B {
private String property1;
private String property2;
}
#Entity
#DiscriminatorValue(value = "C2")
#JsonInclude(JsonInclude.Include.NON_EMPTY)
public class C2 extends B {
private String propertyX;
private String propertyY;
}
The ReST repository looks like this:
#RepositoryRestResource(
path = "items",
collectionResourceRel = "items"
)
public interface ItemRepository extends PagingAndSortingRepository<A, String> { }
Now I have written a test where the expected format is tested.
#Test
public void shouldReturnItemlistWithCorrectDataStructure() throws Exception {
mockMvc.perform(get("/Items"))
.andExpect(jsonPath("$._embedded.items").isArray())
.andExpect(jsonPath("$._embedded.items", hasSize(2)))
.andExpect(jsonPath("$._embedded.items[0].id").value("1234567"))
...
}
I expected one array (items[]) in the result json but there are actually two different arrays c1[] and c2[].
Any ideas what I'm doing wrong?
my superclass is:
#Entity
#Table(name = "TEST_VEHICLE")
#ChangesListener
#AttributeOverride(name = "id", column = #Column(name = "VEHICLE_ID"))
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "VEHICLE_TYPE_ID", discriminatorType = DiscriminatorType.INTEGER)
public abstract class Vehicle extends ParentEntity {
#Column(name = "MAX_SPEED", nullable = false)
private Integer maxSpeed;
public Integer getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(Integer maxSpeed) {
this.maxSpeed = maxSpeed;
}
}
and subclass is:
#Entity
#Table(name = "TEST_BUS")
#DiscriminatorValue("2")
public class Bus extends Vehicle {
#Column(name = "PASSENGER_NUMBER", nullable = false)
private Short passengerNumber;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "FOO_OF_VEHICLE")
private Foo foo;
public Short getPassengerNumber() {
return passengerNumber;
}
public void setPassengerNumber(Short passengerNumber) {
this.passengerNumber = passengerNumber;
}
public Foo getFoo() {
return foo;
}
public void setFoo(Foo foo) {
this.foo = foo;
}
}
using fetch of foo on Root<Vehicle> in criteria:
root.fetch("foo", JoinType.LEFT);
causes this error :
java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [foo] on this ManagedType ...
how can I fetch fields from subclasses?
update:
using treat does not resolve my problem:
Root<Bus> busRoot = builder.treat(root, Bus.class);
busRoot.fetch("foo", JoinType.INNER);
I don't receive any error but foo does not fetch yet.
generated SQL is:
SELECT vehicle0_.VEHICLE_ID AS VEHICLE_2_72_,
vehicle0_.ATTACHMENT_COUNT AS ATTACHME3_72_,
vehicle0_.COMMENTS AS COMMENTS4_72_,
vehicle0_.CREATE_TIMESTAMP AS CREATE_T5_72_,
vehicle0_.CREATOR_USER_ID AS CREATOR_8_72_,
vehicle0_.MODIFIER_USER_ID AS MODIFIER9_72_,
vehicle0_.UPDATE_TIMESTAMP AS UPDATE_T6_72_,
vehicle0_.MAX_SPEED AS MAX_SPEE7_72_,
vehicle0_1_.FOO_OF_VEHICLE AS FOO_OF_V3_70_,
vehicle0_1_.PASSENGER_NUMBER AS PASSENGE1_70_,
vehicle0_2_.ENGINE_TYPE AS ENGINE_T1_71_,
vehicle0_.VEHICLE_TYPE_ID AS VEHICLE_1_72_
FROM TEST_VEHICLE vehicle0_
LEFT OUTER JOIN TEST_BUS vehicle0_1_
ON vehicle0_.VEHICLE_ID=vehicle0_1_.VEHICLE_ID
LEFT OUTER JOIN TEST_CAR vehicle0_2_
ON vehicle0_.VEHICLE_ID =vehicle0_2_.VEHICLE_ID
WHERE vehicle0_.VEHICLE_ID=105
This problem can be solved using meta model.
public abstract class Bus_ extends com.rh.cores.architecture.tests.models.Vehicle_ {
public static volatile SingularAttribute<Bus, Foo> foo;
public static volatile SingularAttribute<Bus, Short> passengerNumber;
}
means this:
root.fetch(Bus_.foo, JoinType.LEFT);
but since fetch signature in JPA is like this:
<Y> Fetch<X, Y> fetch(SingularAttribute<? super X, Y> attribute, JoinType jt);
above code causes compile error!
with changing code like this:
SingularAttribute attribute = Bus_.foo;
root.fetch(attribute, JoinType.LEFT);
we can bypass generics check SingularAttribute<? super X, Y> in JPA standard while Hibernate handle it!
Say I have the following entities:
#Entity
public class Container
{
#OneToMany
Set<AbstractElement> elements;
//getter setter and other attributes...
}
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "CLASS_CODE", discriminatorType = DiscriminatorType.STRING, length = 10)
public abstract class AbstractElement<T> extends AbstractEntity
{
#Transient
T value;
//getter setter and other attributes...
}
#DiscriminatorValue(value = "BOOL")
#Entity
public class BooleanElement extends AbstractElement<Boolean>
{
/**
* {#inheritDoc}
*/
#Column(name = "VALUE_BOOL")
#Override
public Boolean getValue()
{
return super.getValue();
}
}
The question is this:
How can I execute a jpa criteria query on the value of the BooleanElement, starting from the class CONTAINER?
What I actually have so far is this:
CriteriaQuery<Container> criteriaQuery = criteriaBuilder.createQuery(Container.class);
Root<Container> from = criteriaQuery.from(Container.class);
criteriaQuery.select(from);
from = from.join("elements");
Predicate pred = criteriaBuilder.equal(criteriaBuilder.treat(from ,BooleanElement.class).get("value"), FOO);
//etc......
The exception at this point is that "there is not VALUE attributes on the AbstractEntity".
Thanks in advance.
The Treat operator doesn't work in this specific case.
The as operator on the joins it's still not so clear how to implement: i get class cast exception at line 3 when doing this:
CriteriaQuery<AbstractElement> criteriaQuery = criteriaBuilder.createQuery(AbstractElement.class);
Root<AbstractElement> rootAbstract = criteriaQuery.from(AbstractElement.class);
Path predPath = (Path)rootAbstract.as(BooleanElement.class);
predPath.get("value");
The only way to execute this type of query is by executing a subquery
or creating an additional "from" clause:
Root<BooleanElement> from2 = criteriaQuery.from(BooleanElement.class);
Predicate joinPredicate = criteriaBuilder.equal(from, from2);
....
I have a problem with a QueryDSL query. Classes:
#Entity
#Table(name="project")
#Cacheable(true)
#Cache(usage= CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Project extends DomainObject implements Comparable<Project>, IconizedComponent, Commentable {
#ManyToMany(targetEntity=Student.class)
#JoinTable(name="project_student")
#Sort(type=SortType.NATURAL) //Required by hibernate
#QueryInit({"user"})
private SortedSet<Student> projectParticipants = new TreeSet<Student>();
private Project(){}
//attributes, get+set methods etc
}
#Entity
#Cacheable(true)
#Cache(usage= CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) //Hibernate specific
public class Student extends Role {
public Student(){}
//attributes, get+set methods etc
}
#Entity
#DiscriminatorColumn(name = "rolename", discriminatorType = DiscriminatorType.STRING, length = 8)
#Table(name="role", uniqueConstraints={#UniqueConstraint(columnNames={"user_id","rolename"}, name = "role_is_unique")})
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public abstract class Role extends LazyDeletableDomainObject implements Comparable<Role> {
#ManyToOne(optional=false)
protected User user;
public Role(){}
//attributes, get+set methods etc
}
#Entity
#Table(name="user")
#Cacheable(true)
#Cache(usage= CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) //Hibernate specific
public class User extends LazyDeletableDomainObject implements Comparable<User>, IconizedComponent {
private String firstName;
private String lastName;
public User(){}
//attributes, get+set methods etc
}
Query:
private BooleanExpression authorsNameContains(String searchTerm){
QUser user = new QUser("user");
user.firstName.containsIgnoreCase(searchTerm).or(user.lastName.contains(searchTerm));
QStudent student = new QStudent("student");
student.user.eq(user);
return QProject.project.projectParticipants.contains(student);
//java.lang.IllegalArgumentException: Undeclared path 'student'. Add this path as a source to the query to be able to reference it.
}
I have also tried annotating the projectParticipants set in Project with
#QueryInit("*.*")
But that gives the same exception. Any hints?
#Timo Westkämper
#siebZ0r
Thanks for your attention. Sorry for the delayed reply and incorrectly phrased question. Actually what I wanted to do was to write a working BooleanExpression.
In combination with the annotations already made, this was what I was after:
private BooleanExpression authorsFirstNameContains(String searchTerm){
return QProject.project.projectParticipants.any().user.firstName.containsIgnoreCase(searchTerm);
}
I got this right with the help of a colleague.