I want to update a member variable of an object inside my Repository on a LiveData- Object. The problem is, that if I call the getValue() Method, I keep getting an NullPointerException, although the value does exist inside my Room- Library.
My question now is, how do I get the value from the LiveData Object without calling the observe() Method? (I am not able to call the observe method inside my repository, cause that method wants me to enter a LifeCycleOwner- reference, which is not present inside my repository).
Is there any way to get the value out of the LiveData- object?
My architecture looks like that:
ViewModel --> Repository --> Dao
You need to initialize LiveData object in ViewModel before observing it in Activity/Fragment like this
ProductViewModel.java
public ProductViewModel(DataRepository repository, int productId) {
mObservableProduct = repository.loadProduct(mProductId);
}
public LiveData<ProductEntity> getObservableProduct() {
return mObservableProduct;
}
Here observableProduct is LiveData for observing product details which is initialized in constructor and fetched using getObservableProduct() method
Then you can observe the LiveData in Activity/Fragment like this
MainActivity.java
productViewModel.getObservableProduct().observe(this, new Observer<ProductEntity>() {
#Override
public void onChanged(#Nullable ProductEntity productEntity) {
mProduct = productEntity;
}
});
As you already setup your code architecture like
Flow of LiveData is
DAO -> Repository -> ViewModel -> Fragment
You don't need to observe LiveData in repository because you cannot update UI from there. Observe it from Activity instead and update UI from there.
As you are saying its giving null on getValue(), make sure you are updating db and fetching db from single instance of DAO as per I worked with DAO it will not notify db update of one DAO instance to 2nd DAO instance with LiveData
Also you can observeForever as suggested by #Martin Ohlin, but it will not be lifecycle aware and may lead to crashes. Check your requirement before observing forever
Refer to this for Full LiveData Flow
Refer to this for DAO issues
Edit 1 - Without using LifecycleOwner
You can use void observeForever (Observer<T> observer) (reference) method to observe LiveData without providing any LifecycleOwner as I provided by using this context in above example.
This is how you can observe LiveData without providing any LifecycleOwner and observe the LiveData in repository itself
private void observeForeverProducts() {
mDatabase.productDao().loadAllProducts().observeForever(new Observer<List<ProductEntity>>() {
#Override
public void onChanged(#Nullable List<ProductEntity> productEntities) {
Log.d(TAG, "onChanged: " + productEntities);
}
});
}
But you need to call removeObserver(Observer) explicitly to stop observing the LiveData which was automatically done in previous case with LifecycleOwner. So as per documentation
You should manually call removeObserver(Observer) to stop observing this LiveData. While LiveData has one of such observers, it will be considered as active.
As this doesn't require LifecycleOwner you can call this in Repository without using this parameter as you mentioned which is missing in your repository
In order for the LiveData object works well you need to use the observe method. That is if you want to use the getValue() method and expecting a non-null response you need to use the observe method. Make sure initialize the LiveData object in your ViewModel as #adityakamble49 said in his answer. For initialize the object, you can pass the reference of your LiveData object which was created in your Repository:
ViewModel.java
private LiveData<Client> clientLiveData;
private ClientRepository clientRepo;
public ViewModel(ClientRepository clientRepo) {
this.clientRepo = clientRepo;
clientLiveData = clientRepo.getData();
}
Then you have to observe your ViewModel from the Activity and call the method that you want to update in your ViewModel (or Repo, but remember that Repo conects with the ViewModel and ViewModel with the UI: https://developer.android.com/jetpack/docs/guide ):
Activity.java
viewModel.getClient().observe(this, new Observer<Client>() {
#Override
public void onChanged(#Nullable Client client) {
viewModel.methodWantedInViewModel(client);
}
});
I hope it helps.
I'm not sure exactly what you are trying to accomplish here, but it is possible to observe without a LifeCycleOwner if you use
observeForever instead of observe.
Livedata is used to observe the data streams. In case you want to call the get a list of your entities stored within the Live Data. Something like this can be helpful.
public class PoliciesTabActivity extends AppCompatActivity {
private PolicyManualViewModel mViewModel;
private List<PolicyManual> policyManualList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_leaves_tab_manager);
mViewModel = ViewModelProviders.of(PoliciesTabActivity.this).get(PolicyManualViewModel.class);
//Show loading screen untill live data onChanged is triggered
policyManualList = new ArrayList<>();
mViewModel.getAllPolicies().observe(this, new Observer<List<PolicyManual>>() {
#Override
public void onChanged(#Nullable List<PolicyManual> sections) {
//Here you got the live data as a List of Entities
policyManualList = sections;
if (policyManualList != null && policyManualList.size() > 0) {
Toast.makeText(PoliciesTabActivity.this, "Total Policy Entity Found : " + policyManualList.size(), Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(PoliciesTabActivity.this, "No Policy Found.", Toast.LENGTH_SHORT).show();
}
}
});
}
}
One more thing - for others with a similar problem - be aware that live data queries will execute only if there is a live observer (i.e. view listening for updates). It won't fill itself just by "laying" there in declarations, like this:
val myLiveData = repository.readSomeLiveData ()
So make sure that you are observing somewhere your LiveData object, either in view or through Transformations.
Related
I have a method which is annotated with #Transactional , which internally calling multiple inner method for same class which within them may or may not be calling any other external service methods. When it is calling the external service class method , it is working for 1 method meanning it rollbacking , but same service when calling another method of same class [external service class only ] , it is no roll backing , can anyone help me here.
#Transactional
public void processPayments(PaymentRequest request) {
request.getDetails.forEach(payment -> {
method1(payment);
});
// when doSomething1() is success , then its calling below method ,
externalService.doSomething2();// when it api fails , it is rollbacking properly , the process of calling is exactly same. Howcome this is rollbacking not dosomething1() is not rollbacking ?
}
private void method1(PaymentDetails details){
details.getDetails.forEach(detailedPayment -> {
method1_1(detailedPayment);
});
task3();
}
private void method1_1(DetailedPayment detailedPayment){
roundPayment();
task1();
task2();
}
private void roundPayment(){
}
private SomeObject task1(SomeObjet object){
// update object with if conditions
repository.save(object);
}
private SomeObject task2(){
// update object with if conditions
repository.save(object);
}
private SomeObject task3(){
// repository.save(updateSomeObject(someObject));
// externalService.doSomething1(double val1 , double val2); // this is another service , which also uses another service , which uses restTemplate to call external service. , if http status is other than 200 , i am throwing ExternalAPICall Exception , which should roll back full transaction starting from processPayments method
// its not roll backing
}
private void updateSomeObject(SomeObject object){
// update object based on few if conditions
}
Can anyone help me here ? Also i would like to know more about properly use of transactional , like multiple inner method of same class , or multiple inner method of another classe called by proxied class and so on.
The only structural difference between calling doSomething1 and doSomething2 is that the first one is called form inside a inlined function. (which is passed to a stream, which could be implemented some fancy asynchronous way.)
What happens if you refactor your code this way :
#Transactional
public void processPayments(PaymentRequest request) {
for(PaymentDetails details: request.getDetails()) {
method1(details);
}
externalService.doSomething2();
}
(If it works, refactor the other method too, there is good chance that task1 and task2 wont roll back neither, because details.getDetails() has a similar implementation.)
It looks like the ApplicationListenerMethodAdapter hides the method it is annotated for making it impossible to look if that method potentially contains other Annotations. There is some other way around this?
if i have an event listener like this
#EventListener
#SomeOtherAnnotation
public void onSomeEvent(SomeEvent e) {
...
}
and a custom event multicaster
public class CustomEventMulticaster extends SimpleApplicationEventMulticaster {
public <T extends ApplicationEvent> void trigger(final T event,
Function<ApplicationListener<T>, Boolean> allowListener) {
...
}
}
i'd like to do something like trigger only if some annotation exists
customEventMulticaster.trigger(someEvent, (listener) -> {
return listener.getClass().getAnnotation(SomeOtherAnnotation.class) == null;
})
There is a hacky solution - just as case study - but please don't go that way.
Since your application listener is in fact ApplicationListenerMethodAdapter you can use reflection to get method or targetMethod from that class. From there you can get method annotations.
More or less (not checked, pure notepad here)
customEventMulticaster.trigger(someEvent, (listener) -> {
Field f=((ApplicationListenerMethodAdapter)listener).getDeclaredField("method"); // or 'targetMethod' - consult ApplicationListenerMethodAdapter to get the difference
f.setAccessible(true);
Method m=f.get(listener); // cast again if required
anno=m.getAnnotation(yourAnno); // here you can access annotation
return anno == null;
})
To make this at least to pretend ot be safe, add nullchecks and check if listener is indeed castable to ApplicationListenerMethodAdapter
Spring Data REST has the following Event handlers which are fired on HTTP requests like POST, PUT etc.
#RepositoryEventHandler(Author.class)
public class AuthorEventHandler {
Logger logger = Logger.getLogger("Class AuthorEventHandler");
#HandleBeforeCreate
public void handleAuthorBeforeCreate(Author author){
logger.info("Inside Author Before Create....");
}
#HandleAfterCreate
public void handleAuthorAfterCreate(Author author){
logger.info("Inside Author After Create ....");
}
}
My question is if I access another database entity, eg.Book, within #HandleBeforeCreate and modify it, would it occur in a separate transaction or will it occur in the same transaction as the creation of the Author entity?
I already check the Spring Data REST docs but it is not mentioned there.
From my experience, those handlers are performed beyond the main transaction. Literally 'before' and 'after' it. About 'separate' transaction - if you mark your event handler as #Transactional it will be executed in its individual transaction.
Publishing domain events from the aggregate root
If you need to perform some extra actions within the main transaction you can use publishing events from the aggregate root. In this case, you should extend your entity from AbstractAggregateRoot and add to it some methods that register appropriate events, for example:
#Entity
public class Model extends AbstractAggregateRoot {
// entity stuff...
public Model initExtraAction(SomeData payload) {
registerEvent(new ExtraActionEvent(this, payload));
return this;
}
}
where registerEvent is the AbstractAggregateRoot method, and ExtraActionEvent is your custom event, like the folowing:
#Value
public class ExtraActionEvent {
private Model model;
private SomeData payload;
}
Then you can implement an ordinary event handler
#Service
public class EventHandler {
#EventListener
#Transactional(propagation = MANDATORY) // optional
public void handleExtraActionEvent (ExtraActionEvent e) {
Model model = e.getModel();
SomeData payload = e.getPayload();
// Extra actions...
}
}
that will be called in the same transaction as the main one (which saves your entity) if you call initExtendAction method before invoking the save method of your repo (to make sure that this will be done in the same transaction you can use an optional #Transactional(propagation = MANDATORY) annotation):
modelRepo.save(model.initExtraAction(payload));
In the Spring Data REST project we can call initExtraAction method in the 'RepositoryEventHandler' before the entity will be created or updated:
#RepositoryEventHandler(Model.class)
public class ModelEventHandler {
#HandleBeforeCreate
#HandleBeforeSave
public void handleBeforeCreateOrSave(Model model){
// Some manipulations...
model.initExtraAction(...);
}
}
You can find a full example of using AbstractAggregateRoot in the Oliver Gierke Spring RestBucks demo project.
Additional info: DDD Aggregates and #DomainEvents
I currently have a setup where data is inserted into a database, as well as indexed into Solr. These two steps are wrapped in a spring-managed transaction via the #Transaction annotation. What I've noticed is that spring-data-solr issues an update with the following parameters whenever the transaction is closed : params{commit=true&softCommit=false&waitSearcher=true}
#Transactional
public void save(Object toSave){
dbRepository.save(toSave);
solrRepository.save(toSave);
}
The rate of commits into solr is fairly high, so ideally I'd like send data to the solr index, and have solr auto commit at regular intervals. I have the autoCommit (and autoSoftCommit) set in my solrconfig.xml, but since spring-data-solr is sending those commit parameters, it does a hard commit every time.
I'm aware that I can drop down to the SolrTemplate API and issue commits manually, I would like to keep the solr repository.save call within a spring-managed transaction if possible. Is there a way to modify the parameters that are sent to solr on commit?
After putting in an IDE debug breakpoint in org.springframework.data.solr.repository.support.SimpleSolrRepository here:
private void commitIfTransactionSynchronisationIsInactive() {
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
this.solrOperations.commit(solrCollectionName);
}
}
I discovered that wrapping my code as #Transactional (and other details to actually enable the framework to begin/end code as a transaction) doesn't achieve what we expect with "Spring Data for Apache Solr". The stacktrace shows the Proxy and Transaction Interceptor classes for our code's Transactional scope but then it also shows the framework starting its own nested transaction with another Proxy and Transaction Interceptor of its own. When the framework exits its CrudRepository.save() method my code calls, the action to commit to Solr is done by the framework's nested transaction. It happens before our outer transaction is exited. So, the attempt to batch-process many saves with one commit at the end instead of one commit for every save is futile. It seems, for this area in my code, I'll have to make use of SolrJ to save (update) my entities to Solr and then have "my" transaction's exit be followed with a commit.
If using Spring Solr, I found using the SolrTemplate bean allows you to 'batch' updates when adding data to the Solr index. By using the bean for SolrTemplate, you can use "addBeans" method, which will add a collection to the index and not commit until the end of the transaction. In my case, I started out using solrClient.add() and taking up to 4 hours for my collection to get saved to the index by iterating over it, as it commits after every single save. By using solrTemplate.addBeans(Collect<?>), it finishes in just over 1 second, as the commit is on the entire collection. Here is a code snippet:
#Resource
SolrTemplate solrTemplate;
public void doReindexing(List<Image> images) {
if (images != null) {
/* CMSSolrImage is a class with #SolrDocument mappings.
* the List<Image> images is a collection pulled from my database
* I want indexed in Solr.
*/
List<CMSSolrImage> sImages = new ArrayList<CMSSolrImage>();
for (Image image : images) {
CMSSolrImage sImage = new CMSSolrImage(image);
sImages.add(sImage);
}
solrTemplate.saveBeans(sImages);
}
}
The way I've done something similar is to create a custom repository implementation of the save methods.
Interface for the repository:
public interface FooRepository extends SolrCrudRepository<Foo, String>, FooRepositoryCustom {
}
Interface for the custom overrides:
public interface FooRepositoryCustom {
public Foo save(Foo entity);
public Iterable<Foo> save(Iterable<Foo> entities);
}
Implementation of the custom overrides:
public class FooRepositoryImpl {
private SolrOperations solrOperations;
public SolrSampleRepositoryImpl(SolrOperations fooSolrOperations) {
this.solrOperations = fooSolrOperations;
}
#Override
public Foo save(Foo entity) {
Assert.notNull(entity, "Cannot save 'null' entity.");
registerTransactionSynchronisationIfSynchronisationActive();
this.solrOperations.saveBean(entity, 1000);
commitIfTransactionSynchronisationIsInactive();
return entity;
}
#Override
public Iterable<Foo> save(Iterable<Foo> entities) {
Assert.notNull(entities, "Cannot insert 'null' as a List.");
if (!(entities instanceof Collection<?>)) {
throw new InvalidDataAccessApiUsageException("Entities have to be inside a collection");
}
registerTransactionSynchronisationIfSynchronisationActive();
this.solrOperations.saveBeans((Collection<? extends T>) entities, 1000);
commitIfTransactionSynchronisationIsInactive();
return entities;
}
private void registerTransactionSynchronisationIfSynchronisationActive() {
if (TransactionSynchronizationManager.isSynchronizationActive()) {
registerTransactionSynchronisationAdapter();
}
}
private void registerTransactionSynchronisationAdapter() {
TransactionSynchronizationManager.registerSynchronization(SolrTransactionSynchronizationAdapterBuilder
.forOperations(this.solrOperations).withDefaultBehaviour());
}
private void commitIfTransactionSynchronisationIsInactive() {
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
this.solrOperations.commit();
}
}
}
and you also need to provide a SolrOperations bean for the right solr core:
#Configuration
public class FooSolrConfig {
#Bean
public SolrOperations getFooSolrOperations(SolrClient solrClient) {
return new SolrTemplate(solrClient, "foo");
}
}
Footnote: auto commit is (to my mind) conceptually incompatible with a transaction. An auto commit is a promise from solr that it will try to start to write it to disk within a certain time limit. Many things might stop that from actually happening however - a timely power or hardware failure, errors between the document and the schema, etc. But the client won't know that solr failed to keep its promise, and the transaction will see a success when it actually failed.
I have a code that looks like this
public class EmployeeController : Controller
{
public ContextWrapper contextWrapper;
public EmployeeController (IContextWrapper wrapper)
{
contextWrapper = wrapper;
}
In my dependency resolver I have the binding for IContextWrapper
kernel.Bind<IContextWrapper>().To<ContextWrapper>();
The implementation of ContextWrapper has an object which is of type Linq DataContext.
public class ContextWrapper : IContextWrapper
{
public MyDataContext dataContext;
public ContextWrapper(MyDataContext context)
{
this.dataContext = context;
}
Now my action method in this controller looks like this
var empRepository = new EmployeeRepository(contextWrapper);
//do some tests with this repository.
some values = contextWrapper.datacontext.get some values from the database table
//do some tests with these values.
To be able to test this method
I should be able to provide some sort of mock database(not literally) or
make the contextWrapper.datacontext return mocked values or
I even thought of creating another implementation of the IContextWrapper that doesn't use a Linq DataContext object. And creating another constructor for this controller and pass that fake implementation. Also in my dependency resolver I would bind the fake object to the IContextWrapper. Although I do not know how to make Ninject
As a last resort test my method against a test database since it all boils down to this Linq DataContext object and it seems I cannot get rid of it past a certain level.
Problem is the more I read about it, more I get confused. I have tried to give as much detail as possible to explain my problem. If any one has a clear cut idea about how to get this, please suggest.