How to implement #RollbackExecution method for Mongock - java

I have simple document class:
public class Player {
private String name;
private String age;
}
I want to extend it with field Parameters:
public class Player {
private String name;
private String age;
private Parameters parameters;
public class Parameters {
boolean leftFooted;
boolean leftHanded;
}
}
My Mongock #Execution method would be:
#Execution
public void execution(PlayerRepository playerRepository) {
playerRepository.findAll()
.stream()
.map(this::setDefaultParameters)
.forEach(playerRepository::save);
}
with method:
private Player setDefaultParameters(Player player) {
if (player.getParameters == null) {
player.setParameters(new Parameters(false, false));
}
return player;
}
My question is - how to implement #RollbackExecution method if documents created after model extension can also have Parameters field with values 'false, false' and I just can not set Parameters field to null for all documents in database?

First, am I right on the assumption that, before executing the #Execution method, there may be some documents which already have the parameters field with some value? And, after the execution, you struggle to identify those ones amended in the actual change unit versus those that already had the parameters field?
If that’s the case, you really are not breaking anything, and you would leave the #RollbackExecution empty, and the updated documents will remain with its default value in parameters field.
If you still care, you need to somehow mark those documents that you updated in the ChangeUnit (with a flag or something), so you can filter by the flag and restore your changes.

Related

How to serialize and deserialize multi argument enum in Java using JPA 2.1 or lower (Postgres)

public enum CameraType {
CAMERA(false, false, "External lens ", ""),
CameraType{
boolean collector,
boolean hidden,
String description
) {
this.collector = collector;
this.granular = hidden;
this.description = description;
} // end ctor
public void setHide(boolean hidden) {
this.hide = hidden;
}
} // end enum
I have few Instance of CameraType.
I have a setter for "hidden" property which on certain condition is set to true or false.
Now I serialize CameraType with few other fields inside SecurityEntity.
```
#Entity
#Table
public class Security {
Few more fields...
#Enumerated(EnumType.STRING)
#Column(nullable = false)
private CameraType cameraType
And other fields...
}
```
When I deserialize the value of "hidden" field is always false. If I understand correctly, during deserialization ctor is called and default is assigned.
Is there a way I can retain the value of "hidden" field(true or false) after deserialization per instance of CameraType.
I am using Postgres DB 10.
enter code here
Please Please help. I am out of clues.
By definition, enums are immutable elements of a fixed set. Because of this, to represent an enum value you just need its name. That's exactly what JPA does during serialization/deserialization.
You are trying to violate the rules. While Java allows you to treat enums almost as plain objects, JPA treats them according to what they are supposed to be. That's why your code is not working.
You can either:
make CameraType into a class and serialize it as such, or
split CameraType into two parts, for example enum CameraType (immutable) and class CameraConfig (with all the mutable fields)
The former answer is correct : enums must be immutable and dividing parts into immutable and mutable data is a good choice.
One addition here: using the enum values for database storage is often not a good choice, because when another developer decides to refactor the enum names and you are after this reading old entries from database, you got a crashing application...
So I would suggest to use javax.persistence.AttributeConverter to deserialize/serialize an enum in a specific and rename save way.
Here a very simple example with an enum called MyDefinition:
enum MyDefinition{
ONE("def_one"),
TWO"def_two"),
THREE("def_three"),
;
private String id;
private MyDefinition(String id){
this.id=id;
}
public String getId(){
return id;
}
public static MyDefinition fromId(String id) {
for (MyDefinition definition : MyDefinition.values()) {
if (definition.id.equals(id)) {
return definition;
}
}
return null;
}
}
Here the converter:
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
#Converter(autoApply = true)
public class MyDefinitionAttributeConverter implements AttributeConverter<MyDefinition, String> {
#Override
public String convertToDatabaseColumn(MyDefinition attribute) {
if (attribute == null){
return null;}
}
return attribute.getId();
}
#Override
public MyDefinition convertToEntityAttribute(String dbData) {
return MyDefinition.fromId(dbData);
}
So we can use the ids for database. A renaming of the enum names will no longer lead to a crashing application when reading old data.

Design pattern for Immutable object with eventual immutability

I want to construct an object which I want to pass on to the rest of the application as an immutable object. However problem here is that some fields are directly available at object construction time whereas some require a response from an RPC call. RPC call takes some time to return and I do not want to block the callers during this time because I would like callers to use the fields which are passed in during the object construction time. I have a design in mind but wanted to know if there is a standard pattern.
Following example illustrates what I am looking for.
public class Player {
private int name; // Available at construction time
private int age; // Available at construction time
private String profileUrl; // Need to get this from an RPC call
// useful for checking whether we have profileUrl
private boolean profileUrlAvailable = false;
Player(int name, int age) {
this.name = name;
this.age = age;
}
// usual getter methods
// Package protected for immutability
void setProfileUrl(String profileUrl) {
this.profileUrl = profileUrl;
this.profileUrlAvailable = true;
}
public boolean hasProfileUrl() {
return profileUrlAvailable;
}
// throws if profile url is not yet available.
public String getProfileUrl() {
if (!profileUrlAvailable) {
throw new IllegalStateException("Profile url not available");
}
return profileUrl;
}
}
This example is not threadsafe, consider that it will be taken care of. To be able to let interested callers know when the profile url is available, I will expose a method to register callables which will be notified when the profileUrl is available.
I think this approach does not work well if I add few more fields similar to profileUrl which will eventually be available. I wanted suggestions on the ways to solve this.
Does it simplify if I make sure that all fields similar to profileUrl are made available at the same time (ie., they are set using a single method) ?
Does Lazy initialization suit your needs?
Code snippet:
public class Player {
private int name; // Available at construction time
private int age; // Available at construction time
private String profileUrl = null; // Need to get this from an RPC call
Player(int name, int age) {
this.name = name;
this.age = age;
}
// usual getter methods
public String getProfileUrl() {
if (profileUrl == null) {
profileUrl = Remote.getProfileUrl();
}
return profileUrl;
}
}
You would move your RPC to the accessor method, in this case getProfileUrl.
Only the first call would actually block waiting for the remote procedure to finish.
Other accessor methods for fields that require this kind of heavy initialization would look the same.
If you can use Player class this way and are able to cache profileUrl for future calls you would solve your problem and hit this pattern's goal.

Why am I not able to fetch database field values in a class using Jooq's into method?

This is my methood with the Jooq query:
private List<UserEmailOrganisation> getEmailsAndOrgNames() {
Result<Record2<String, String>> r = dsl
.fetch(dsl
.select(I_USERS.EMAIL_ID, I_ORGANISATIONS.NAME)
.from(I_USERS)
.leftOuterJoin(I_ORGANISATIONS)
.on(I_USERS.ORGANISATION_ID.equal(I_ORGANISATIONS.ID))
.where(DSL.timestampAdd(I_USERS.UPDATED, MINIMUM_INACTIVE_DATE,
DatePart.DAY).lessOrEqual(DSL.currentTimestamp())));
logger.debug(r.toString());
return r.into(UserEmailOrganisation.class);
}
logger.debug method prints -
|email_id |name |
+-----------------------------------+----------------+
|email1#some.com |org1 |
|email2#some.com |org2 |
So my query is returning some results. So I think the issue is not with the query but the into method.
This is my UserEmailOrganisation class
public class UserEmailOrganisation {
public String emailId;
public String name;
public UserEmailOrganisation(String emailId, String name) {
this.emailId = emailId;
this.name = name;
}
}
From the JOOQ docs
http://www.jooq.org/javadoc/3.5.x/org/jooq/impl/DefaultRecordMapper.html
If no default constructor is available, but at least one "matching" constructor is available, that one is used.
A "matching" constructor is one with exactly as many arguments as this record holds fields
When several "matching" constructors are found, the first one is chosen (as reported by Class.getDeclaredConstructors()
When invoking the "matching" constructor, values are converted onto constructor argument types.
So my code should work right? As it has a matching constructor. But it doesn't. I get the following exception.
org.jooq.exception.MappingException: No matching constructor found on type class com.kubera.insights.admin.jobs.BackOfficeUsersReminderJob$UserEmailOrganisation for record org.jooq.impl.DefaultRecordMapper#2ccd7880
at org.jooq.impl.DefaultRecordMapper.init(DefaultRecordMapper.java:321)
at org.jooq.impl.DefaultRecordMapper.<init>(DefaultRecordMapper.java:257)
at org.jooq.impl.DefaultRecordMapper.<init>(DefaultRecordMapper.java:244)
at org.jooq.impl.DefaultRecordMapperProvider.provide(DefaultRecordMapperProvider.java:81)
at org.jooq.impl.ResultImpl.into(ResultImpl.java:1499)
The problem was I was using an inner class to hold the table values.
That is not possible according to issue -
https://github.com/jOOQ/jOOQ/issues/1821
Moving the class UserEmailOrganisation outside solved the issue.
Is it returning a null result? Can you dump the SQL text of this query, and then run it against the I_USERS table to see if you get a result? You might need to add another constructor to handle a null result.
public class UserEmailOrganisation {
public String emailId;
public String name;
public UserEmailOrganisation(String emailId, String name) {
this.emailId = emailId;
this.name = name;
}
public UserEmailOrganisation(Object response) {
if (response == null) {
// what happens if it's empty?
}
}
}

Multiple Parameters or is there any way to force use of setters (Java)?

I have a java class named Transactions, with several methods. On in particular, inserts in Sqlite database all values representing a new transaction, something like this:
Date; Time; Category; Payee; Payer; Value; Tags; Description (...)
When I call the method to save a new Transaction, it looks like:
new Transactions().saveNewTransaction(String date, String time, String category, Int Payee, Int Payer, String value, String tags, String Description (...)
I think this method seems big, bad for readable code and the best way would be those fields be as variables from Transactions class and the method saveNewTransaction(), takes no parameters but instead accessing the variables inside the class.
The only problem is: how can I force to a class (In my case an Activity class) call all setters needed to save a new transaction?
The risk would be call saveNewTransaction() and several fields with values not set by the Activity class (at least the method assures all fields must be set by the caller)
Thanks!
If your requirement is that each created Transactions object should result in some database entry, you could consider dropping the saveNewtTransaction method and instead perform the save-action within some "builder" class:
public class TransactionBuilder {
private Date date;
public TransactionsBuilder with(#NonNull Date date){
this.date = date;
return this;
}
public Transaction build(){
validateFields();
Transaction transaction = new Transaction();
transaction.set(date);
createADatabaseEntry(transaction);
return transaction;
}
private void validateFields() {
org.springframework.util.Assert.notNull(date, "The date cannot be null.");
}
Have the checker framework issue compiler warnings, when developers try to pass null to setters annotated with #NonNull.
Obviously, in your case, the builder would have some different with()-methods, namely some which matches what is needed for your Transactiion object / saveNewTransaction()-method.
Ps I have not given it any thought what your Transaction class is all about. But if the Transactions class only has that one method, I would create a variant of above mentioned solution:
Create an interface with a single method that defines the functionality.
Create an implementation which contains the logic of your saveNewTransactions()-method. Only difference being that this method should only accept a single argument, which is an "input bean", fx TransactionInputBean.
Create your input bean (a simply class with private fields, and public getters&setters.
Create a builder for the input bean.
you could leave all of these variables uninitialized or set them to a value that shouldn't occur by the setters (eg. -1).
Then in your new Transactions().save(); you'd need to check if they still have that value.
But this solution won't work in the way you wrote above, because you are already saving the transaction when you create the object. And here you'd need to create the new object first and then call all the setters.
Your transactions class could look like this:
package de.jeanma.stackOverflow;
import java.lang.Integer;
import java.lang.String;
public class Transactions{
private String date, time, category, value, tags, description;
private int payee, payer;
public Transactions(){
//Add everything here, needed for the constructor
}
public Transactions(String date, String time, String category, int payee, //I recommend creating an constructor that sets the values as well, beacause you
int payer, String value, String tags, String Description){ // might want to create the object and call the save() directly withoud calling every setter one by one
this.date = date;
this.time = time;
this.category = category;
this.value = value;
this.tags = tags;
this.description = description;
this.payee = payee;
this.payer = payer;
//Add everything here, needed for the constructor as well
}
//Here you can place all your other methods
public void save(){
if(!(isInitialized(date) && isInitialized(time) && isInitialized(category) && isInitialized(value) //here all values are checked if they are initialized.
&& isInitialized(tags) && isInitialized(description) && isInitialized(Integer.valueOf(payee)) //The primitive int's are made to complex Integer's because
&& isInitialized(Integer.valueOf(payer)))){ // the method isInitialized() expects an object
//here you could throw an exception or do something like: System.exit(-1);
}
}
private boolean isInitialized(Object Obj){ // this is the method that's checking if the value is initialized
if(Obj.equals(null)) return false;
else return true;
}
//Add all the setters here (I'm too lazy to do that now)
}
I hope that this awnser satisfies you.

Is there any way to access this.toString()'s value when calling another constructor?

For everyone who is talking about the fact that the object is in an "unitialized state", please refer to the answer to this question which shows that an object reference can be passed around, dereferenced, have methods invoked from it, and have fields accessed before a constructor terminates and all fields have been assigned (including final fields).
So here's the use case:
public class Entity {
private final String name;
public Entity() {
this(toString()); //Nope, Chuck Testa
}
public Entity(String name) {
this.name = name;
}
}
The compiler error is:
Cannot refer to an instance method while explicitly invoking a constructor.
Note that toString() has not been overriden and is the default call from Object.
I'm certainly interested in the philosophical/technical reasons behind this, so if anyone can explain that, that would be an awesome bonus. But I'm looking for a way to call toString() from that default constructor as it refers down to the more specific one with more arguments. The actual use case is a bit more complicated and ends up referring all the way down to a constructor with four arguments, but that shouldn't really matter.
I know I could do something like this...
private static final String TO_STRING_CONSTRUCTOR_ARGUMENT = "aflhsdlkfjlkswf";
public Entity() {
this(TO_STRING_CONSTRUCTOR_ARGUMENT);
}
public Entity(String name) {
this.name = name == TO_STRING_CONSTRUCTOR_ARGUMENT ? toString() : name;
}
... but it seems like a pretty inelegant solution.
So, any way to pull it off? Or any recommended best practices to deal with this situation?
I would prefer not to pass this around until the object is created. Instead I would do this:
public class Entity {
private final String name;
public Entity() {
this(null); // or whatever
}
public Entity(String name) {
this.name = name;
}
public String getName() {
return name != null ? name : Objects.hashCode(this);
}
}
If you can live without the final name, you can use an initializer block:
public class Entity {
private String name;
{name = this.toString();}
public Entity() {
}
public Entity(String name) {
this.name = name;
}
}
this is only available after all calls to this() or super() are done. The initializer runs first after the constructors call to super() and is allowed to access this.
As for the reasons why that is a compiler error, please see section 8.8.7 of the JLS. The reasons why this was made a compiler error are not clear, but consider that the constructor chain has to be the first thing executed when new'ing an Object and look at the order of evaluation here:
public Entity() {
this(toString());
}
toString() is evaluated first before the even the super constructor is invoked. In general this leaves open all kinds of possibilities for uninitialized state.
As a personal preference, I would suggest that everything an object needs to have in order to create valid state should be available within its constructor. If you have no way of providing valid state in a default constructor without invoking other methods defined in the object hierarchy, then get rid of the default constructor and put the onus on the users of your class to supply a valid String to your other constructor.
If you are ultimately just trying invoke the other constructor with the value of toString(), then I would suggest the following instead:
public Entity() {
name = toString();
}
which accomplishes the same goal you set out to achieve and properly initializes name.
As explained in the JLS this is not allowed before the instance is initialized.
However, there are ways to handle your scenario in a consistent manner.
As I see your case, you want to signify either a generated value (toString()) or a user provided value, which can be null.
Given this constraints, using TO_STRING_CONSTRUCTOR_ARGUMENT is failing for at least one specific use case, however obscure it may be.
Essentially you will need to replace the String with an Optional similar to what exists in Google Guava and will be included in Java 8, and seen in many other languages.
Having a StringOptional/StringHolder or whatever you choose, similar to this:
public class StringOptional {
private String value;
private boolean set = false;
public StringOptional() {}
public StringOptional(String value) {
this.value = value;
this.set = true;
}
public boolean isSet() { return set; }
public String getValue() { return value; }
}
Then you can call constructors with the knowledge of the inferred path.
public class Entity {
public Entity() {
this(New StringOptional());
}
public Entity(String s) {
this(new StringOptional(s));
}
private Entity(StringOptional optional) {
super(optional);
}
}
And store this for subsquent need:
if (optional.isSet() ? optional.getValue() : toString();
This is how I usually would handle a maybe-null scenario, hope it augments as an answer.
You cannot 'use' an instance that has not been created yet. By calling a second constructor you are postponing the creation, you cannot use it before the call or in the action of calling.
You can use a static method factory in your class Entity, and put the constructor private:
public class Entity {
private String name;
private Entity() {
}
public Entity(String name) {
this.name = name;
}
public static Entity createEntity() {
Entity result = new Entity();
result.name = result.toString();
return result;
}
}

Categories

Resources