How to read Hibernate mapping - java

I need to know which physical column is associated to a persistent's attribute.
e.g.
Class LDocLine has this attribute
private Integer lineNumber;
which is mapped in hibernate like this :
<property name="lineNumber" column="LINENUMBER" type="integer"/>
The method I need is something like :
getColumn("LDocLine","lineNumber) => "LINENUMBER"
I assume its existence internally, but not sure if it's in the public api.
Thanks in advance

Do you have access to Configuration object you've used to build your session factory? If so, you can use the following:
Value v = configuration.getClassMapping(entityName).getProperty(propertyName).getValue();
for (Iterator it = v.getColumnIterator(); it.hasNext(); ) {
Column column = (Column) it.next();
column.getName(); // or .getQuotedName() or bunch of other useful stuff
}
Column documentation.
If you don't have access to configuration, you can obtain column data from SessionFactory instance, however in this case you're technically no longer using public API as you'll have to class cast to internal implementations:
AbstractEntityPersister persister = (AbstractEntityPersister) sessionFactory.getClassMetadata(entityName);
String[] columnNames = persister.getPropertyColumnNames(propertyName);
In both cases entityName is the name of your entity (its class name unless explicitly overridden)

As you mentioned in your reply, you are not having access to 'Configuration' object.In case you are having access to hibernate 'Session' object, then following code may be helpful to you.
Collection clsMetaData = session.getSessionFactory()
.getAllClassMetadata().values();
for (Iterator i = clsMetaData.iterator(); i.hasNext();) {
ClassMetadata cmd = (ClassMetadata) i.next();
System.out.println("cmd" + cmd.getEntityName());
for (String s : cmd.getPropertyNames()) {
System.out.println("prop:" + s);
}
}
In this way you can get details about Class metadata information.

This is something that Hibernate is not generally used for as you do not need to refer to column names in order to retreive objects through HQL or Criteria.
Why do you need this functionality?

Related

jOOQ: best way to get aliased fields (from #as(alias, aliasFunction))

I have to access the same table for multiple references from a "root" table. In order to do so, I'm creating aliases for these tables:
protected final Table<XyzRecord> foo = Tables.XYZ.as("foo", <foo-alias-function>);
protected final Table<XyzRecord> bar = Tables.XYZ.as("bar", <bar-alias-function>);
bar-alias-function would be declared as follows_
protected final Function<Field<?>, String> fooFieldAliasFunction = f -> "foo_" + f.getName();
Now since I'd like to benefit from type safe queries, I need to re-use the same alias-function in my queries to access the fields:
jooq.select()
.from (root)
.leftJoin(foo).on(
checklistTarget.field(fooFieldAliasFunction.apply(Tables.XYZ.ID), Tables.XYZ.ID.getType())
.eq(root.FOO_ID)
)
.leftJoin(bar).on(
checklistTarget.field(barFieldAliasFunction.apply(Tables.XYZ.ID), Tables.XYZ.ID.getType())
.eq(root.BAR_ID)
)
...
;
This seems awefully clumsy (a lot of code) and not terribly efficient (since the aliased field names are probably stored with the aliased table).
I assumed there would be a method on the alias that would give me the aliased field directly (e.g. foo.getField(Tables.XYZ.ID), but that doesn't seem to be the case.
Of course the problem is amplified if I want to select specific fields...
Am I missing something? What's the recommended way of doing this?
Thank you!
I assumed there would be a method on the alias that would give me the aliased field directly (e.g. foo.getField(Tables.XYZ.ID), but that doesn't seem to be the case.
This kind of API would be useful indeed, although the existing Table.field(Field) method shouldn't be retrofitted to assume this behaviour. A new method might be introduced. On the other hand, you could write a simple utility:
<T, R extends Record> Field<T> field(Table<R> table, TableField<R, T> field) {
if (table == foo)
return foo.field(fooFieldAliasFunction.apply(field), field.getType());
else if (table == bar)
return foo.field(barFieldAliasFunction.apply(field), field.getType());
else
throw IllegalArgumentException();
}
And then call it like this:
jooq.select()
.from (root)
.leftJoin(foo).on(field(foo, XYZ.ID).eq(root.FOO_ID))
.leftJoin(bar).on(field(bar, XYZ.ID).eq(root.BAR_ID))
...
;

How to get the property data type in FileNet P8

In FileNet P8, I need to get the datatype of the property of a custom class using JAVA API. Is there any way to do the same?
This should give you an idea of what you need to do:
//SymbolicName of the property we will search for.
String strSearchName = PropertyNames.DATE_LAST_MODIFIED;
//Document (or other object) that we will use to get classDescription.
Document document = (Document) arg0;
PropertyDescription objPropDesc = null;
PropertyDescriptionList pdl = document.get_ClassDescription().get_PropertyDescriptions();
Iterator<?> iter = pdl.iterator();
while (iter.hasNext())
{
objPropDesc = (PropertyDescription) iter.next();
// Get SymbolicName property from the property cache
String strPropDescSymbolicName = objPropDesc.get_SymbolicName();
if (strPropDescSymbolicName.equalsIgnoreCase(strSearchName))
{
// PropertyDescription object found
System.out.println("Property description selected: " + strPropDescSymbolicName);
System.out.println(objPropDesc);
TypeID type = objPropDesc.get_DataType();
System.out.println(type.toString());
break;
}
}
The idea is to:
Take an object (Document in this case).
Get its Class Description.
Get the list of Property Descriptions from the Class Description.
Loop through the Property Descritpions until you locate the Description you are trying to find.
Get the TypeId from the Property Description.
The TypeId contains the information you need to know what the Type is of the Property.
I borrowed code from here : Working with Properties
You should also familiarize yourself with this : Properties
Edit to add a different method:
In the case of creating documents, we need to be able to obtain the Class Description object. This means we will need to perform additional round trips.
// Get the ClassDescription
String strSymbolicName = "myClassName";
ClassDescription objClassDesc = Factory.ClassDescription.fetchInstance(myObjStore, strSymbolicName, null);
// find the PropertyDescription
PropertyDescription pds = null;
PropertyDescriptionList pdl = objClassDesc.get_PropertyDescriptions()‌​;
Iterator<?> itr = pdl.iterator();
while(itr.hasNext()){
pds = (PropertyDescription) itr.next();
System.out.println("Symbolic Name is "+pds.get_SymbolicName()+" DataType is "+pds.get_DataType().toString());
}
// You can now use it in a loop of several documents if you wish.
...
Take a look here as well : Working With Classes
Christopher Powell's answer is correct, there is one thing it does not cover, though (depending on the definition of the custom class in question). Consider this as a "best practice" or just an extension of the code borrowed from the URL that Christopher mentioned.
The FileNet P8 class hierarchy allows for inheritance of property definitions. Simple example: one can search through the 'Document' class - which is the root class of the class hierarchy - of an object store, and use some property of a subclass in the search sql. Imagine Subclass1 as an immediate subclass of Document. Subclass1 has a property Property1. Even if Document does not have this property in its class description, a search in Document with Property1='somevalue' will return objects of Subclass1 (if there is a match with 'somevalue').
However,
objClassDesc.get_PropertyDescriptions()‌​;
won't return property descriptions of subclasses, thus you might end up with API_PROPERTY_NOT_IN_CACHE errors.
To give you a good starting point if you are facing this case, look at below code:
PropertyDescriptionList ownProps = objClassDesc.get_PropertyDescriptions();
PropertyDescriptionList subclassProps = null;
if (objClassDesc.get_HasProperSubclassProperties()) {
logger.debug("Document class '"+documentClassname+"' supports 'include descendant properties' queries, including descendant properties.");
subclassProps = objClassDesc.get_ProperSubclassPropertyDescriptions();
}
List<PropertyDescription> result = mergePropertyDescriptionLists(ownProps, subclassProps);
If you need to merge those two lists, you are better off using a List of PropertyDescription objects instead of PropertyDescriptionList: the ones returned by the server are read-only.
#SuppressWarnings("unchecked")
protected List<PropertyDescription> mergePropertyDescriptionLists(PropertyDescriptionList list1, PropertyDescriptionList list2) throws Exception {
try {
#SuppressWarnings("unchecked")
List<PropertyDescription> mergedList = new ArrayList<PropertyDescription>();
if (list1 != null && (list1.size() > 0)) {
mergedList.addAll(list1);
}
if (list2 != null && (list2.size() > 0)) {
mergedList.addAll(list2);
}
return mergedList;
} catch (Throwable t) {
throw new Exception("Failed to merge property description lists.", t);
}
}

How to return only specific fields for a query in Spring Data MongoDB?

How can we select specific fields in Spring Data Mongo. I tried the following but I got cast exception from Foo to String.
Using #Query
#Query(value="{path : ?0}", fields="{path : 0}")
String findPathByPath(String path);
Non #Query
String findPathByPath(String path);
Here is the document model
#Document(collection = "foo")
public class Foo {
String name, path;
…
}
MongoDB only returns JSON documents for standard queries. What you'd like to see can be achieved by still returning a List<Foo>. The fields property in #Query will cause only the fields set to 1 being returned.
#Query(value="{ path : ?0}", fields="{ path : 0 }")
List<Foo> findByPath(String path);
We usually recommend introducing a dedicted DTO for that so that you prevent the partially filled Foo instance from being handed to save(…) in turn.
Another option is using the aggreation framework but that's more involved.
You can use
Query query = new Query();
query.fields().include("path");
You can use
public interface PersonRepository extends MongoRepository<Person, String>
#Query(value="{ 'firstname' : ?0 }",fields="{ 'firstname' : 1, 'lastname' : 1}")
List<Person> findByThePersonsFirstname(String firstname);
}
More information in spring data documentation
You can use below query to get specific fields.
#Query(fields="{path : 1}")
Foo findPathByPath(String path);
Records present in DB
{
"name" : "name2",
"path" : "path2"
},
{
"name" : "name3",
"path" : "path3"
}
Below query will return Foo object if path=Path3
{
"name": null,
"path": "path3"
}
we need to specify required fields with fieldName:1 and if don't require then specify it with 0.
I found this question while trying to get the value of a field from a specific object in my collection. From what my research shows, Mongo doesn't provide a way to natively return just a specific field's value from an object. (Disappointing since it seems pretty basic to be able to return just a specific value from a field like I would do in SQL or JSONPath).
To get around this, I wrote the following method using Spring MongoDB with Java 11:
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.MongoTemplate; //not used, just showing which type the template is
import java.util.Arrays;
import static java.util.Objects.requireNonNull;
/**
* Use this method to get a specific field from an object saved in Mongo. The objectId will be
* the id of the object to fetch, and the fieldValueToReturn will be the field to return.
*
* #return the value of the provided field-path converted to the class type provided
*/
public <T> T getFieldValueById(String objectId, String fieldValueToReturn, String collectionName, Class<T> classTypeToReturn) {
var query = new Query().addCriteria(Criteria.where("_id").is(objectId));
query.fields().include(fieldValueToReturn);
var result = mongoTemplate.findOne(query, org.bson.Document.class, collectionName);
requireNonNull(result, "Did not find any documents with id '" + objectId + "' in collection: " + collectionName);
return result.getEmbedded(Arrays.asList(fieldValueToReturn.split("\\.")), classTypeToReturn);
}
The getEmbedded call allows us to get the value of the nested field within the returned Bson document.
To use the method, just call it like this:
getFieldValueById("A1234", "field.nestedfield.nestedfield", "collectionName", String.class);
Hopefully this helps out someone else looking on how to do this.
As a side note, I'm not sure how to extend this to return a list of objects - if I get to that dilemma and solve it, I will try to update this answer. I'm also not sure if this is slower than running a Mongo aggregate query - I haven't tried running any performance comparisons between the two methods.
EDIT 2022-09-30: To return a list of a custom Pojo, it looks like you'll have to use an aggregate query via spring-data-mongodb. Also it seems basic queries are faster than aggregate queries, so use basic queries where possible.
You can directly pass your json query with #Query annotation, for example:
#Query("{ 'firstname' : 'john' }")
Here is the link to all json based queries in Spring Data MongoDb - https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#mongodb.repositories.queries.json-based
You can do the following.
In your repository, you have the method:
String findPathByPath(String path);
If the document looks like this (below), and you want to only return path
#Document(collection = "foo")
public class Foo {
String name;
String path;
String type;
…
}
Then create a Projection interface, e.g.
#Projection(name = "flattenedFoo", types = Foo.class)
public interface FlattenedInvoice {
String getPath(); // This returns the path value in Foo class
}
You can use the getter methods to get the fields from Foo that you are interested in.
Then in your get request, you would have to specify the projectionName.
e.g. with (#Resource path)
#RestResource(path = "findByPath", rel = "findByPath")
String findPathByPath(String path);
You could then say (In a get request):
..../findByPath?path=target_path&projection=flattenedFoo
this would then return a json with only the fields specifies in FlattenedFoo interface.

How to get multiple selected rows in a table or indexedcontainer?

I have a Table whose DataSource is set to a IndexedContainer. I also have multiple selection enabled on my Table. The Question is, how do I get all the selected values.. as an array perhaps?
My IndexedContainer:
private void populateAnalyteTable () {
Analyte[] analytes = Analyte.getAnalytes();
for (Analyte analyte : analytes) {
Object id = ic_analytes.addItem();
ic_analytes.getContainerProperty(id, "ID").setValue(analyte.getId());
ic_analytes.getContainerProperty(id, "Analyte Name").setValue(analyte.getAnalyteName());
}
// Bind indexed container to table
tbl_analytes.setContainerDataSource(ic_analytes);
}
What I'm eventually trying to get is an array of Analyte objects
Why do you want to use IndexContainer? Why don't you use BeanItemCotainer?
Please find the snippet of code below
table.setMultiSelect(true);
BeanItemContainer<Analyte> container = new BeanItemContainer<Analyte>(Analyte.class);
container.addAll(Arrays.asList(Analyte.getAnalytes()));
table.setContainerDatasource(container);
// Add some Properties of Analyte class that you want to be shown to user
table.setVisibleColumns(new Object[]{"ID","Analyte Name"});
//User selects Multiple Values, mind you this is an Unmodifiable Collection
Set<Analyte> selectedValues = (Set<Analyte>)table.getValue();
Please let me know in case it doesn't solve the issue
The vaadin objects supporting MultiSelect all return a set of the selected items.
https://www.vaadin.com/api/com/vaadin/ui/AbstractSelect.html#getValue%28%29
The drawback of this, if you need the selected items in "real" order (as displayed onscreen)
you will then have to find them from the Set to the Container
Just add your object as the Item-ID, like luuksen already propesed. Just change the initialisation of yout IndexedContainer to:
for (Analyte analyte : analytes) {
Object id = ic_analytes.addItem(analyte);
ic_analytes.getContainerProperty(id, "ID").setValue(analyte.getId());
ic_analytes.getContainerProperty(id, "Analyte Name").setValue(analyte.getAnalyteName());
}
table.getValue() is what you are looking for.
This method gives you an Object (if table is single select) or a Set<Object> (if multiselect) of the ID(s) of selected item(s). Runtime type depends on runtime id type, but if you do not need the value you can go around with Object .
If you are looking for Analytes as an array you can do
#SuppressWarnings("unchecked")
Set<Object> selectedIds = (Set<Object>) tbl_analytes.getValue();
List<Analyte> listAnalytes = new ArrayList<Analyte>();
for (Object id : selectedIds) {
listAnalytes.get(tbl_analytes.getItem(id));
}
listAnalytes.toArray();
Note that this approach works with every standard container you may use in Vaadin.
Regards!
EDIT: actually what .getValue() returns depends on the used container. In most of the cases it's the ID.

Join using Hibernate Criteria without using Alias on Restriction

I have an object with 70 attributes. For ease of use I created 2 objects, a 'main' object and a 'details' object, with 1:1 relationship based on an auto-generated integer ID. I had a SEARCH screen that allowed searching on any of the main attributes, for which I build Restriction objects for whatever the user typed in. What was nice was that I did this all through iterating through the fields and building criterion - I didn't need ugly code to specifically handle each of the 30 attributes.
Now they want to search on the details fields as well. My previous screen-field-iterating code works perfectly with no changes (the whole reason for making it 'generic'), however I cannot get the JOIN to work to query on details fields.
class House {
Integer houseID;
String address;
. . .
HouseDetails houseDetails;
}
class HouseDetails {
Integer houseID;
String color;
. . .
}
I tried to create an alias and add it to the criteria :
criteria.createAlias("houseDetails", "houseDetails");
but I get this error :
org.hibernate.QueryException: could not resolve property: color of: House
Here's the thing - I know this would work if I prefix my restrictions with the alias name, but I do NOT want to have to know which table (House or HouseDetails) the field comes from. That would ruin all the automatic looping code and create specific code for each field.
Since SQL can do this as long as the column names are unique :
select * from house, housedetails where house.houseID = housedetails.houseID
and color = 'blue';
I'm wondering how can I get this to work using criteria??
As an aside, but related to this : Is there a way to perform something like Java's introspection on Hibernate HBM.XML mapping files? A number of times I've wanted to do this to solve problems but never found an answer. For the above problem, if I could easily find out which table contained each field, I could add the prefix to the Restriction. Something like this :
// Map of search keys (columns) to searching values
for ( String key : parms.keySet() ) {
String val = parms.get(key);
if ( HIBERNATE-SAYS-KEY-IS-FROM-DETAILS-TABLE ) {
key = "houseDetails." + key;
}
criteria.add(Restrictions.eq(key,val));
}
You can make method to find table name for passed column name.
By using SessionFactory.getClassMetaData() you can get all the information about that class. Once you have ClassMetaData then you can get all the property names. An demo method is shown below:
public String findTableName(String columnName)
{
boolean found=false;
Map<String, ClassMetadata> classMetaData = sessionFactory.getAllClassMetadata();
for (Entry<String, ClassMetadata> metaData : classMetaData.entrySet())
{
String[] propertyNames = metaData.getValue().getPropertyNames();
for (String property : propertyNames)
{
if(property == columnName)
{
return metaData.getKey() + "." + property;
found=true;
break;
}
}
if(found)
break;
}
}
The alias mechanism in hibernate and the Criteria API is pretty well specified. I suggest going through the documentation a little a bit.
I think what you want is something like this:
Criteria criteria = session.createCriteria(House.class);
criteria.createAlias("houseDetails.color", "houseColor");
criteria.add(Restrictions.eq("houseColor", "red"));

Categories