kbss-cvut / jopa Goto Github PK
View Code? Open in Web Editor NEWJava OWL Persistence API
Home Page: https://github.com/kbss-cvut/jopa/wiki
License: GNU Lesser General Public License v3.0
Java OWL Persistence API
Home Page: https://github.com/kbss-cvut/jopa/wiki
License: GNU Lesser General Public License v3.0
The EntityManager.getReference
method should be supported by JOPA.
This would provide a performance boost in operations like removal or setting a reference to an entity, as the whole instance wouldn't have to be loaded. See the JPA specification for more details.
When using spring-boot-devtools, classpath changes cause app restart. As a result, the generated metamodel (in the first app run) becomes obsolete and the referenced entity classes do not correspond to the classes of the runtime entities (second app run).
Similar issue seems to be tackled here:
https://stackoverflow.com/questions/44657399/spring-boot-jpa-meta-model-issue
@psiotwo
When an attribute is mapped to OWLDataProperty
or OWLAnnotationProperty
and the field type is String
(or a collection of strings), should JOPA perform automatic conversion of values loaded from ontology to it, regardless of their actual type? For example:
class A {
@OWLAnnotationProperty(iri="dcterms:created")
private String created;
}
and the following triple:
ex:a dcterms:created "2018-10-28T23:59:59"^^xsd:dateTime .
Should it be loaded and automatically converted to String
, or should it be skipped because its value type does not correspond to the field range? Or, should this be configurable?
Note that on update/insertion to repository, the triple value would be based on the attribute mapping, i.e., always string. So the dateTime-typed value would be replaced with a string in the above example on update.
It seems like a reasonable feature to support.
It seems that the current implementation of named graph support is counter intuitive or at least incomplete.
Currently, when a context is set for an attribute in a descriptor, it means that the assertion is expected to be in the specified context. While this may make sense for literal values, it seems incorrect for entity references. In this case, one would expect the assertion to be in the subject's context and the target of the assertion to be in the referenced context.
Current status in example:
EntityDescriptor d = new EntityDescriptor(context1);
d.addAttributeContext(att1, context2);
d.addAttributeDescriptor(att2, new EntityDescriptor(context2));
Interpreted as:
:subject a :type :context1
:subject :att1 "object1" :context2
:subject :att2 :object2 :context2
:object2 a :type :context2
Expected, for att2
would be:
:subject a :type :context1
:subject :att2 :object2 :context1
:object2 a :type :context2
I.e., shouldn't the property assertions be always in the subject's context and the attribute context would be used only to specify where the rest of the attributes of the referenced entity are? Or should this be configurable.
RDF collections (lists) should be supported by JOPA as well.
Subtask of #44.
As a user, I need to be able to retrieve string property values in multiple languages in one persistence unit, which JOPA currently does not allow.
The use-case I have is a set of dc:source annotations for a resource. dc:source property should be modeled as an owl:AnnotationProperty, not owl:DatatypeProperty. However JOPA throws a NotYetImplemented exception for the field
@OWLAnnotationProperty ...
Set sources
throws an exception.
Implement support for JPQL queries.
Implement support for the Criteria API.
This would especially improve possibilities of generic filtering in queries.
Add implementation of the OntoDriver for Jena.
GraphDB 8 as a back-end, using RDF4J connector, does not work.
When reading an entity using EntityManager.find, JOPA freezes.
When loading an entity instance A with reference to other entities (e.g. B), only A's post load callback is invoked. In reality, B's post load should be called as well.
When a file-based storage is used (e.g. a TTL file for RDF4J/Jena driver or OWL file in OWL API driver), it would be nice to be able to reload the data from the file through some API.
This feature would be useful in cases when the file storage is used mostly for reading and an external agent may change the file contents. Being able to reload the driver would allow the application to access the updated data.
As a user, I want to be able to use Java 8 streams when working with queries.
What works:
@MappedSuperClass A0 {
@id x;
}
@MappedSuperClass A extends A0{
}
@owlclass B1 extends A{
}
And respectively for B2
When an entity is stored (or to be stored) in a specific context, but one of its attributes is (or will be) stored in the default context, persist and retrieval do not reflect this fact and attempt to keep the attribute in the entity context.
This causes issues for example when working with GraphDB, which stores inferred statements in a separate context, so the user may set default context for attributes containing such data.
When using an in-memory storage in the Jena and RDF4J drivers, I want to be able to set the MemoryStore
(for RDF4J/Sesame) or Dataset
(for Jena).
Consider a case where data are generated directly using RDF4J or Sesame into a memory store/dataset. Then, the user would like to be able to access this data via JOPA.
Support for lexical form of literal values will have to following properties:
@LexicalForm
will be added. It can be used together with @OWLAnnotationProperty
and @OWLDataProperty
.String
.http://www.w3.org/2001/XMLSchema#string
.This should make the feature compatible with RDF 1.1 and OWL (2), which still uses rdf:plainLiteral
, but it has been superseded in RDF 1.1 by xsd:string
.
Similarly to JPA, JOPA should support mapping of SPARQL query results to objects based on the SparqlResultSetMapping
(SqlResultSetMapping
in JPA).
This would help for instance with performance, since it can be used to bulk load data using a single query.
When an OWL entity (class, property) is annotated with an rdfs:comment, OWL2Java should use it to generate javadoc for the resulting artifact (class, attribute, vocabulary constant).
I have two entities - FunctionsDTO (one <=> many) FunctionDTO, mapped like this:
@OWLClass(iri = s_c_functions_dto)
public class FunctionsDTO extends AbstractEntity {
@Sequence
@OWLDataProperty(iri = s_p_has_function_dto, fetch = FetchType.EAGER)
private List<FunctionDTO> functions;
...
}
@OWLClass(iri = s_c_function_dto)
public class FunctionDTO extends AbstractEntity {
...
}
The problem is that serialization only works on the top level of the hierarchy:
{
"http://onto.fel.cvut.cz/ontologies/s-pipes/has-function-dto" : {
"@list" : [
"cz.cvut.kbss.spipes.model.dto.FunctionDTO@316840ee",
"cz.cvut.kbss.spipes.model.dto.FunctionDTO@2a176721",
"cz.cvut.kbss.spipes.model.dto.FunctionDTO@63226973"
]
},
"@type" : [
"http://onto.fel.cvut.cz/ontologies/s-pipes/functions-dto"
]
}
Am I doing something wrong?
JOPA should support simple literals as datatype/annotation property values, i.e. having datatype http://www.w3.org/2001/XMLSchema#string
, not requiring language tag.
Both loading and saving.
If I try to generate vocabulary by maven plugin (jopa-maven-plugin:0.9.12:owl2java-transform) for a configuration that has invalid imported ontologies, the plugin silently ignores them. (see relevant fragment of my configuration in the attachment).
As a developer, I want to be able to use the Java 8 Date and Time API in entities.
Graph A->B->A, where both relationships are Cascade.MERGE causes JOPA to go into infinite loop, ending with StackOverflowException.
In triplestore I have the following triple:
<http://example.org/question-1> <http://onto.fel.cvut.cz/ontologies/form/has-question-origin> <http://example.org/question-origin-1> .
Within JOPA I used the following mapping to object model:
@OWLDataProperty(iri = "http://onto.fel.cvut.cz/ontologies/form/has-question-origin")
private URI origin;
Unfortunately, it did not load any data (i.e. class field origin
was loaded with null
value). As a workaround I can use :
@OWLObjectProperty(iri = "http://onto.fel.cvut.cz/ontologies/form/has-question-origin")
private URI origin;
The workaround works well so it is not an issue to me, but I was wondering if it is correct behavior.
RDF containers should be supported by JOPA as well.
Subtask of #44.
The query parser is unable to recognize parameter names (SPARQL variables) if they are not delimited by a space. E.g. {?a a ?var} does not recognize ?var as variable.
When an entity has a mapped superclass which declares lifecycle callbacks (e.g., @PrePersist
), they are not invoked for the entity instances.
Reimplement the EntityManager.refresh
method, as its current implementation (UoW.revertObject
) does not correspond to JPA behavior.
Currently, each field is accessed using the same property for reading as well as writing.
The goal is to separate these tasks:
Use-case: Providing support for specifying a subproperty of a given property upon data authoring.
When polymorphic instance load is requested, there are multiple strategies possible.
Consider the following example:
@OWLClass(iri="http://example.org/Person")
class Person {
...
}
@OWLClass(iri="http://example.org/Student")
class Student extends Person {
...
}
and a request to load an instance:
entityManager.find(Person.class, id);
We see two main strategies for this case:
This strategy has the following execution plan:
EntityType<Person>
represents entity without subtypes, load entity and exit.id
.http://example.org/Person
, load instance as class Person
.Person
which corresponds to an ontological class contained in types of id
.id
contain http://example.org/Student
, load id
as instance of class Student
.Person
, throw an exception, because the load would be ambiguous.If Person
were abstract, step 1 could be skipped.
EntityType<Person>
and individual id
.http://example.org/Person
, reconstruct a Person
instance and return.Person
which corresponds to an ontological class asserted in the loaded axioms.http://example.org/Student
, determine properties, which are in EntityType<Student>
and not in EntityType<Person>
and were thus not loaded in step 1.Student
and return it.If Person
were abstract, step 2 could be skipped.
Both strategies are suitable for different use cases.
Try-first strategy is useful in cases where subclasses of the superclass do not add any attributes and are used only as a means of classification of the instances. In this case, there would be only one call to the underlying OntoDriver which would load all the necessary data for reconstructing the entity.
Two-step strategy is suitable for cases where subclasses contain additional attributes, because it requires two calls to the underlying OntoDriver anyway, the first one to get the types and the second to load the actual data. It is also more straightforward than the Try-first strategy, because it does not involve any computation of difference between attribute sets of the superclass and the determined subclass.
Note that both strategies lead to the same result in the end - the correct instance being loaded, or an exception being thrown because of class ambiguity.
For the aforementioned reasons, the Two-step strategy should be the default one and will be implemented first.
JOPA should, besides OWL linked lists, support RDF containers and collections.
Hello,
I have been trying to map my RDF4J server ontology with some models in java for now 3 days, and I still get a lot of errors. Maybe you can help. I have tried to follow the methodology used in the reporting app, but with no more luck.
For example, I get mostly 2 kinds of error :
I have installed an RDF4J server, and imported my simple ontology composed of 2 classes - Person & Book - which look like that :
- <http://www.semanticweb.org/ontologies/library.owl#Person> rdf:type rdfs:Class
- <http://www.semanticweb.org/ontologies/library.owl#name> rdfs:domain <http://www.semanticweb.org/ontologies/library.owl#Person>
- <http://www.semanticweb.org/ontologies/library.owl#placeOfBirth> rdfs:domain <http://www.semanticweb.org/ontologies/library.owl#Person>
So a person has a name and a placeOfBirth.
Some instances of the Person class are (lib: = http://www.semanticweb.org/ontologies/library.owl#)
- lib:person1 rdf:type lib:Person
- lib:person1 lib:name "Clint Eastwood"
- lib:person1 lib:placeOfBirth "San Francisco"
- lib:person2 rdf:type lib:Person
- lib:person2 lib:name "Marty McFly"
- lib:person2 lib:placeOfBirth "New York"
My Person Model looks like that :
@OWLClass(iri = "http://www.semanticweb.org/ontologies/library.owl#Person")
public class Person implements Serializable {
@Id
private URI uri;
@OWLDataProperty(iri = "http://www.semanticweb.org/ontologies/library.owl#name")
private String name;
@OWLDataProperty(iri = "http://www.semanticweb.org/ontologies/library.owl#placeOfBirth")
private String placeOfBirth;
public Person(){}
... Getters & setters
}
My DAO looks like that :
@Repository
public class PersonDao extends BaseDao<Person> {
protected PersonDao(){
super(Person.class);
}
public List<Person> findAll(EntityManager em){
List<Person> toto = em.createNativeQuery("SELECT ?x WHERE { ?x a ?type . }", Person.class).setParameter("type", typeUri)
.getResultList();
for(Person p : toto){
System.out.println(p.getName());
System.out.println(p.getPlaceOfBirth());
}
return toto;
}
public Person find(URI uri){
Objects.requireNonNull(uri);
final EntityManager em = entityManager();
try {
return findByUri(uri, em);
} finally {
em.close();
}
}
protected Person findByUri(URI uri, EntityManager em) {
return em.find(type, uri);
}
}
My controller is like that :
@RestController
@RequestMapping("/tests")
public class TestController {
private final PersonService personService;
@Autowired
public TestController(PersonService personService){
this.personService = personService;
}
[...]
@GetMapping(value = "/persons", produces = MediaType.APPLICATION_JSON_VALUE)
public Collection<Person> getAllPersons(){
return personService.findAll();
}
@GetMapping(value = "/person", produces = MediaType.APPLICATION_JSON_VALUE)
public Person getPerson(){
URI uri =
URI.create("http://www.semanticweb.org/webatrio/ontologies/library.owl#person1");
return personService.find(uri);
}
My PersistenceProvider looks like that :
@Configuration
@PropertySource("classpath:config.properties")
public class PersistenceFactory {
private static final Map<String, String> DEFAULT_PARAMS = initParams();
private final Environment environment;
private EntityManagerFactory emf;
@Autowired
public PersistenceFactory(Environment environment) {
this.environment = environment;
}
@Bean
@Primary
public EntityManagerFactory getEntityManagerFactory() {
return emf;
}
@PostConstruct
private void init() {
// Allow Apache HTTP client used by RDF4J to use a larger connection pool
// Temporary, should be configurable via JOPA
System.setProperty("http.maxConnections", "20");
final Map<String, String> properties = new HashMap<>(DEFAULT_PARAMS);
properties.put(ONTOLOGY_PHYSICAL_URI_KEY, environment.getProperty(REPOSITORY_URL.toString()));
properties.put(DATA_SOURCE_CLASS, environment.getProperty(DRIVER.toString()));
this.emf = Persistence.createEntityManagerFactory("books", properties);
}
private static Map<String, String> initParams() {
final Map<String, String> map = new HashMap<>();
map.put(OntoDriverProperties.ONTOLOGY_LANGUAGE, "en");
map.put(SCAN_PACKAGE, "com.example.test.model");
map.put(JPA_PERSISTENCE_PROVIDER, JOPAPersistenceProvider.class.getName());
return map;
}
}
File RestConfig :
@Configuration
@ComponentScan(basePackages = "com.example.test")
public class RestConfig {
@Bean
public ObjectMapper objectMapper() {
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return objectMapper;
}
}
My service class looks like that :
@Service
public class PersonRepositoryService extends BaseRepositoryService<Person> implements PersonService {
private final PersonDao personDao;
@Autowired
public PersonRepositoryService(PersonDao personDao){
this.personDao = personDao;
}
@Override
protected GenericDao<Person> getPrimaryDao() {
return personDao;
}
}
my config.properties :
repositoryUrl=http://localhost:8080/rdf4j-server/repositories/books2
driver=cz.cvut.kbss.ontodriver.sesame.SesameDataSource
For more information, the querying on the server is happening, the error occur when trying to map the results with the model.
Thank you for the help.
JOPA fails to load classes named 'classXyz' that are packed in a JAR file.
Currently, enums are mapped to plain data/annotation literals, so the information about the value being enumerated basically gets lost in the repository. At the same time, OWL already provides a mechanism of enumerating values - owl:oneOf. JOPA should support mapping to this.
Consider the following relationship declaration:
@OWLObjectProperty(iri = Vocabulary.hasA, cascadeType=MERGE)
A a;
When I then do
assert b.getA() != null;
b.setA(new A());
em.merge(b);
JOPA does not remove the original hasA
property assertion, which causes an IC violation exception when the record is loaded later.
When @properties are used they do not distinuguish between object IRIs, literals and their datatypes. For example. I have two OWL Classes:
@owlclass(iri="http://example.org#A")
class A {
@properties
private Map<String,Set> props;
@types
private Set types;
}
@owlclass(iri="http://example.org#B")
class B extends A {
@OWLDataProperty(iri="http://example.org#p")
private Integer p;
}
Then persisting a new instance of A, where the property is set through properties and the type B is set through types stores the property as a language-tagged literal. This makes the property unloadable into B through em.find(), because the type is Integer for B.
Create a new Value class that serves for grouping IRIs and literals with datatypes/language tags
Use a simple language in the values of the @properties map. Each string would be either:
'<'+IRI+'>'
which would be interpreted as IRI and the property would be interpreted as an Object Propertyplainliteral
whicch would be interpreted as a plain literal and equipped with the PU default language tag, i.e. plainliteral
@en'"'+lexform+'"^^'+datatypeIRI
which would be interpreted as a datatyped literal.Currently, a project cannot use JOPA and Lombok due to problems in the Maven build configuration (AspectJ maven plugin and Lombok).
Find a way to configure the build so that both libraries can be used in a project.
Currently, it is possible to specify a descriptor for an object property collection, i.e., a single descriptor is used for all elements of the collection. However, such functionality is not sufficient.
For example, consider the following class:
@OWLClass("skos:Concept")
public class Term {
@OWLObjectProperty(iri="skos:broader")
private Set<Term> parents;
// ... other attributes
}
If the parent terms belong to different vocabularies, which are stored in separate contexts (RDF named graphs), it is currently not possible to reference them.
It should be possible to specify descriptors for individual elements of the collection
When OWL2Java processes an ontology with imports or different contexts and a property/class is encountered multiple times, multiple constants are generated for these occurrences into the vocabulary file.
It should skip properties/classes which were already added.
Currently, only ListAttribute
and SetAttribute
are supported.
Adding support for CollectionAttribute
should be relatively easy. Its semantics would be set-based, as is usual with RDF/OWL.
Currently, JOPA uses a mix of TRANSACTIONAL and EXTENDED persistence scope strategies.
Both scopes should be separated and implemented properly according to JPA spec, which is vague in certain aspects but the general idea is there.
When merging an instance with a reference to another instance, JOPA calculates changes on the reference and considers it a change if any of its attributes have changed, even though it should only check that it is the same instance. An example:
A.hasB = B;
B.name = "Test";
persist(A, B);
B.name = null;
result = merge(A)
=> hasChanges(A) == true, which is wrong, because B is still the same instance.
Correct behavior:
hasChanges(A) == false && result.hasB.name == "Test", because there is no cascading to B, so the value from storage should be used.
As a user, I want to be able to configure RDF4J OntoDriver to create an in-memory or native store with the SPIN sail and pass in a custom ruleset which is to be applied to it.
As a user, I want to be able to use Virtuoso server as storage. This will, according to documentation, require a custom OntoDriver implementation using either Virtuoso's RDF4J or Jena provider.
The driver should probably allow the user to choose whether the RDF4J or Jena Virtuoso Provider will be used for connection. The other choice is to support only one of the providers for starters.
Note that Virtuoso code (including the providers) is licensed under GPLv2, so the driver, which will probably have to incorporate the provider(s) - as they are not published in Maven - will have to adhere to this license as well.
Relevant sources:
When generating a Vocabulary from scripts that have the same prefix defined for different values, there's no way to predict which one will get what Vocabulary entry.
Discovered by @blcham.
jopa-maven-plugin:0.9.14:owl2java-transform fails within my project with error :
[ERROR] Failed to execute goal cz.cvut.kbss.jopa:jopa-maven-plugin:0.9.14:owl2java-transform (generate-sources) on project semantic-pipes-forms: Execution generate-sources of goal cz.cvut.kbss.jopa:jopa-maven-plugin:0.9.14:owl2java-transform failed: An API incompatibility was encountered while executing cz.cvut.kbss.jopa:jopa-maven-plugin:0.9.14:owl2java-transform: java.lang.NoSuchMethodError: com.google.inject.util.Types.collectionOf(Ljava/lang/reflect/Type;)Ljava/lang/reflect/ParameterizedType
It seems to me that the problem is in conflicting dependencies of the plugin:
~/.m2/repository/org/sonatype/sisu/sisu-guice/3.1.0/sisu-guice-3.1.0-no_aop.jar
~/.m2/repository/com/google/inject/guice/4.1.0/guice-4.1.0.jar
as both of them contain:
./com/google/inject/util/Types.class
JDK9 class loader is not a java.net.URLClassLoader
anymore => cz.cvut.kbss.jopa.loaders.EntityLoader.discoverEntities
fails with ClassCastException
.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.