In my (java) Controller in a Play2 project I'm saving some data to an object.
So entity here is an instance of a Model subclass.
I do stuff like this
log.debug("Saving title=" + title + ", tags=" + tags);
entity.title = title;
entity.tags = tags;
entity.save();
// verify:
ModelClass m = ModelClass.find.byId(entity.id);
log.debug("Saved title=" + m.title + ", tags=" + m.tags);
Where title is a String and tags is a List<String>. The debug log says
Saving title=foo, tags=[bar, quux]
Saved title=foo, tags=null
So data is coming in, I'm not getting any warnings, but the list of strings is just lost somewhere along the way. I'm just using an in-memory h2 db, maybe it works when I'm really persisting it, but... what's up with this?
Edit: The generated SQL create syntax doesn't contain "tags" at all. So there's obviously something wrong with that.
Edit: see How to persist a property of type List<String> in JPA?
In JPA you must declare a List as #ElementCollection for it to be persisted. It seems that EBean do not support this feature.
One way to do it should be to declare your List tags as #Transient (ie. not persisted) and have methods to manage it while keeping up to date a simple String that contains your tags comma separated. That would be this String that gets persisted in a single column.
Related
I have approximately the following entity:
public class Article {
private String name;
private Long fileId;
}
As you can see, it has a field fileld that contains the id of the associated file, which is also an entity. However, the file does not know anything about the Article, so the only thing that connects them is the fileId field in the Article. Therefore, they must be explicitly linked so as not to get lost. Now to get a linked file, I have to make a separate query to the database for each Article. That is, if I want to get a list of 10 Articles, I need to make a request to the database 10 times and get the file by its id. This looks very inefficient. How can this be done better? I use jooq, so I can't use JPA, so I can't substitute a file object instead of the fileId field. Any ideas?
I'm going to make an assumption that your underlying tables are something like this:
create table file (
id bigint primary key
content blob
);
create table article (
name text,
file_id bigint references file
);
In case of which you can fetch all 10 files into memory using a single query like this:
Result<?> result =
ctx.select()
.from(ARTICLE)
.join(FILE).on(ARTICLE.FILE_ID.eq(FILE.ID))
.fetch();
As the tittle says I want to create some transactions to make queries. In my case I have Wallet and Customer classes and each of them has a type attribute for knowing what type they are. I want, for example to make a query to get all the wallets so I guess I would have to create a query like TYPE=WALLET. The code I use to query is the following.
String queryStr = "{\"selector\": {\"type\": \"wallet\"}}";
QueryResultsIterator<KeyValue> rows = stub.getQueryResult(queryStr);
The things that I don’t know who to structure the string for querying, of if there is a better way of doing this type of queries. Thanks for your help.
Here's the deal:
public static List<Survey> getFilteredSurveys(Municipality municipality, Company company) {
String sql = "SELECT DISTINCT id FROM survey INNER JOIN " +
"(SELECT SURVEY_ID FROM publicity INNER JOIN brand "+
"ON publicity.brand_id=brand.id WHERE brand.company_id="+company.getId()+") "+
"ON survey_id=survey.id WHERE survey.municipality_id="+municipality.getId();
RawSql rawSql = RawSqlBuilder.parse(sql).create();
List<Survey> surveys = Ebean.find(Survey.class).setRawSql(rawSql).findList();
for (Survey survey : surveys) {
List<Publicity> publicities = new ArrayList<>();
for (Publicity publicity : survey.publicities) {
if(publicity.getBrand().getCompany() == company){
publicities.add(publicity);
}
}
survey.setPublicities(publicities);
}
return surveys;
}
This app is meant for measuring Publicities in a given place,
So people upload a 'Survey' of a place containing all the 'Publicity' that place has.
That function is supposed to return a List,
Each Survey has a List,
And each Publicity has a Brand, which is associated to a Company (ex. Coke -> Coca Cola Co.)
What I'm trying to do is this:
Given a Company, show all the surveys that contain a 'Coca Cola Co.' publicity, but showing only the publicities that belong to 'Coca Cola Co.'
I have a 'Surveys' controller which receives a form with a Municipality and a Company, calls this method, and it renders a view with its result.
This is part of the view template:
#(surveys: java.util.List[Survey])
#for(survey <- surveys){
#for(publicity <- survey.getPublicities){
<tr>
<td>#publicity.getBrand.getName</td>
<td>#publicity.getType.getName</td>
<td>#publicity.getSquareMeters</td>
</tr>
}
}
Problem: even though I removed some publicities from each Survey, all the publicities show up in the view. Why is this happening?
I know I'm not persisting the changes, and I don't want to, I just want to temporarily obfuscate the data so the user only sees the publicities that belong to a given company.
Why isn't this view using the surveys as they are given to it, modified?
Actually I'll put this in an answer ...
You should look at the SQL executed in the log (because I suspect you are getting N+1) here and you could fairly easily avoid that.
You should probably look to change your raw sql to include the publicities columns in the select clause (name, type, squareMeters) to avoid the extra queries.
Alternatively you could add fetch("publicities") to the query (so that they are fetched eagerly via a query join 100 at a time).
Also refer to:
https://github.com/ebean-orm/avaje-ebeanorm/issues/223
... RawSql that includes a OneToMany not working
https://github.com/ebean-orm/avaje-ebeanorm/issues/224
... Enhancement adding RawSqlBuilder.tableAliasMapping()
Ideally you'd be able to use 4.5.2 and take advantage of that fix and that enhancement.
So, I found a fix,
My fix was:
for (Survey survey : surveys) {
survey.getAddress(); //This line fixes the issue
List<Publicity> publicities = new ArrayList<>();
for (Publicity publicity : survey.publicities) {
if(publicity.getBrand().getCompany() != null){
if(publicity.getBrand().getCompany().getId().equals(company.getId())){
publicities.add(publicity);
}
}
}
survey.setPublicities(publicities);
}
My guess is that the problem resides in the way ebean lazily instantiates objects, despite setting Publicities to FetchType.EAGER, and the fact that the output from this function was the expected one, also inspecting surveys in the controller seemed to be ok, and also a #println(surveys) in the view showed only the publicities corresponding to the company I had selected.
I have an application in which I want to overwrite an individual entity. This is how I originally create entity logs:
Entity log = new Entity("Log", "Logkey");
String property1 = req.getParameter("property1");
String property2 = req.getParameter("property2");
log.setProperty("property1", property1);
log.setProperty("property2", property2);
datastore.put(log);
Here is how entity logs are retrieved to be overwritten:
Query query = new Query("Log", "Logkey")
.setFilter(timeStampFilter);
List<Entity> logs = datastore.prepare(query).asList(FetchOptions.Builder.withLimit(1));
request.setAttribute("logs", logs);
and sent to a jsp form page as value="${log.properties.property1}" where they should be overwritten. This entry is then sent to a second servlet with the POST method and requested as parameters as in the earlier code but saved as a new entity with the same kind:
Entity edit_log = new Entity("Log", "Logkey");
String property1 = req.getParameter("property1");
String property2 = req.getParameter("property2");
edit_log.setProperty("property1", property1);
For rewriting and existing entity, after retreiving a specific log by timestamp, you can get the key of this log using getKey() method and then create an entity with this key and the new details. Now when you put this new entity to the datastore it will replace the earlier one with the same key
With the code you've written, you only have a single Log entity in your datastore with the key "Logkey" that you are constantly overwriting.
If you're using some other code to retrieve entities and rewrite them, then you'll need to show that other code. Otherwise, this question is poorly written, because the code given is already describing what you want to do (always overwrite the same entity).
If you have code elsewhere creating/saving entities, it's best to show that too.
Edit: It looks like you end up creating a nested entity with the data from the old entity in a new entity with the same key. It's far easier just to reuse the entity you received from the query.
log = logs.get(0)
log.setProperty("property1", req.getParameter("property1");
log.setProperty("property2", req.getParameter("property2");
datastore.put(log);
Furthermore, since you actually know the key ("Logkey"), you don't need to issue a datastore query, you can just fetch the entity by key - which is good because you get around eventual-consistency behavior.
If your new entity has the same key as your original one, then when you store it it will override the old entity.
I have a result of a db query in java.sql.ResultSet that needs to be converted to hierarchical data structure. It looks a bit like so:
name|version|pname|code|count
n1|1.1|p1|c1|3
n1|1.1|p1|c2|2
n1|1.1|p2|c1|1
n1|1.2|p1|c1|0
n2|1.0|p1|c1|5
I need that converted into a hierarchical data structure:
N1
+ 1.1
+ p1
+ c1(3)
+ c2(2)
+ p2
+ c1(1)
+ 1.2
+ p1
+ c1(0)
N2
+ 1.0
+ p1
+ c1(5)
So my data structure can look something like this
Name {
String name
List<Version> versions
}
Version {
String version
List<PName> pnames
}
PName {
String pName
List<CodeCount> codeCounts
}
CodeCount {
String code
Integer count
}
Anyone have suggestions/code snippets on the best way to do this?
There are a few ways, and how you do it depends on how robust your solution needs to be.
One would be to just write a couple of objects that had the attributes in the database. Then you could get the result set, and iterate over it, creating a new object each time the key field (for example, "name") changed, and adding it to a list of that object. Then you'd set the attributes appropriately. That is the "quick and dirty" solution.
A slightly more robust way would be to use something like Hibernate to do the mapping.
If you do decide to do that, I would also suggest redoing your tables so that they accurately reflect your object structure. It may not be needed if you just want a fast solution. But if you are seeking a robust solution for commercial or enterprise software, it's probably a good idea.