How best to setup Room and Retrofit - java

I'm currently trying to setup Room with Retrofit. I have a working implementation...
It requests an ApiItemBean, then the ApiDatabase calls its ApiItemDAO to map a databased or new ApiItem with the ApiItemBean into a Single<ApiItem>.
That single is used in the ApiRepository by the ApiRequest, and eventually subscribed to in the figurative Usage.
ApiItemBean
public class ApiItemBean {
private final int a;
private final int b;
public ApiItemBean(int a, int b) {
this.a = a;
this.b = b;
}
public int a() {
return a;
}
public int b() {
return b;
}
}
ApiItem
#Entity(tableName = "api_item") public static class ApiItem {
#PrimaryKey private final int id;
ApiItem(int id) {
this.id = id;
}
public int id() {
return id;
}
}
ApiItemDAO
#Dao public interface ApiItemDAO {
#Insert(onConflict = OnConflictStrategy.REPLACE) long insert(ApiItem apiItem);
#Query("SELECT * FROM api_item WHERE id = :id") ApiItem apiItem(int id);
}
ApiDatabase
#Database(entities = { ApiItem.class }, version = 1) abstract class ApiDatabase extends RoomDatabase {
abstract ApiItemDAO apiItemDAO();
// More...
Single<ApiItem> mapApiItem(ApiItemBean apiItemBean) {
final ApiItem apiItem = new ApiItem(apiItemBean.a());
final ApiItem apiItemDb = apiItemDAO().apiItem(apiItem.id());
if (apiItemDb == null || !apiItemDb.equals(apiItem))
Log.i(getClass().getSimpleName(), String.format("insert=%s", apiItemDAO().insert(apiItem)));
return Single.just(apiItemDAO().apiItem(apiItem.id()));
}
// More...
}
ApiRequest
public interface ApiRequest {
#GET("API_ITEM_BEAN_REQUEST") Single<ApiItemBean> getApiItemBean();
// More...
}
ApiRepository
public class ApiRepository {
private final ApiRequest apiRequest;
private final ApiDatabase apiDatabase;
ApiRepository(ApiRequest apiRequest, ApiDatabase apiDatabase) {
this.apiRequest = apiRequest;
this.apiDatabase = apiDatabase;
}
Single<ApiItem> getApiItem() {
return apiRequest.getApiItemBean().flatMap(apiDatabase::mapApiItem);
}
// More...
}
Usage
public class Usage {
private final ApiRepository apiRepository;
Usage(ApiRepository apiRepository) {
this.apiRepository = apiRepository;
}
public void onUse() {
apiRepository.getApiItem().subscribe();
}
}
I am unsure on how best to check the requests against the database. See ApiDatabase::mapApiItem for an example of how I thought to do it.
Side note: I know there is no specific question, and should possibly be moved to CodeReview, however the code example is not fully working considering it would need to depend on a specific api and I thought it best to try here first. Sorry to any mods.

Related

Consider defining a bean of type 'form' in your configuration

Place that is complaining the error:
#Data
public class AluguelValorForm {
#Autowired
private ValorAluguelMultaService valorAluguelMultaService;
#NotNull #NotEmpty
private String idAluguel;
#NotNull
private Double valor;
public AluguelValor converter(AluguelValorRepository aluguelValorRepository, AluguelForm form ) {
Double valorAluguel = valorAluguelMultaService.valorAluguel(form);
return new AluguelValor(idAluguel,valorAluguel);
}
public AluguelValor update(String idAluguel,Double valor) {
AluguelValor aluguelValor = new AluguelValor();
aluguelValor.setId(idAluguel);
aluguelValor.setValor(valor);
return aluguelValor;
}
Repository:
#Repository
public interface AluguelValorRepository extends MongoRepository<AluguelValor, String> {
Aluguel getReferenceById(String id);
}
Place that I call the method in AluguelValorForm:
#PostMapping
//#CacheEvict(value = "listaDeTopicos",allEntries = true)
public void cadastrar(#RequestBody AluguelForm form) {
Optional<Carro> carro = carroRepository.findByPlaca(form.getPlaca_carro());
Optional<Cliente> cliente = clienteRepository.findByCpf(form.getCpf());
if(carro.isPresent() && cliente.isPresent()) {
Aluguel aluguel2 = form.converter(aluguelRepository);
aluguelRepository.save(aluguel2);
Double valorAluguel = valorAluguelMultaService.valorAluguel(form);
AluguelValor aluguelValor = aluguelValorForm.update(aluguel2.getId(), valorAluguel);
aluguelValorRepository.save(aluguelValor);
}
}
Problem solved. Apparently, it's not possible to #Autowired a class that doesn't have any bean, like my RentValue. That's why I got this error.

cannot create an instance of class - viewmodel ( java )

I am trying to use android viewmodel , livedata with room database. although room database is working finely , when i try to create a instance of viewmodel class i always got following error
Cannot create an instance of class com.ceylonlinux.apwsdelivery.viewmodel.UnproductiveActivityViewModel
these are my viewmodel , activity , entity and dao classes
database class
#Database(entities = { Reason.class}, version = 1)
public abstract class APWSDatabase extends RoomDatabase {
public abstract UnproductiveVisitDao unproductiveVisitDao();
public abstract ReasonDao reasonDao();
public abstract CustomerDao customerDao();
}
entity class
#Entity
public class Reason {
#PrimaryKey
private int reasonId;
private int reasonType;
private String reasonName;
public Reason() {
}
public Reason(int reasonId, int reasonType, String reasonName) {
this.reasonId = reasonId;
this.reasonType = reasonType;
this.reasonName = reasonName;
}
public int getReasonId() {
return reasonId;
}
public void setReasonId(int reasonId) {
this.reasonId = reasonId;
}
public int getReasonType() {
return reasonType;
}
public void setReasonType(int reasonType) {
this.reasonType = reasonType;
}
public String getReasonName() {
return reasonName;
}
public void setReasonName(String reasonName) {
this.reasonName = reasonName;
}
}
Dao class
public interface ReasonDao {
#Insert
void insertAll(Reason... reasons);
#Insert
void insert(Reason reason);
#Query("SELECT * FROM reason")
List<Reason> loadAll();
#Query("SELECT * FROM reason where reasonType = (:reasonType)")
List<Reason> getReasonsByType(Integer reasonType);
#Query("SELECT * FROM reason where reasonType = (:reasonType)")
LiveData<List<Reason>> getReasonsByTypeLive(Integer reasonType);
#Delete
void delete(Reason reason);
}
viewmodel class
public class UnproductiveActivityViewModel extends ViewModel {
private LiveData<List<Reason>> unproductiveReasons;
private Application application;
private ReasonRepository repository;
public UnproductiveActivityViewModel(Application application) {
this.application = application;
try {
repository = new ReasonRepository(application);
unproductiveReasons = repository.getUnproductiveReasons();
}catch (Exception e)
{
Log.e("ERROR",e.toString());
}
}
public LiveData<List<Reason>> getUnproductiveReasons() {
return unproductiveReasons;
}
}
in my activity class i used it like as follows
viewModel = new ViewModelProvider(UnproductiveActivity.this).get(UnproductiveActivityViewModel.class);
viewModel.getUnproductiveReasons().observe(this, reasonsz -> {
reasons = (ArrayList<Reason>) reasonsz;
adapter_reason = new ArrayAdapter<Reason>(UnproductiveActivity.this, android.R.layout.simple_spinner_item, reasons);
adapter_reason.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
un_reason_spinner.setAdapter(adapter_reason);
un_reason_spinner.setPaddingSafe(0, 0, 0, 0);
});

Hibernate createCriteria query with annotation based composite primary key

In my project, I am having trouble writing a createCriteria query with a composite primary key. My Entity class & DAO method are given below -
#Entity
#Table(name="METRICS")
public class Metrics implements Serializable {
private static final long serialVersionUID = -2580493160757497919L;
#EmbeddedId
protected MetricsID metricsID;
#Column(name="PROJ_PERF")
private String proj_perf;
#Column(name="ANALYSIS")
private String analysis;
public String getProj_perf() {
return proj_perf;
}
public void setProj_perf(String proj_perf) {
this.proj_perf = proj_perf;
}
public String getAnalysis() {
return analysis;
}
public void setAnalysis(String analysis) {
this.analysis = analysis;
}
public MetricsID getMetricsID() {
return metricsID;
}
public void setMetricsID(MetricsID metricsID) {
this.metricsID = metricsID;
}
}
#Embeddable
public class MetricsID implements Serializable {
private static final long serialVersionUID = 4691163770334366543L;
#Column(name="PROJECT_ID")
private String project_id;
#Column(name="METRICS_NO")
private int metrics_no;
public String getProject_id() {
return project_id;
}
public void setProject_id(String project_id) {
this.project_id = project_id;
}
public int getMetrics_n0() {
return metrics_no;
}
public void setMetrics_no(int i) {
this.metrics_no = i;
}
}
#Override
#Transactional
public List<Metrics> viewMetrics(String project_id) throws Exception {
List<Metrics> metrics = (List<Metrics>)sessionFactory.getCurrentSession().
createCriteria(Metrics.class).createAlias("metricsID.project_id", "project_id_alias").
add(Restrictions.eqProperty("project_id_alias.project_id", project_id)).list();
return metrics;
}
The error I am getting is - org.hibernate.QueryException: not an association: metricsID.project_id
I searched for several similar examples, and used alias on the suggestion of one of the search results, but it's my first time using an alias. What am I doing wrong?
Why do you need to use an alias? Have you tried to access directly?
Following this example, this code should work
#Override
#Transactional
public List<Metrics> viewMetrics(String project_id) throws Exception {
List<Metrics> metrics =
(List<Metrics>) sessionFactory.getCurrentSession()
.createCriteria(Metrics.class)
.add(Restrictions.eq("metricsID.project_id", project_id))
.list();
return metrics;
}

Why am I able to pass Long to Map.get() method which is not keyed on Longs

I've run into some funky behavior with generics and I was wondering if someone could shed some light as to why this is happening. To start, I have a class Foo which has a field id. The hashCode method on Foo just returns the id. In another class I create a Map<Foo, Double> bar = new HashMap<Foo, Double().
Then, at a later part of the code the strangeness starts, and I am able to do the following (simplified here):
Long baz = new Long(1);
bar.get(baz);
So, my question is, Why doesn't the compiler and catch this and report it as an error?
EDIT: I made one mistake in my initial question in that get is the method that works, not put. I have posted the full code below.
Map<WebPage, Double> scoresForPhrase = new HashMap<WebPage, Double>();
// Now that we have a list of matching docs, we can calculate the
// Score of each word in the phrase for each document
for (String term: phrase.getWords()) {
TreeSet<Posting> wordPostings = wordMap.get(term);
for(Long doc: matchingDocs) {
if (docDenomScores.get(doc) == null) {
docDenomScores.put(doc, getDocTotal(doc));
}
// The set is of postings, which are compared by docId, so
// we need a temporary one to enable searching
Posting temp = new Posting(doc, new ArrayList<Integer>());
Posting wordPosting = wordPostings.ceiling(temp);
WebPage page = (WebPage) mWebpageDb
.getPageIdToWebPageTable().get(doc);
score = getTermScore(wordPosting, page,
wordPostings.size());
score = score * queryTermWeights.get(term);
Double curScore = scoresForPhrase.get(doc);
}
}
As for the Foo class, it is:
public class WebPage implements Serializable {
private static final long serialVersionUID = -4907557806357281837L;
private String mUrl;
private int mMaxTf;
private long mPageId;
private long mLastTimeUpdated;
private List<Long> mParentIds;
private long mContentLength;
private String mTitle;
private List<Long> mChildren;
private List<String> mAllUrls;
public WebPage(String url, long pageId, long lastTimeUpdated,
List<Long> parentIds, long contentLength, String title, List<Long> children,
List<String> allUrls) {
super();
this.mUrl = url;
this.mPageId = pageId;
this.mLastTimeUpdated = lastTimeUpdated;
this.mParentIds = parentIds;
this.mContentLength = contentLength;
this.mTitle = title;
this.mChildren = children;
this.mAllUrls = allUrls;
this.mMaxTf = 0;
}
public void setUrl(String mUrl) {
this.mUrl = mUrl;
}
public void setPageId(int mPageId) {
this.mPageId = mPageId;
}
public void setLastTimeUpdated(long mLastTimeUpdated) {
this.mLastTimeUpdated = mLastTimeUpdated;
}
public void setParentIds(List<Long> mParentId) {
this.mParentIds = mParentId;
}
public void setContentLength(long mContentLength) {
this.mContentLength = mContentLength;
}
public void setChildren(List<Long> mChildren) {
this.mChildren = mChildren;
}
public void setAllUrls(List<String> allUrls) {
this.mAllUrls = allUrls;
}
public void setMaxTf(int newTf) {
this.mMaxTf = newTf;
}
public String getUrl() {
return mUrl;
}
public long getPageId() {
return mPageId;
}
public long getLastTimeUpdated() {
return mLastTimeUpdated;
}
public List<Long> getParentIds() {
return mParentIds;
}
public long getContentLength() {
return mContentLength;
}
public List<Long> getChildren() {
return mChildren;
}
public String getTitle() {
return mTitle;
}
public List<String> getAllUrls() {
return mAllUrls;
}
public int getMaxTf() {
return mMaxTf;
}
#Override
public boolean equals(Object o) {
if (!(o instanceof WebPage)) {
return false;
} else {
return ((WebPage)o).mPageId == mPageId;
}
}
#Override
public int hashCode() {
return (int)mPageId;
}
public String toString() {
return mUrl;
}
}
So two things. First, remember that due to type-erasure there is no runtime checking of generic types. The Map<Foo, Double> simply becomes Map<Object, Object>.
Second, with regards to a compiler warning or error, you should get a warning or error if bar is declared of type Map<Foo, Double>. But if it is declared as Map, no warning or error. My guess is that bar is defined as Map bar.
UPDATE
The reason there is no error on get is that by definition get takes an Object not the generic type. It is one of the odd things about the interface.
Map.get
Your Map<Foo, Double> might have been casted to Map:
Map<Foo, Double> barOriginal = new HashMap<Foo, Double();
// ...
Map bar = barOriginal;
// ...
Long baz = new Long(1);
bar.put(baz, new Double(1));

Jackson serialize only interface methods

I have one object A with some methods ma, mb, mc and this object implements an interface B with only ma and mb.
When I serialize B I expect only ma and mb as a json response but I get as well mc.
I'd like to automatize this behaviour so that all the classes I serialize get serialized based on the interface and not on the implementation.
How should I do that?
Example:
public interface Interf {
public boolean isNo();
public int getCountI();
public long getLonGuis();
}
Implementation:
public class Impl implements Interf {
private final String patata = "Patata";
private final Integer count = 231321;
private final Boolean yes = true;
private final boolean no = false;
private final int countI = 23;
private final long lonGuis = 4324523423423423432L;
public String getPatata() {
return patata;
}
public Integer getCount() {
return count;
}
public Boolean getYes() {
return yes;
}
public boolean isNo() {
return no;
}
public int getCountI() {
return countI;
}
public long getLonGuis() {
return lonGuis;
}
}
Serialization:
ObjectMapper mapper = new ObjectMapper();
Interf interf = new Impl();
String str = mapper.writeValueAsString(interf);
System.out.println(str);
Response:
{
"patata": "Patata",
"count": 231321,
"yes": true,
"no": false,
"countI": 23,
"lonGuis": 4324523423423423500
}
Expected response:
{
"no": false,
"countI": 23,
"lonGuis": 4324523423423423500
}
Just annotate your interface such that Jackson constructs data fields according to the interface's class and not the underlying object's class.
#JsonSerialize(as=Interf.class)
public interface Interf {
public boolean isNo();
public int getCountI();
public long getLonGuis();
}
You have two options:
1) put #JsonSerialize annotation on your interface (see #broc.seib answer)
2) or use specific writer for the serialization (as of Jackson 2.9.6):
ObjectMapper mapper = new ObjectMapper();
String str = mapper.writerFor(Interf.class).writeValueAsString(interf);

Categories