I've implemented a soft delete behavior in my imaginary Video rental app, and now I am trying to implement a way to reactivate my "deleted" customers, but I can't get my approach to work, surely something simple, but google did not let me find the answer, so here I am.
Here is an excerpt from my repo interface (JpaRepository):
#Query("select m from Movie m where m.isDeleted = true")
List<Movie> findAllIsDeleted();
#Override
#Query("select m from Movie m where m.isDeleted=false")
List<Movie> findAll();
#Modifying
#Transactional
#Query("update Movie m set m.isDeleted=true where id=?1")
void softDelete(Long id);
In my service class I have:
public List<Movie> findAllMovies(String filterText) {
if (filterText == null || filterText.isEmpty()) {
return movieRepository.findAll();
} else {
return movieRepository.search(filterText);
}
}
public List<Movie> findAllDeletedMovies() {
return movieRepository.findAllIsDeleted();
}
And an excerpt from my listview class looks like:
...
Checkbox showDeleted = new Checkbox("Show deleted movies", e -> {
updateList();
Notification.show(e.getValue().toString());
});
...
private void updateList() {
if (showDeleted.getValue() == true) {
grid.setItems(service.findAllDeletedMovies());
}
grid.setItems(service.findAllMovies(filterText.getValue()));
}
But obviously there is something wrong in the listener part, or there is a silent "sure we want to help feature" that I am not aware of. Because the updateList function is not executed. What have I missed?
The problem lies in the implementation of your updateList method.
No matter if the value of the checkbox is true, at the end it always sets the items again that are returned by service::findAllMovies.
move the last statement into an else block and it should work.
private void updateList() {
if (showDeleted.getValue()) { // btw, if(boolValue == true) is redundant. just do if(boolValue)
grid.setItems(service.findAllDeletedMovies());
} else {
grid.setItems(service.findAllMovies(filterText.getValue()));
}
}
I Don’t Know you data table design,
but you can try this
"select m.* from Movie m where m.isDeleted = true"
Related
I'm trying to figure out how to sort my recyclerview's data by click of a button. So far I've got it sorted initially as I start the application and add new items on the list. But I'd like to be able to sort it by different parameters as I'm using the app, by clicking a button "Sort by distance".
I'm using the MVVM architecture and am working with LiveData.
Here's my MainActivity (Not completely, but irrelevant parts are omitted)
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView recyclerView = findViewById(R.id.recyclerview);
final RunListAdapter adapter = new RunListAdapter(new RunListAdapter.RunDiff());
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
mRunViewModel = new ViewModelProvider(this).get(RunViewModel.class);
mRunViewModel.getAllRuns().observe(this, runs -> {
adapter.submitList(runs);
});
sortByDistanceButton = findViewById(R.id.sortByDistance);
sortByDistanceButton.setOnClickListener(view -> {
//DO SOMETHING
});
Here's my RunViewModel
public class RunViewModel extends AndroidViewModel {
private RunRepository mRepository;
private final LiveData<List<Run>> mAllRuns;
public RunViewModel(Application application) {
super(application);
mRepository = new RunRepository(application);
mAllRuns = mRepository.getAllRuns();
}
LiveData<List<Run>> getAllRuns() {
return mRepository.getAllRuns();
}
public void insert(Run run) {
mRepository.insert(run);
}
public void delete(Run run) {
mRepository.delete(run);
}
}
Here's my RunRepository
public class RunRepository {
private RunDao mRunDao;
private LiveData<List<Run>> mAllRuns;
RunRepository(Application application) {
RunRoomDatabase db = RunRoomDatabase.getDatabase(application);
mRunDao = db.runDao();
mAllRuns = mRunDao.getRunsByDate();
}
LiveData<List<Run>> getAllRuns() {
return mRunDao.getRunsByDate();
}
void insert(Run run) {
RunRoomDatabase.databaseWriteExecutor.execute(() ->
mRunDao.insert(run));
}
public void delete(Run run) {
RunRoomDatabase.databaseWriteExecutor.execute(() -> {
mRunDao.deleteRun(run);
});
}
}
And finally here's my RunDao
public interface RunDao {
#Insert(onConflict = OnConflictStrategy.IGNORE)
void insert(Run run);
#Query("DELETE FROM run_table")
void deleteAll();
#Query("SELECT * FROM run_table ORDER BY date")
LiveData<List<Run>> getRunsByDate();
#Query("SELECT * FROM run_table ORDER BY distance ASC")
LiveData<List<Run>> getRunsByDistance();
#Query("SELECT * FROM run_table ORDER BY duration ASC")
LiveData<List<Run>> getRunsByDuration();
#Delete
void deleteRun(Run run);
}
I've tried to pass on a "choice" argument and setting the choice to a different one, but to no avail. I've tried to read about other's having similar problems, but I haven't been able to figure how to apply any of them to my situation. I'm completely stumped.
Here are some SO questions I've read and tried to wrap my head around.
This one actually is very similar to mine and the original question asked figured out a solution, but never shared the details of the implementation =)
How to sort LiveData from Room Database? Doing a button to switch recyclerview's item's order
https://www.reddit.com/r/androiddev/comments/n9nraa/i_want_to_sort_a_livedatalistplayers_from_room_in/
how to properly sort a list using repository pattern and live data in an MVVM android application?
Moving from top to bottom,
You are observing mRunViewModel.getAllRuns()
Your mRunViewModel.getAllRuns() is mRepository.getAllRuns();
Your mRepository.getAllRuns(); is mRunDao.getRunsByDate();
So you are so far just unconditionally routing LiveData from Room to your view.
Simplest way to achieve switching could be this:
Change your RunDao to have single parameterized method like this example
#Query("SELECT * FROM Persons ORDER BY
CASE WHEN :isAsc = 1 THEN first_name END ASC,
CASE WHEN :isAsc = 0 THEN first_name END DESC")
List<Person> getPersonsAlphabetically(boolean isAsc);
(your implementation will be different, including return type and parameter type, but the above should give the idea)
Route the above dao method into repo and use updated repo inside vm. Every time view decides it's time to show some different sorting order, it will talk to view model, viewmodel will pull repo's method and repo will pull dao with proper parameter, dao will give you back livedata with properly sorted content.
I need to check if user is allowed to cancel appointment. Right now i first call service method which return boolean and if it is false then I call another method which returns reason in String. It is working but I think it's ugly. Do you have any ideas how can I improve this? Or maybe this approach is okay?
AppointmentService:
public boolean isUserAllowedToCancelAppointment(int userId, int appointmentId) {
User user = userService.findById(userId);
Appointment appointment = findById(appointmentId);
// only scheduled appointments can be canceled
if(!appointment.getStatus().equals("scheduled")){
return false;
}
// other conditions...
}
public String getCancelNotAllowedReason(int userId, int appointmentId) {
User user = userService.findById(userId);
Appointment appointment = findById(appointmentId);
if(!appointment.getStatus().equals("scheduled")){
return "status";
// other conditions and reasons...
}
Controller:
#GetMapping("/{id}")
public String showAppointmentDetail(#PathVariable("id") int appointmentId, Model model, #AuthenticationPrincipal CustomUserDetails currentUser) {
if (appointmentService.isUserAllowedToCancelAppointment(currentUser.getId(), appointmentId)) {
model.addAttribute("allowCancel", true);
} else {
model.addAttribute("allowCancel", false);
model.addAttribute("cancelNotAllowedReason", appointmentService.getCancelNotAllowedReason(currentUser.getId(), appointmentId));
}
}
Create an enum of possible issues:
enum Issue {TOO_LATE, NOT_SCHEDULED, ... }
And then create a single validation method which returns a Set<Issue>:
public Set<Issue> validate(int userId, int appointmentId) { ... }
Then you can check the different cases and if they apply add the Issue to the set:
Set<Issue> issues = EnumSet.noneOf(Issue.class);
if( not scheduled ) {
issues.add(Issue.NOT_SCHEDULED);
}
if( too late ) {
issues.add(Issue.TOO_LATE);
}
return issues;
Then at the call site you can check if issues.isEmpty():
Set<Issue> issues = appointmentService.validate(userId, appointmentId);
model.addAttribute("allowCancel", issues.isEmpty());
model.addAttribute("cancelNotAllowedReason", issues);
You are making two calls, while you should only make one.
This is indeed not recommended.
Drop the boolean method, and keep something like:
public String getCancelNotAllowedReason(int userId, int appointmentId) {
User user = userService.findById(userId);
Appointment appointment = findById(appointmentId);
if(!appointment.getStatus().equals("scheduled")){
return "not yet scheduled";
}
if ( appointment.isDateTooClose()) {
return "Too late to cancel";
}
// other conditions and reasons...
return "Valid";
}
In the calling class:
if ( service.getCancellNotAllowedReason(1, 1).equals("Valid")) {
//cancel
} else {
// show information message
}
It might be better, though, to return a List<String> of reasons. Otherwise, your client might fix the first issue, and assume he can cancel than, just to get stuck on the next issue.
getCancelNotAllowedReason() can simply return null when a cancel is allowed. No need for a separate check function.
String reason = appointmentService.getCancelNotAllowedReason(currentUser.getId(), appointmentId);
model.addAttribute("allowCancel", reason == null);
model.addAttribute("cancelNotAllowedReason", reason);
I had to create an account to ask this question because I couldn't find the right way to do this. The only thing that comes close is this question here, but it doesn't go all the way and I'm still stuck. Here we go...
I'm trying to build an app following as much of the Architecture Components principles.
I'm currently trying to add a row in one of my database table, and get the ID of this row in return, to then insert a row in another table, with a reference to the first one.
I've created my database object:
#Entity(indices = {#Index("id")})
public class Search {
#PrimaryKey(autoGenerate = true) private int id;
...
And the corresponding DAO:
#Dao
public interface SearchDao {
#Insert
long insert(Search search);
...
As you can see, my DAO returns a long with the created ID. This is the behavior which was pointed out in the question I linked before, and documented here.
Since I'm following Android Architecture Components principles, I'm using a Repository class to do all my database related work. In this Repository, I've created a public method to insert a new object, which is creating and executing an AsyncTask to do the work:
public class Repository {
public void insertSearch(Search search) {
new insertSearchAsyncTask(this.mSearchDao).execute(search);
}
...
private static class insertSearchAsyncTask extends AsyncTask<Search, Void, Long> {
private SearchDao mAsyncTaskDao;
insertSearchAsyncTask(SearchDao dao) {
this.mAsyncTaskDao = dao;
}
#Override
protected Long doInBackground(final Search... params) {
long id = this.mAsyncTaskDao.insert(params[0]);
return id;
}
}
I know I can use the onPostExecute(long id) method to do stuff with the result of the doInBackground method, but this onPostExecute method cannot return anything to the insertSearch method, where I created the AsyncTask and executed it.
I know need to change the return type of my insertSearch method to long. However if I want to have something to return, I need to get the result of the execution of the AsyncTask. How can I do that?
I've tried this (according to the validated answer):
public class Repository {
private long result_id = 0;
public long insertSearch(Search search) {
new insertSearchAsyncTask(this.mSearchDao).execute(search);
return result_id;
}
private class insertSearchAsyncTask extends AsyncTask<Search, Void, Long> {
private SearchDao mAsyncTaskDao;
insertSearchAsyncTask(SearchDao dao) {
this.mAsyncTaskDao = dao;
}
#Override
protected Long doInBackground(final Search... params) {
long id = this.mAsyncTaskDao.insert(params[0]);
return id;
}
#Override
protected void onPostExecute(Long search_id) {
result_id = search_id;
}
}
}
But this feels very very wrong. I had to make the insertSearchAsyncTask class not-static, and I have to store the result of the insert in an attribute of my Repository.
I'm hoping there is a better/correct way of doing this.
I've also looked at other suggested answers on the link above, especially one about Delegates, but this doesn't suit my need as I need the method insertSearch to return the result, not another one called by the AsyncTask when it finishes.
I hope I've explained my problem clearly enough.
Any idea anyone?
Thanks a lot!!
How to use hql update query using hibernate template
thi is the hql statement "update Login set empSmartId = 48750005" +" where empPassword = 6328ef1675ddb7106eba8dc2661961d7"
using getHibernatetemplate()
Current code:
public class LoginDaoImp extends HibernateDaoSupport implements LoginDao {
public boolean resetAttempt(Login login) {
try { login.setAttempt(0);
getHibernateTemplate().update(login);
return true;
} catch (Exception e) { e.printStackTrace(); }
return false; }
i can save whole pojo class above code work but i want to use where condition to update only hibernateTemplate to update the data
you would be looking something like this
public class LoginDaoImp extends HibernateDaoSupport implements LoginDao
{
public boolean resetAttempt(Login login)
{
try
{
// you should create method for login to retrived based on password
//Remember getting login by password is not correct way, Instead you you should use primary key
//Getting persisted object of Login
Login persisted_login = getLoginByPassword(6328ef1675ddb7106eba8dc2661961d7);
//Setting value in persisted object of Login
persisted_login.setEmpSmartId (48750005);
getHibernateTemplate().update(persisted_login);
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}
I know this question is very old but, may this solution help someone else...
Here is a trick to update a record in DB.
The trick is first fetch the required record from the DB, update the required fields using setter methods of associated POJO class & then give a call to hibernateTemplate.save(Object entity) method, just like below:-
public void updateUser(User user, String password) {
#SuppressWarnings("unchecked")
List<User> results = (List<User>) hibernateTemplate
.find("FROM User where username = ?", new Object[] { new String(user.getUsername()) });
User userToBeUpdate = results.get(0);
userToBeUpdate.setPassword(password);
hibernateTemplate.save(userToBeUpdate);
}
This is working solution in my case.
so.. I have a checkboxSelectionGrid like the one in this link: http://gwt-ext.com/demo/#checkboxSelectionGrid
I want to select multiple post tex all with the same company.
how do I do that?
Well, it is a little unclear as to whether you are wanting to preselect several with the same company name, or do something with them afterwards, but here is how I went about doing it. Perhaps it will be of some use to someone.
Note I'm using Reversible Fees, not Companies per the linked code, but the concept is the same.
private GridEx fees;
private GridEx<BeanModel> fees;
.
.
.
fees.getStore().add(feeItems); // this finishes adding all the items.
List<BeanModel> preselectedItems = new ArrayList<BeanModel>();
for (BeanModel feeItem : (fees.getStore() != null && fees.getStore().getModels() != null) ? fees.getStore().getModels() : Collections.<BeanModel>emptyList()) {
if (feeItem.<ReversibleFee>getBean().isSelected()) {
preselectedItems.add(feeItem);
}
}
fees.getSelectionModel().select(preselectedItems, false);
If you are wanting to do something with the data every time a checkbox is checked, you can do it like so:
fees.getSelectionModel().addSelectionChangedListener(new SelectionChangedListener<BeanModel>() {
#Override
public void selectionChanged(SelectionChangedEvent<BeanModel> beanModelSelectionChangedEvent) {
feeTotal.setValue(0);
for (BeanModel anItem : fees.getStore().getModels()) {
if (beanModelSelectionChangedEvent.getSelection().contains(anItem)) {
anItem.<ReversibleFee>getBean().setSelected(true);
feeTotal.setValue(feeTotal.nullSafeGetValue() + Math.abs(anItem.<ReversibleFee>getBean().getAmount()));
} else {
anItem.<ReversibleFee>getBean().setSelected(false);
}
}
}
});
And finally, if you're wanting to do something with the data after submit, you can do it like so:
for (BeanModel beanModel : fees.getSelectionModel().getSelectedItems()) {
beanModel.<ReversibleFee>getBean().setAmount(788);
}