New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Custom exceptions #1112
Comments
The only places for throwing custom exceptions are in your own life cycle hooks or data stores. You could create your own store that extends whatever store you are using. On load, you can throw your own CustomErrorException. |
Just a bit more background. Each DataStore in Elide is responsible for telling Elide which entities it manages. It does this in the populateEntityDictionary method (which Elide calls during startup). It is pretty common to have one store manage a single entity or multiple entities. When using multiple stores, you should register the If I were you and I wanted this customized exception behavior, I would create a store for the Account model that extends the JPAStore and only register the Account bean. For all other models, I would register them in a different JPAStore. Both JPAStores can share the same entity manager factory. To make this a little simpler, I created a PR to allow direct control over the JPAStore and what entities it manages (rather than determining the entities from the Entity Manager Factory). You don't technically need this, but it makes things a little easier. If you go this route, let me know if you have questions. |
Great guidance @aklish, thank YOU and I would go this route as per your advise and will definitely ask you for more guidance. One of other things I also got from your input here is that there might be some particular side-effect when using multiple stores. It would be better if you have an example to demonstrate that point. Sorry to ask you for that as I still do not have enough understanding of the code base and obviously do not have a good view of possible integration points of Elide where I can hook my custom implementation. |
I've updated the docs (in PR) to show how to enable multiple stores: Please take a look and let me know if that makes sense. To override the store in your case, I would do something like: //AccountStore is your custom store to manage account models.
public class AccountStore extends JpaDataStore {
public AuditStore(EntityManagerSupplier entityManagerSupplier,
JpaTransactionSupplier transactionSupplier) {
//Using the code from the PR I submitted, we signal that this store only manages Account
super(entityManagerSupplier, transactionSupplier, Account.class);
}
@Override
public DataStoreTransaction beginTransaction() {
//Call the super class to get the actual transaction.
JpaTransaction jpaTx = super.beginTransaction();
jpaTx.begin();
return new AccountStoreTransaction(jpaTx);
}
}
//TransactionWrapper just delegates all transaction calls to another wrapped transaction.
public class AccountStoreTransaction extends TransactionWrapper {
public AccountStoreTransaction(DataStoreTransaction wrappedTx) {
this.tx = wrappedTx;
}
//We'll delegate everything but loadObject.
@Override
public Object loadObject(EntityProjection projection, Serializable id,
RequestScope scope) {
Object returnObj = super.loadObject(projection, id, scope);
//Nothing was found by the provided ID.
if (returnObj == null) {
//Throw your custom exception here.
throw new CustomErrorException(...);
}
}
} You may need to also overload Finally, you'll also need to create your other store which doesn't do anything special. Just create a JpaDataStore and pass in the new constructor everything minus Account.class. Let me know if that makes sense. |
Awesome @aklish. I would like to clarify a few more things: I guess you made a typo in the below code segment, it should be AccountStore instead of AuditStore constructor, correct?
Also, are we supporting Maven snapshot so that I can use the latest code (including your last PR)? Thanks for your time. |
Yes - that's a typo. We don't publish snapshots - but the release is going out today. It may take 24 hours to sync to maven central though. If you need something right away without that change, you can override the AccountStore @Override
public void populateEntityDictionary(EntityDictionary dictionary) {
dictionary.bindEntity(Account.class);
} You'll need to do the same thing for the other store with your other models. |
Hi @aklish , Leveraged on your spring-boot-elide example, I first made a custom store as per your guidance:
Then declared a DataStore bean:
However, when I ran the example, the 2 beans EntityManagerSupplier and JpaTransactionSupplier could not be found. So my follow up question is: how to create these 2 beans without creating any circular reference? Thanks. |
The You are already creating those in your You should just remove the following variables:
|
Sorry, I should provide you with following information in the last question: It is always required these Meaning that if I removed these because it is required these 2 functions to be passed in somehow for the custom data store constructor. Specifically, when I ran the example, this error should occur:
Thanks. |
I think I would need to see your code. If you could post a small project to github, I could fork it and send you a PR. If you really want to, you could create beans for the @Bean
public JpaDataStore.EntityManagerSupplier createEntityManagerSupplier(EntityManagerFactory factory) {
return factory::createEntityManager;
}
@Bean
public JpaDataStore.JpaTransactionSupplier createJpaTransactionSupplier() {
return NonJtaTransaction::new;
} However, that shouldn't be necessary. |
I did create these 2 beans and got the error of circular reference as I mentioned in the previous question As per your suggestion, please take a look at the branch Thanks for your time. |
I made these changes to get past that issue:
@Configuration
public class AppConfig {
@Bean
public DataStore dataStore(EntityManagerFactory entityManagerFactory) {
DataStore store1 = new JpaDataStore(
entityManagerFactory::createEntityManager, (NonJtaTransaction::new), Shop.class);
DataStore store2 = new CustomerStore(
entityManagerFactory::createEntityManager, (NonJtaTransaction::new ));
return new MultiplexManager(store1, store2);
}
} |
It works. Big thank @aklish. It was strange that I had done the similar way as you did - just created these 2 beans in the AppConfig then used them to create store 2, but always got circular reference error when I ran the example as I mentioned earlier. Anyway, I can move on and do more experiments from now on. Thank you once again. |
Re-open this as it causes 1118. We do need a proper way to define beans needed for a custom data store. Please advise @aklish . Also, I still do not know how to throw an exception like AccountAlreadyExistsException for the request creating an entity that already exists in database. Is it a proper way to check if an entity id already exists within loadObject or loadObjects method? In any case, can you give me an sample code for that? Thanks. |
Hi all,
I have read the documentation but could not find any information guiding of how to throw a custom exception. Let's say, when a client sends request to an Json API to create an entity with id which already exists, I would throw AccountAlreadyExistsException instead of TransactionException. How can I do that?
To be more specific, I had a look at the source code and was aware of CustomErrorException and HttpStatusException as well but still did not see a clear way of doing that. Looks like I can only throw a custom exception for my business logic code (life cycle hooks). Please advise.
Thanks,
Thai
The text was updated successfully, but these errors were encountered: