I have faced this problem a few times in the past, but haven't really found a good solution/design for it.
The below example code will generate PDF doc from Entity (Company or Article)
public class Entity
{
int id;
}
public class Company extends Entity
{
private String HQ;
}
public class Article extends Entity
{
private String title;
}
public interface EntityPDFGenerator
{
void generate(Entity entity);
}
public class ArticlePDFGenerator implements EntityPDFGenerator
{
public void generate(Entity entity)
{
Article article = (Article) entity;
// create Article related PDF from entity
}
}
public class CompanyPDFGenerator implements EntityPDFGenerator
{
public void generate(Entity entity)
{
Company company = (Company) entity;
// create Company related PDF
}
}
Main class:
public class PDFGenerator
{
public void generate(Entity entity)
{
EntityPDFGenerator pdfGenerator = getConcretePDFGenerator(entity);
pdfGenerator.generate(entity);
}
// lets make the factory task simple for now
EntityPDFGenerator getConcretePDFGenerator(Entity entity)
{
if(entity instanceof Article){
return new ArticlePDFGenerator();
}else{
return new CompanyPDFGenerator();
}
}
}
In the above approach the problem is with the casting the Entity to the concrete type (casting can be dangerous in later stage of the code). I tried to make it with generics, but then I get the warning
Unchecked call to 'generate(T)'
Can I improve this code?
Here, you go with the suggested changes:
public interface EntityPDFGenerator<T extends Entity> {
void generate(T entity);
}
public class ArticlePDFGenerator implements EntityPDFGenerator<Article> {
public void generate(Article entity)
{
// create Article related PDF from entity
}
}
public class CompanyPDFGenerator implements EntityPDFGenerator<Company> {
public void generate(Company entity)
{
// create Company related PDF
}
}
Short answer
Generics is not the right tool here. You can make the casting explicit:
public class CompanyPDFGenerator implements EntityPDFGenerator
{
public void generate(Entity entity)
{
if (! (entity instanceof Company)) {
throw new IllegalArgumentException("CompanyPDFGenerator works with Company object. You provided " + (entity == null ? "null" : entity.getClass().getName()));
}
Company company = (Company) entity;
System.out.println(company);
// create Company related PDF
}
}
Or you can define some sort of data structure in the entity class and use only that in the printer:
public abstract class Entity
{
int id;
public abstract EntityPdfData getPdfData();
}
// ...
public class CompanyPDFGenerator implements EntityPDFGenerator
{
public void generate(Entity entity)
{
EntityPdfData entityPdfData = entity.getPdfData();
// create Company related PDF
}
}
Long answer
Generics is useful if you know the types at compile-time. I.e. if you can write into your program that actual type. For lists it looks so simple:
// now you know at compile time that you need a list of integers
List<Integer> list = new ArrayList<>();
In your example you don't know that:
public void generate(Entity entity)
{
// either Article or Company can come it. It's a general method
EntityPDFGenerator pdfGenerator = getConcretePDFGenerator(entity);
pdfGenerator.generate(entity);
}
Suppose you want to add type to the EntityPDFGenerator , like this:
public static interface EntityPDFGenerator<T extends Entity>
{
void generate(T entity);
}
public static class ArticlePDFGenerator implements EntityPDFGenerator<Article>
{
public void generate(Article entity)
{
Article article = (Article) entity;
// create Article related PDF from entity
}
}
public static class CompanyPDFGenerator implements EntityPDFGenerator<Company>
{
public void generate(Company entity)
{
Company company = (Company) entity;
// create Company related PDF
}
}
This looks nice. However, getting the right generator will be tricky. Java generics is invariant. Even ArrayList<Integer> is not a subclass of ArrayList<Number>. So, ArticlePdfGenerator is not a subclass of EntityPDFGenerator<T extends Entity>. I.e. this will not compile:
<T extends Entity> EntityPDFGenerator<T> getConcretePDFGenerator(T entity, Class<T> classToken)
{
if(entity instanceof Article){
return new ArticlePDFGenerator();
}else{
return new CompanyPDFGenerator();
}
}
I would suggest to move the getGenerator() method in the Entity class and override it in the Company and Article classes.
Unless, of course, there is a good reason not to.
Related
I have a spring boot service that should persist several entities of two types in a transaction to an Oracle DB. The table for the first entity type is huge (3 Mio. entries/day, partitioned,...) and I have the issue that I need to react on duplicates. There are some fields I use to create a hash on and I have a unique constraint in the database on that field. I thought it is a clever idea to just saveAndFlush entity by entity and react on the ConstraintViolationException. Based on the result of saving the list of first entities, I need to create the second entity and save that as well, but it rolls back everything.
My question now would be if this approach is generally wrong, or ok and there is some small issue? If it is generally wrong, how should I do this duplicate check then (a select upfront is not an option)?
Here is some pseudo-code to get a better idea
#Entity
public class Foo{
public String uniqueHash;
// couple of other properties that will be used to calculate the hash
}
#Entity
public class Bar{
private List goodIds;
private List badIds;
public Bar(List goodIds, List badIds){
this.goodIds = goodIds;
this.badIds = badIds;
}
}
#Repository
#Transactional(noRollbackFor = PersistenceException.class)
public interface FooRepository extends JpaRepository<Foo, String> {
Foo saveAndFlush(Foo f) throws PersistenceException;
}
#Repository
#Transactional(noRollbackFor = PersistenceException.class)
public interface BarRepository extends JpaRepository<Bar, String> {
Bar saveAndFlush(Bar b) throws PersistenceException;
}
SomeService
#Transactional(noRollbackFor = PersistenceException.class)
public void doSomething(List<Foo> foos){
List<String> goodIds = new ArrayList();
List<String> badIds = new ArrayList();
for (Foo foo : foos) {
try {
fooRepository.saveAndFlush(foo);
goodIds.add(foo.getId());
} catch (PersistenceException e) {
if (e.getCause() instanceof ConstraintViolationException) {
badIds.add(foo.getId);
} else {
throw e;
}
}
}
barRepository.saveAndFlush(new Bar(goodIds, badIds));
}
Finally, I found a way to achieve the expected behavior, and even better, I was able to get rid of these "noRollBackFor" attributes. I only restructured the process and try to save everything in a transaction, if it fails, the Exception is caught on the calling method, the input is "cleaned" and the transactional method is called again (recursively). These duplicates are rare situations (happens every 10k Foo instance), so from a performance perspective, it's fine to have these subsequent transactions. Here is the changed pseudo-code again
#Entity
public class Foo{
public String uniqueHash;
// couple of other properties that will be used to calculate the hash
}
#Entity
public class Bar{
private List goodIds;
private List badIds;
public Bar(List goodIds, List badIds){
this.goodIds = goodIds;
this.badIds = badIds;
}
public List getGoodIds(){
return goodIds;
}
public List getBadIds(){
return badIds;
}
}
#Repository
public interface FooRepository extends JpaRepository<Foo, String> {
}
#Repository
public interface BarRepository extends JpaRepository<Bar, String> {
}
public class FooException extends RuntimeException {
private final Foo foo;
public FooException(String message, Foo foo) {
super(message);
this.foo = foo;
}
public getFoo(){
return foo;
}
}
SomeService
public void doSomething(List<Foo> foos, Bar bar){
try{
doSomethingTransactional(foos,bar);
}
catch (FooException e) {
bar.getBadIds().add(e.getFoo().getId());
foos.remove(foo);
doSomething(foos, bar);
}
}
#Transactional
public void doSomethingTransactional(List<Foo> foos, Bar bar){
for (Foo foo : foos) {
try {
fooRepository.saveAndFlush(foo);
bar.getGoodIds.add(foo.getId());
} catch(DataAccessException e) {
if (e.getCause() instanceof ConstraintViolationException
&& ((ConstraintViolationException) e.getCause()).getConstraintName().contains("Some DB Message")) {
throw new FooException("Foo already exists", foo);
} else {
throw e;
}
}
}
barRepository.saveAndFlush(bar);
}
You might be able to use a custom #SQLInsert to make use of Oracles MERGE statement for this purpose. Also see https://stackoverflow.com/a/64764412/412446
I have a utility class OldRemote which has been deprecated now, but still it will be used for a while till the new class NewRemote is stable. And both the utility classes has the same method names and parameters, But the return type pojo classes are different. Even return type pojo structure is same, but naming is different.
In simple, both the function return types are pojo's with different field names.
Is there any generic way to handle this below usecase ?
I have created a service interface which has the generic method contract of both old and new class.
public interface RemoteService {
//contract [ return type is object to receive all/any Pojo classes ]
Object turnOnTV();
static Service GetRemoteservice(boolean isOldRemote){
if(isOldRemote){
return new OldRemote();
}
return new NewRemote();
}
}
OldRemote Class
public class OldRemote implements RemoteService{
#Override
public OldPojo turnOnTV() {
OldPojo oldPojo = new OldPojo();
System.out.println("OldPojo");
return oldPojo;
}
}
NewRemote Class
public class NewRemote implements Service{
#Override
public NewPojo turnOnTV() {
NewPojo newPojo = new NewPojo();
System.out.println("NewPojo");
return newPojo;
}
}
Demo usage of above implementation.
public class DemoTvRemote {
public static void main(String[] args) {
RemoteService remoteService1 = RemoteService.GetRemoteservice(true);
OldPojo oldRemote = (OldPojo) remoteService1.turnOnTV();
RemoteService remoteService2 = RemoteService.GetRemoteservice(false);
NewPojo shr = (NewPojo) Service2.test();
}
}
This above code works fine. But the problem is I don't want to type cast in all the places where turnOnTV() is used in my entire code base. Even If I have to do that, I will have to write a condition to switch between OldPojo and NewPojo where ever the turnOnTV() is invoked.
Is there any way to solve this problem ?
You could create a base class or interface they both extend/implement.
public abstract class RemoteServiceBase<E> {
public abstract E turnOnTv();
}
public class NewRemoteService extends RemoteServiceBase<NewRemotePojo >{
public NewRemotePojo turnOnTv() {
return new NewRemotePojo();
}
}
public class OldRemoteService extends RemoteServiceBase<OldRemotePojo >{
public OldRemotePojo turnOnTv() {
return new OldRemotePojo();
}
}
This would still only work if you know the service type. Otherwise you work with the common generic type as one would expect.
We can deal with this with the following approach :
1) We can create a dummy POJO class in a common location with having the reference of both OldPojo and NewPojo as data members
public class CommonPojo {
OldPojo oldPojo;
NewPojo newPojo;
public void setOldPojo(OldPojo oldPojo){
this.oldPojo=oldPojo;
}
public void setNewPojo(NewPojo newPojo){
this.newPojo=newPojo;
}
public OldPojo getOldPojo(){
return oldPojo;
}
public NewPojo getNewPojo(){
return newPojo;
}
}
2)We can write a Utility method as follow which can give an object of commonpojo :
public class CommonRemote {
public static CommonPojo turnOnTv(Boolean isOldRemote){
CommonPojo commonPojo = new CommonPojo
if(isOldRemote){
OldPojo oldPojo =new OldPojo();
commonPojo.setOldPojo(oldPojo);
}else{
NewPojo newPojo =new NewPojo();
commonPojo.setNewPojo (newPojo);
}
}
}
3) Use this method as turnOnTv() as Follows :
public class DemoTvRemote {
public static void main(String[] args) {
CommonPojo remote1 = CommonRemote.turnOnTv(true);
OldPojo oldRemote = remote1.getOldPojo();
CommonPojo remote2 = CommonRemote.turnOnTv(false);
NewPojo newRemote = remote2.getNewPojo();
}
}
with this approach with little changes in code We can achieve your requirement without any typecasting.
I have three classes:
public AccountTable { void insert(Account account){}}
public CustmerTable { void insert(Customer customer){}}
public TransactionTable { void insert(Transaction Transaction){}}
and an endpoint which receives a message class for insertion as:
public message {
Entity entity; //after deserialization, becomes Account, Customer or Transaction
String entityName; //contains class of the entity
}
How can I redirect to the correct insertion method? The only solution I'm seeing is check using istanceOf and use switch/if, but I don't think that's much scalable if more classes get added, and I can't change Customer, Account or Transaction, as they are legacy code and very sensitive to changes.
I believe you could do something like this:
public interface Saveable <T> {
void save(T entity);
}
public class AccountSaver implements Saveable<Account> {
void save(Account account){
// ... save account
}
}
public class CustomerSaver implements Saveable<Customer> {
void save(Customer customer){
// ... save customer
}
}
public class TransactionSaver implements Saveable<Transaction> {
void save(Transaction transaction){
// ... save transaction
}
}
Well, if Class1/2/3 are service you manage, and you already managed json to right class,
then i'll create a service like
public class MyService {
#Autowired
List<ClasseX> services;
public void save(Object obj){
for (ClasseX service : services) {
if(service.accept(obj)){
service.save(obj);
break;
}
}
}
and add a method on ClassX, where each instance will do the instanceOf check
public boolean accept(Object obj)
i'm developing an android app using MVP pattern.
I'd like to have different presenters, and each one implements getItems, that call a getAll static method of the associated model.
I thought to use generics, ended up like this:
public class BasePresenter<T> {
protected T mModel;
List getItems() {
mModel.getAll();
}
}
public class Presenter extends BasePresenter<Model> {
}
but i cannot access getAll methods using generics, because is not an Object's method.
Since for me would be dumb to write the same boring method getAll() for all presenter, changing only the model, is there there any way to accomplish that?
I'm asking because even in Google's official MVP guide, it use always the same method to retrive data, overriding it on each presenter, and i'm hoping that there is a better way.
I thought to "cast" the superclass mModel, but i don't know how and if it's possible to do, though.
Try this
public class BasePresenter<M extends BaseModel<M>> {
#Nullable
private M mModel;
#Nullable List<M> getItems() {
if (mModel != null) {
return mModel.getModelList();
}
return null;
}
}
And the BaseModel is
public abstract class BaseModel<M> {
private List<M> modelList;
public List<M> getModelList() {
return modelList;
}
public void setModelList(List<M> modelList) {
this.modelList = modelList;
}
}
Sub model
public class LoginModel extends BaseModel<LoginModel> {
#Override
public List<LoginModel> getModelList() {
return super.getModelList();
}
#Override
public void setModelList(List<LoginModel> modelList) {
super.setModelList(modelList);
}
}
And finally presenter is like this
public class LoginPresenter extends BasePresenter<LoginModel> {
//do your code
}
Hope it helps you.
Maybe this can help you:
List getItems(){
if(mModel instanceof TheSuperClassOrInterface){
return ((TheSuperClassOrInterface)mModel).getAll();
}else{
return null;
}
}
Could you guys please help me find where I made a mistake ?
I switched from SimpleBeanEditorDriver to RequestFactoryEditorDriver and my code no longer saves full graph even though with() method is called. But it correctly loads full graph in the constructor.
Could it be caused by circular reference between OrganizationProxy and PersonProxy ? I don't know what else to think :( It worked with SimpleBeanEditorDriver though.
Below is my client code. Let me know if you want me to add sources of proxies to this question (or you can see them here).
public class NewOrderView extends Composite
{
interface Binder extends UiBinder<Widget, NewOrderView> {}
private static Binder uiBinder = GWT.create(Binder.class);
interface Driver extends RequestFactoryEditorDriver<OrganizationProxy, OrganizationEditor> {}
Driver driver = GWT.create(Driver.class);
#UiField
Button save;
#UiField
OrganizationEditor orgEditor;
AdminRequestFactory requestFactory;
AdminRequestFactory.OrderRequestContext requestContext;
OrganizationProxy organization;
public NewOrderView()
{
initWidget(uiBinder.createAndBindUi(this));
requestFactory = createFactory();
requestContext = requestFactory.contextOrder();
driver.initialize(requestFactory, orgEditor);
String[] paths = driver.getPaths();
createFactory().contextOrder().findOrganizationById(1).with(paths).fire(new Receiver<OrganizationProxy>()
{
#Override
public void onSuccess(OrganizationProxy response)
{
if (response == null)
{
organization = requestContext.create(OrganizationProxy.class);
organization.setContactPerson(requestContext.create(PersonProxy.class));
} else
organization = requestContext.edit(response);
driver.edit(organization, requestContext);
}
#Override
public void onFailure(ServerFailure error)
{
createConfirmationDialogBox(error.getMessage()).center();
}
});
}
private static AdminRequestFactory createFactory()
{
AdminRequestFactory factory = GWT.create(AdminRequestFactory.class);
factory.initialize(new SimpleEventBus());
return factory;
}
#UiHandler("save")
void buttonClick(ClickEvent e)
{
e.stopPropagation();
save.setEnabled(false);
try
{
AdminRequestFactory.OrderRequestContext ctx = (AdminRequestFactory.OrderRequestContext) driver.flush();
if (!driver.hasErrors())
{
// Link to each other
PersonProxy contactPerson = organization.getContactPerson();
contactPerson.setOrganization(organization);
String[] paths = driver.getPaths();
ctx.saveOrganization(organization).with(paths).fire(new Receiver<Void>()
{
#Override
public void onSuccess(Void arg0)
{
createConfirmationDialogBox("Saved!").center();
}
#Override
public void onFailure(ServerFailure error)
{
createConfirmationDialogBox(error.getMessage()).center();
}
});
}
} finally
{
save.setEnabled(true);
}
}
}
with() is only used for retrieval of information, so your with() use with a void return type is useless (but harmless).
Whether a full graph is persisted is entirely up to your server-side code, which is intimately bound to your persistence API (JPA, JDO, etc.)
First, check that the Organization object you receive in your save() method on the server-side is correctly populated. If it's not the case, check your Locators (and/or static findXxx methods) ; otherwise, check your save() method's code.
Judging from the code above, I can't see a reason why it wouldn't work.
It took me some time to realize that the problem was the composite id of Person entity.
Below is the code snippet of PojoLocator that is used by my proxy entities.
public class PojoLocator extends Locator<DatastoreObject, Long>
{
#Override
public DatastoreObject find(Class<? extends DatastoreObject> clazz, Long id)
{
}
#Override
public Long getId(DatastoreObject domainObject)
{
}
}
In order to fetch child entity from DataStore you need to have id of a parent class. In order to achieve that I switched "ID class" for Locator<> to String which represents textual form of Objectify's Key<> class.
Here is how to looks now:
public class PojoLocator extends Locator<DatastoreObject, String>
{
#Override
public DatastoreObject find(Class<? extends DatastoreObject> clazz, String id)
{
Key<DatastoreObject> key = Key.create(id);
return ofy.load(key);
}
#Override
public String getId(DatastoreObject domainObject)
{
if (domainObject.getId() != null)
{
Key<DatastoreObject> key = ofy.fact().getKey(domainObject);
return key.getString();
} else
return null;
}
}
Please note that your implementation may slightly differ because I'm using Objectify4.