I'm having problems with relation
#RelationshipEntity(type = RelTypes.Tag.TAG_ON_OBJECT_EVALUATION)
public class TagOnObjectEvaluation
{
#StartNode
private Mashup taggableObject;
#EndNode
private Tag tag;
// Other fields, getters and setters
}
In both the entities involved (Mashup and Tag), I have this field (with opposite Direction)
#RelatedToVia(type = RelTypes.Tag.TAG_ON_OBJECT_EVALUATION,
direction = Direction.INCOMING /*Direction.OUTGOING*/)
private Set<TagOnObjectEvaluation> tagOnObjectEvaluations =
new HashSet<TagOnObjectEvaluation>();
Then, I have various service class to manage Tag, Mashup and TagOnObjectEvaluation. The class under test now is the latter.
Note: the name is a bit confusing and it's a legacy from the previous coder, you can read DAO as Service. Also GenericNeo4jDAOImpl (again, read it as GenericServiceNeo4jImpl) simply defines standard methods for entities management (create(), find(), update(), delete(), fetch() )
#Service
public class TagOnObjectEvaluationDAONeo4jImpl extends
GenericNeo4jDAOImpl<TagOnObjectEvaluation> implements
TagOnObjectEvaluationDAO
{
#Autowired
private TagOnObjectEvaluationRepository repository;
public TagOnObjectEvaluationDAONeo4jImpl()
{
super(TagOnObjectEvaluation.class);
}
public TagOnObjectEvaluationDAONeo4jImpl(
Class<? extends TagOnObjectEvaluation> entityClass)
{
super(entityClass);
}
#Override
public TagOnObjectEvaluation create(TagOnObjectEvaluation t)
{
Transaction tx = template.getGraphDatabaseService().beginTx();
TagOnObjectEvaluation savedT = null;
try
{
// This is to enforce the uniqueness of the relationship. I know it can fail in many ways, but this is not a problem ATM
savedT =
template.getRelationshipBetween(
t.getTaggableObject(), t.getTag(),
TagOnObjectEvaluation.class,
RelTypes.Tag.TAG_ON_OBJECT_EVALUATION);
if (savedT == null)
savedT = super.create(t);
tx.success();
}
catch (Exception e)
{
tx.failure();
savedT = null;
}
finally
{
tx.finish();
}
return savedT;
}
}
It seems pretty straightforward until now.
But when I'm trying to persist a RelationshipEntity instance, I have many problems.
#Test
public void testRelationshipEntityWasPersisted()
{
TagOnObjectEvaluation tagOnObjectEvaluation = new TagOnObjectEvaluation(taggedObject, tag);
tagOnObjectEvaluationDao.create(tagOnObjectEvaluation);
assertNotNull(tagOnObjectEvaluation.getId());
LOGGER.info("TagOnObjectEvaluation id = " + tagOnObjectEvaluation.getId());
tagDao.fetch(tag);
assertEquals(1, tag.getTaggedObjectsEvaluations().size());
}
The last test fail: the size is 0 and not 1. Also, although it seems that the entity is correctly stored (it gets an id assigned), if I'm navigating the db later on there is no track of it at all.
I've also tried to add the relationship in a different way, using the sets of the involved nodes; f.e.
tag.getTaggedObjectsEvaluations().add(tagOnObjectEvaluation);
tagDao.update(tag);
but with no improvements at all.
You need to change the direction of the relationship in your entity Mashape, (entity corresponding to the #StartNode of your #RelationshipEntity TagOnObjectEvaluation).
#NodeEntity
class Mashape {
// ...
#RelatedToVia(type = RelTypes.Tag.TAG_ON_OBJECT_EVALUATION, direction = Direction.OUTGOING)
private Set<TagOnObjectEvaluation> tagOnObjectEvaluations = new HashSet<TagOnObjectEvaluation>();
}
Just point that according to the specifications of #RelatedToVia spring-data-neo4j annotation, the direction by default is OUTGOING, so you really don't need to specify the direction in this case. This also should be correct:
#RelatedToVia(type = RelTypes.Tag.TAG_ON_OBJECT_EVALUATION)
private Set<TagOnObjectEvaluation> tagOnObjectEvaluations = new HashSet<TagOnObjectEvaluation>();
Hope it helps.
Related
I am struggeling with a design - persistence issue . I have a tree and all elements in this tree are subclasses of the same abstract superclass. The tree has a root object with a list of children which themselves can have children, and so on. Changes to attributes of a children would need to be propagated to all parent levels. To make it easier to illustrate, let's say we deal with products which would have multiple releases, having multiple development phases. Each of these would have a lifetime. So we would have something like
#Entity
#Access(AccessType.PROPERTY) // JPA reading and writing attributes through their getter and setter methods
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(
discriminatorType = DiscriminatorType.STRING,
name = "lifetime_level",
length=10
)
public abstract class LifeTime implements Serializable {
// all classes are incomplete for sake of brevity
protected Integer version; // Optimistic locking support
protected LocalDate plannedBegin; // The calculated, potentially shifted, begin resulting from children assignments
protected LocalDate plannedEnd;
private LifeTime parent;
protected List<LifeTime> children;
#ManyToOne
public LifeTime getParent() {
return parent;
}
public void setParent(LifeTime parent) {
this.parent = parent;
}
#OneToMany(mappedBy="parent")
public List<LifeTime> getChildren() {
return children;
}
private void setChildren(List<LifeTime> children) {
this.children = FXCollections.observableList(children);
}
public void addChild(LifeTime child) {
children.add(child);
}
public LocalDate getPlannedBegin() {
return plannedBegin;
}
public void setPlannedBegin(LocalDate aBegin) {
this.plannedBegin = aBegin;
adjustParentBegin(parent, this);
}
public LocalDate getPlannedEnd() {
return plannedEnd;
}
public void setPlannedEnd(LocalDate aEnd) {
this.plannedEnd = aEnd;
adjustParentEnd(parent, this);
}
protected void adjustParentBegin(LifeTime parent, LifeTime child) {
if(child.getPlannedBegin.isBefore(parent.getPlannedBegin)) {
parent.setPlannedBegin(child.getPlannedBegin);
}
}
protected void adjustParentEnd(LifeTime parent, LifeTime child) {
if(child.getPlannedEnd.isAfter(parent.getPlannedEnd)) {
parent.setPlannedEnd(child.getPlannedEnd);
}
}
#Version
public Integer getVersion() {
return version;
}
private void setVersion(Integer version) {
this.version = version;
}
}
We would have the concrete classes Product, Release and Phase. All would extend LifeTime. As root object we have a product. The product has children representing releases of the product and each release would have several development phases. We would also have baselines, iterations that we ignore for the moment. Next, we have somewhere a service class that cares for handling the business logic:
#Service
public class LifeTimeService {
#Autowired ProductRepository productRepository;
#Autowired ReleaseRepository releaseRepository;
#Autowired PhaseRepository phaseRepository;
public Product createProduct() {
Product product = new Product();
release.setPlannedBegin(LocalDate.now());
release.setPlannedEnd(LocalDate.now());
product = productRepository.save(product); // without will throw TransientPropertyValueException: object references an unsaved transient instance when saving release
createRelease(product);
return productRepository.save(product);
}
public Release createRelease(Product product) {
Release release = new Release();
product.addChild(release);
release.setParent(product);
release.setPlannedBegin(product.getPlannedBegin()); // initial values
release.setPlannedEnd(product.getPlannedEnd());
release = releaseRepository.save(release); // without will throw TransientPropertyValueException: object references an unsaved transient instance when saving phases
addPhases(release); // add phases and adjust begin and end of the release
return releaseRepository.save(release);
}
protected void addPhases(Release release) {
LocalDate date = release.getPlannedBegin;
Phase phase1 = new Phase();
release.addChild(phase1);
phase1.setParent(release);
phase1.setPlannedBegin(date);
date = date.plusMonth(3);
phase1.setPlannedEnd(date);
phaseRepository.save(phase1);
phase2 = new Phase();
release.addChild(phase2);
phase2.setParent(release);
phase2.setPlannedBegin(date);
date = date.plusMonth(3);
phase2.setPlannedEnd(date);
phaseRepository.save(phase2);
}
}
Let's say we have Controller class, that makes use of the Service
#Controller
public class MyController {
#Autowired LifeTimeService service;
protected Product product;
public void myTest() {
Product product = service.createProduct(); // this creates a product with an initial release and all phases
Release release = service.createRelease(product); // now the product has a later plannedEnd than the corresponding database row
}
}
Obviously, I want that the createRelease method creates and returns a release. The issue is that it alters all levels of the tree: It creates phases but also changes the parent product begin and end date. I would need to save the product after I call createRelease to persist this change. This approach doesn't scale if the tree has more levels. Otherwise, if I save the product within createRelease, the product in the myTest method would have the wrong version. Or createRelease returns the saved parent - what is counter intuitive - and I have to code a method which return last created release. This is all unsatisfying.
While the above class example follows the Domain Driven Design, whereby each object is in charge of securing its integrity, I was although thinking about Anemic Domain Model and moving the two adjustment methods to the service class and make them recursive. But I don't see how it changes or fixes the above issue.
In reality my tree goes over at least 5 hierarchical levels. So whatever solution you propose, it should scale to multiple levels.
I need to find a proper solution to have a Spring-Boot #Component (singleton) class hold a List of database table objects, which could be accessed throughout the life of an application. I need to get a value of a certain language column value (there could be many language columns) depending on the parameters.
My idea was to do it like this:
#Component
public class CardTypeValueComponent {
private List<CardTypesTabModel> listOfCardTypes;
private CardTypesModelRepository cardTypesModelRepository;
private static final String UNKNOWN = "-";
#Autowired
public CardTypeValueComponent(CardTypesModelRepository cardTypesModelRepository) {
Assert.notNull(cardTypesModelRepository, "CardTypesModelRepository cannot be null");
this.cardTypesModelRepository = cardTypesModelRepository;
}
#PostConstruct
private void getAllCardTypesFromDb() {
this.listOfCardTypes = cardTypesModelRepository.findAll();
}
public String getCardTypeLanguageValue(int cardType, String language) {
String cardTypeLangValue = UNKNOWN;
for (CardTypesTabModel cardTypesTabModel : listOfCardTypes) {
if (cardTypesTabModel.getTypeId() == cardType && "spanish".equals(language)) {
cardTypeLangValue = cardTypesTabModel.getSpanishValue();
} else {
cardTypeLangValue = cardTypesTabModel.getEnglishValue();
}
}
return cardTypeLangValue;
}
}
Is it a proper way of completing such a task whilst keeping in mind that the table object column count could increase in the future?
Excuse me for the pseudo code. Thanks.
Added more details:
CardTypesTabModel Entity class:
#Entity
public class CardTypesTabModel {
private int type;
private String englishValue;
private String spanishValue;
// other values, getters & setters
}
What you're trying to do is re-inventing the caching mechanisme.
You may consider to relay on the Spring Cache Abstraction http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html then choose JCache (JSR-107) as implementation.
I will start by saying that:
I'm using ODA (godmode,khan,marcel).
I'm the only code signer.
sessionAsSigner is working the first time I load an XPage that calls it.
sessionAsSigner becomes null after I reload a page (cmd + R) but not when I'm subsequently referencing it in any action during in the context of the viewScope lifetime.
I'm implementing #Gidgerby 's concept of controller classes
I would also add that sessionAsSigner works consistently if I just prepare a simple XPage that does the following:
<p>
<xp:text value="session: #{session.effectiveUserName}" />
</p>
<p>
<xp:text value="sessionAsSigner: #{sessionAsSigner.effectiveUserName}" />
</p>
Now, I am not using SSJS. I'm JSF/EL oriented as much as I can, according to my current knowledge. So, the way I access Domino back-end is unconventional for a Domino XPages programmer.
Where I can't get getSessionAsSigner to work consistently is when I try to do the above mentioned thing...
Here is my XPage controller (backing bean):
public class TestPageController extends StandardXPageController {
private static final long serialVersionUID = 1L;
private AnswerDTO answer;
public TestPageController() {
loadQuotation();
}
private void loadQuotation() {
AnswerDAO answerDAO = Factory.createAnswerDAO();
try {
answer = answerDAO.read("doc_id");
} catch (Exception e) {
e.printStackTrace();
}
}
public AnswerDTO getAnswer() {
return answer;
}
}
AnswerDTO is a POJO. I'm using the DAO/DTO design pattern.
The AnswerDAO implementation - with simplified code (wrap method is just a mere mapping of fields) - as following:
public class AnswerDAODominoImpl implements AnswerDAO {
private transient Session s;
private transient Database db;
private Session getSession() {
if (s == null) {
s = XSPUtil.getCurrentSessionAsSigner();
}
return s;
}
private Database getDatabase() throws NotesException {
if (db == null) {
db = getSession().getDatabase("server_name", "server_path");
}
return db;
}
public AnswerDTO read(String id) throws Exception {
Database db = getDatabase();
return wrap(db.getDocumentByID(id));
}
}
This is the ViewHandler class:
public class ViewHandler extends ViewHandlerExImpl {
public ViewHandler(final javax.faces.application.ViewHandler delegate) {
super(delegate);
}
#SuppressWarnings("unchecked")
public UIViewRoot createView(final FacesContext context, final String pageName) {
try {
// pageName is the XPage name prefixing the slash (e.g. "/home")
String pageClassName = pageName.substring(1);
Class<? extends XPageController> controllerClass = null;
try {
controllerClass = (Class<? extends XPageController>) context.getContextClassLoader().loadClass(
"com.sea.xsp.controller." + StringUtil.getProperCaseString(pageClassName) + "PageController");
} catch (ClassNotFoundException cnfe) {
controllerClass = StandardXPageController.class;
}
XPageController controller = controllerClass.newInstance();
Map<String, Object> requestScope = (Map<String, Object>) context.getApplication().getVariableResolver().resolveVariable(context, "requestScope");
requestScope.put("controller", controller);
UIViewRootEx root = (UIViewRootEx) super.createView(context, pageName);
root.getViewMap().put("controller", controller);
requestScope.remove("controller");
// MethodBinding beforePageLoad = context.getApplication().createMethodBinding("#{controller.beforePageLoad}", new Class[] { PhaseEvent.class });
// root.setBeforePageLoad(beforePageLoad);
return root;
} catch (Exception e) {
e.printStackTrace();
}
return super.createView(context, pageName);
}
}
Basically, what the viewhandler does is to check the existence of a java class which prefix is the XPage name itself.
eg. test.xsp = something.something.package.TestPageController
This approach allows me to forget about declaring specific XPage related classes as generic managed beans in the faces-config.xml
All the XPages will get an easy handle to their corresponding backing bean that will always be named #{controller}
Now, having that being said if I simply write the following in an XPage, everything will work the first time, but not a second time (getSession() is OK, getSessionAsSigner is null), never ever again. I need to push a new change to the database (design update after any change to the java code and xsp.application.forcefullrefresh=true) and it will work again, but, again, just the first time the page is loaded.
<p>
<xp:text value="answer doc id: #{controller.answer.id}" />
</p>
Ideas?
This is possibly due to a bug we discovered a bit ago with the XPages runtime, somehow related to ClassLoader#loadClass. It turns out that using that as you do (and as I used to) can cause sessionAsSigner to stop working after the first page load. The fix is to switch to Class.forName(String, true, ClassLoader): https://github.com/jesse-gallagher/XPages-Scaffolding/commit/d65320fd6d98ff2fbaa814a95eb38ce7bad5a81d
What happens if you run through in Java debugger? Is it going into XSPUtil.getSessionAsSigner() the second time round?
I tend to use ExtLibUtil.resolveVariable() to get a handle on sessionAsSigner if godmode is enabled. Alternatively, there's org.openntf.domino.utils.Factory.getSessionAsSigner(). Do those give a different result?
(In RC3 that is still be available but is marked deprecated in favour of Factory.getSession(SessionType.SIGNER) because we're supporting implementations outside XPages, so there are more session types involved.)
I have a class and there are variables inside it as well. Sometimes I want to ignore some fields and sometimes not when deserializing (maybe at serializing too). How can I do it at Jackson?
For serialization, "filtering properties" blog entry should help. Deserialization side has less support, since it is more common to want to filter out stuff that is written.
One possible approach is to sub-class JacksonAnnotationIntrospector, override method(s) that introspect ignorability of methods (and/or fields) to use whatever logic you want.
It might also help if you gave an example of practical application, i.e what and why you are trying to prevent from being deserialized.
You might want to use JsonViews ( took it originally from http://wiki.fasterxml.com/JacksonJsonViews - broken now - web archive link: https://web.archive.org/web/20170831135842/http://wiki.fasterxml.com/JacksonJsonViews )
Quoting it:
First, defining views means declaring classes; you can reuse existing ones, or just create bogus classes -- they are just view identifiers with relationship information (child inherits view membership from parents):
// View definitions:
class Views {
static class Public { }
static class ExtendedPublic extends PublicView { }
static class Internal extends ExtendedPublicView { }
}
public class Bean {
// Name is public
#JsonView(Views.Public.class) String name;
// Address semi-public
#JsonView(Views.ExtendPublic.class) Address address;
// SSN only for internal usage
#JsonView(Views.Internal.class) SocialSecNumber ssn;
}
With such view definitions, serialization would be done like so:
// short-cut:
objectMapper.writeValueUsingView(out, beanInstance, ViewsPublic.class);
// or fully exploded:
objectMapper.getSerializationConfig().setSerializationView(Views.Public.class);
// (note: can also pre-construct config object with 'mapper.copySerializationConfig'; reuse)
objectMapper.writeValue(out, beanInstance); // will use active view set via Config
// or, starting with 1.5, more convenient (ObjectWriter is reusable too)
objectMapper.viewWriter(ViewsPublic.class).writeValue(out, beanInstance);
and result would only contain 'name', not 'address' or 'ssn'.
You should probably look at the modules feature of recent Jackson versions.
One possible mechanism would be to use a BeanDeserializerModifier.
I've been looking for a useful online tutorial or example, but nothing immediately appears. It might be possible to work something up if more is known of your context. Are you managing your ObjectMappers manually, or using them in a JAX-RS setting, injected in Spring, or what?
I searched the entire web (yes I did) to find the answer. then I wrote something on my own.
I'm working with Jackson ion deserialisation. I wrote a custom reader that ignores the fields dynamically.
You can do the same thing for json deserialisation.
Lets assume an entity like this.
User {
id
name
address {
city
}
}
Create a tree structure to represent field selection.
public class IonField {
private final String name;
private final IonField parent;
private final Set<IonField> fields = new HashSet<>();
// add constructs and stuff
}
Custom Ion Reader extending from amazon ion-java https://github.com/amzn/ion-java
public class IonReaderBinaryUserXSelective extends IonReaderBinaryUserX {
private IonField _current;
private int hierarchy = 0;
public IonReaderBinaryUserXSelective(byte[] data, int offset, int length,
IonSystem system, IonField _current) {
super(system, system.getCatalog(), UnifiedInputStreamX.makeStream(data, offset, length));
this._current = _current;
}
#Override
public IonType next() {
IonType type = super.next();
if (type == null) {
return null;
}
String file_name = getFieldName();
if (file_name == null || SystemSymbols.SYMBOLS.equals(file_name)) {
return type;
}
if (type == IonType.STRUCT || type == IonType.LIST) {
IonField field = _current.getField(getFieldName());
if (field != null) {
this._current = field;
return type;
} else {
super.stepIn();
super.stepOut();
}
return next();
} else {
if (this._current.contains(file_name)) {
return type;
} else {
return next();
}
}
}
#Override
public void stepIn() {
hierarchy = (hierarchy << 1);
if (getFieldName() != null && !SystemSymbols.SYMBOLS.equals(getFieldName())) {
hierarchy = hierarchy + 1;
}
super.stepIn();
}
#Override
public void stepOut() {
if ((hierarchy & 1) == 1) {
this._current = this._current.getParent();
}
hierarchy = hierarchy >> 1;
super.stepOut();
}
Construct dynamic view. This Tree dynamically created and passed to the reader to deserialise.
Let's say we only need city inside the address.
IonField root = new IonField("user", null);
IonField address = new IonField("address", root);
IonField city = new IonField("city", address);
address.addChild(city);
root.addChild(id);
//now usual stuff.
IonFactory ionFactory = new IonFactory();
IonObjectMapper mapper = new IonObjectMapper(ionFactory);
File file = new File("file.bin"); // ion bytes
byte[] ionData = Files.readAllBytes(file.toPath());
IonSystem ionSystem = IonSystemBuilder.standard().build();
IonReader ionReader = new IonReaderBinaryUserXSelective(ionData, 0, ionData.length, ionSystem, root);
User user = mapper.readValue(ionReader, User.class);
Have a class:
class Node implements Serializable
{
private String name;
public String getName { return name; }
public void setName(String val){ name = val; }
public Node(){}
}
#PersistenceCapable(identityType = IdentityType.APPLICATION, detachable="true")
class NodeBag implements Serializable
{
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
Long id;
#Persistent(serialized="true")
private ArrayList<Node> nodes = new ArrayList<Node>();
public String getNodes { return nodes; }
public void setNodes(ArrayList<Node> val){ nodes = val; }
public NodeBag(){}
}
I can save it to the db with this
PersistenceManager pm = PMF.getManager();
try
{
pm.makePersistent(newBag);
}
finally
{
pm.close();
}
But when i load it back
PersistenceManager pm = PMF.getManager();
Query q = pm.newQuery(NodeBag.class);
try
{
List<NodeBag> pipelines = (List<NodeBag>)q.execute();
return nodeBags; // nodeBags[0].nodes is always empty
}
finally
{
q.closeAll();
}
Nodebag.nodes is always empty!
Did i miss something?
Thanks in advance.
Regards,
Paul
In your call to return the objects you can use the FetchPlan to specify what FetchGroup to return. See JDO docs for more information on the FetchGroup options.
You can ensure that all the entities are fetched, by specifying in your PersistenceManager the FetchGroup to use. The modified code is shown below:
PersistenceManager pm = PMF.getManager();
pm.getFetchPlan().setGroup(FetchGroup.ALL);
Query q = pm.newQuery(NodeBag.class);
try {
List<NodeBag> pipelines = (List<NodeBag>)q.execute();
return nodeBags; // nodeBags[0].nodes is always empty
} finally {
q.closeAll();
}
I had a heck of a time getting fetch groups to work. Both Query and PersistenceManager have a getFetchPlan(), but only the one on PersistenceManager seems to work.
Also, make sure you make your objects detachable and use pm.detachCopyAll() on the result.
Missed putting it in the fetch plan ? mark in default fetch group perhaps, or access the field, or put in a custom fetch plan, as per the DataNucleus docs and JDO spec.
Actually, i wanted also the return the answer across the wire by converting it to JSON.
And i've managed to load the child objects. The trick i use is detach. By detaching, everything will be loaded.
Thanks.
Use Collection insted of List and it should start working as long as it is inside a transaction