I got next database structure with OneToOne relation:
[company]
company_id (PK)
company_name
[company_configuration]
company_configuration_id (Autoincrement, PK)
company_id (UNIQUE KEY,FK)
company_configuration_v
I have been using ORMlite and I have next classes for this two tables:
#DatabaseTable(tableName = "company")
public class Company {
public static final String ID_COMPANY = "company_id";
public static final String COMPANY_NAME = "company_name";
#DatabaseField(generatedId = true, columnName = ID_COMPANY)
private int idCompany;
#DatabaseField(columnName = COMPANY_NAME)
private String companyName;
#DatabaseTable(tableName = "company_configuration")
public class CompanyConfiguration {
public static final String COMPANY_CONFIGURATION_ID = "company_configuration_id";
public static final String COMPANY_ID = "company_id";
public static final String COMPANY_CONFIGURATION_V = "company_configuration_v";
#DatabaseField(generatedId = true, columnName = COMPANY_CONFIGURATION_ID)
private int idCompanyConfiguration;
#DatabaseField(foreign = true,foreignAutoRefresh = true, columnName = COMPANY_ID)
private Company companyId;
#DatabaseField(columnName = COMPANY_CONFIGURATION_V)
private String companyConfigurationV;
Here is OneToOne relation because I want to divide a table with many columns.
As you can see in the example above, there is not relation from Company class to CompanyConfiguration class.
I know that I can add this snippet of code(examle below) into Company class, but I don't need a #ForeignCollectionField becaues the collection will contain only one CompanyConfiguration object:
#ForeignCollectionField()
private ForeignCollection<CompanyConfiguration> companyConfigurations;
I need to add something like this (examle below) into Company class and will get the reference from Company class to CompanyConfiguration class:
#OneToOne(targetEntity = CompanyDbConfig.class)
#JoinTable(name = "company_configuration")
#JoinColumn(name = "id_company")
CompanyConfiguration companyConfiguration;
Shortly, I want to get Company object using ORMlite. See the example below. After fetching company from the database, I want to have and CompanyConfiguration object within company object.
Company company = daoCompany.queryForId(id); //daoCompany is an instance of ORMlite Dao class
Is it possible and how to do that using ORMlite?
I posted an OrmLite question myself so I looked through the unanswered questions to see if there was anything I could answer. Even though this is an old topic, I wanted to take a stab at it in case it could help someone.
I've read your post a few times and I think you're asking how to load the information from two tables into one model. You're separating a rather large table into two in the database but you want it to come back as one model. If that is correct, here's my take on the code. This assumes you want to use objects to build the query instead of passing in a query string.
public class CompanyResult
{
public long CompanyId { get; set; }
public long ConfigurationId { get; set; }
public string Name { get; set; }
public string ConfigurationV { get; set; }
}
var query = _db.From<Company>
.Join<CompanyConfiguration>((c, cc) => c.idCompany == cc.idCompany)
.Where(c => c.idCompany == companyId)
.Select<CompanyConfiguration>((c, cc) = new {
CompanyId = c.idCompany,
ConfigurationId = cc.idCompanyConfiguration,
Name = c.companyName,
ConfigurationV - cc.companyConfigurationV
});
var results = _db.Single<CompanyResult>(query);
You'd keep your existing models so they could be used as DTOs. You'd just be using the new model model above to pass back the exact properties you want.
*I wrote this in Notepad++, forgive any typos.
Related
Having the weirdest issue here, all is working fine, except that my 1-to-M query is duplicating the data.
Customer table
#Entity(tableName = "customer_table")
public class Customer {
#ColumnInfo(name = "Customer_Serial", index = true)
#PrimaryKey
private int customerSerial;
#ColumnInfo(name = "Customer_Name")
private String customerName;
public Customer(int customerSerial, String customerName) {
this.customerSerial = customerSerial;
this.customerName = customerName;
}
}
Invoice table
#Entity(tableName = "invoice_table")
public class Invoice {
#ColumnInfo(name = "Invoice_Number", index = true)
#PrimaryKey
private int invoiceNumber;
#ColumnInfo(name = "Customer_Serial")
private int customerSerial;
public Invoice(int invoiceNumber, int customerSerial) {
this.invoiceNumber = invoiceNumber;
this.customerSerial = customerSerial;
}
}
CustomerInvoice relation
public class CustomerInvoice {
#Embedded public Customer customer;
#Relation(
parentColumn = "Customer_Serial",
entityColumn = "Customer_Serial"
)
public List<Invoice> invoices;
}
DAO
#Transaction
#Query("SELECT * FROM customer_table INNER JOIN invoice_table ON invoice_table.Customer_Serial = customer_table.Customer_Serial")
List<CustomerInvoice> getAllCustInvoices();
#Insert
void insertInvoice(Invoice... invoice);
#Insert
void insertCustomer(Customer... customer);
If I debug my application, set a breakpoint to test the Room stuff, then use the 'Evaluate' feature in Android Studio, I do the following
Invoice invoice1 = new Invoice(1234, 1);
Invoice invoice2 = new Invoice(2468, 1);
Customer customer = new Customer(1, "Test Customer");
dao.insertCustomer(customer);
dao.insertInvoice(invoice1);
dao.insertInvoice(invoice2);
If I then retrieve the information using getAllCustInvoices()
The list returned has 2 in it.
It has the customer duplicated for each invoice assigned to them, and then both invoices listed in each 1.
I'm not entirely sure where I am going wrong here, this is a simplified example of what the app itself is actually doing, simplified enough to see if something else in my code was causing the problem or not.
Turns out, even with the simplified example, it has the issue.
The issue
When #Relation is used Room extracts the children (ALL children) per parent (effectively running a second query to extract the children, hence the recommendation for using #Transaction ). By specifying the JOIN you are extracting the same parent for each child (i.e. the cartesian product) and hence the duplication.
i.e. Room does the equivalent of the JOIN internally
The Fix
#Transaction
#Query("SELECT * FROM customer_table")
List<CustomerInvoice> getAllCustInvoices();
My initial requirement was to fetch Car details as:
List<String> carMake = getUserCarMake();
Page<Car> carDetails = carRepository.findAllCarsInfo(carMake, pageRequest);
CarRepository.java
#Query(value="SELECT A FROM Cars A WHERE A.model = ?1
public Page<Cars> findAllCarsInfo(List<String> carMake, Pageable pageRequest);
Now my requirement has changed to fetch car details based car models for each car make. So I have changed the code as shown
for (Cars car : userCars) {
String carMake = car.getCarMake();
List<String> carModelForMake = new ArrayList<>();
List <CarModels> carModelList = car.getCarModels();
for (CarModels carModel : carModelList) {
carModelForMake.add(carModel.getModelName());
Page<Car> carDetails = carRepository.findAllCarsInfo(carModelForMake, carMake, pageRequest)
}
}
CarRepository.java
#Query(value="SELECT A FROM Cars A WHERE A.model IN ?1 AND A.make = ?2”
public Page<Car> findAllCarsInfo(List<String> carModel, String carMake,Pageable pageRequest);
So for each car i have a carMake and corresponding carModels for that make which i then pass to the query to fetch carDetails which is a Page Object. As a result same query is called multiple times for different carMake.
The problem is how do I manage the Page object here. In the above scenario the Page object will contain only the details of last car from the carModelList, rest will be overwritten as I do not
have an option of carDetails.addAll() as in case of List.
Note: I cannot use the below query as the model can overlap across different makes.
SELECT A FROM Cars A WHERE A.model IN ?1 AND A.make IN ?2
Also my pageRequest has size as (0, 20 )
I have tried to modify the query to remove pageRequest and use findAll to fetch the results in List and then convert them to PageObject but that breaks the pagination because the page.getContent() has the entire result set and not just 20 records.
Page<Car> carDetails = new PageImpl <>(carDetailsList, pageRequest, carDetailsList.size());
How can I effectively get Page object or merge different page objects here so that my pagination works as it did in my previous requirement.
Sometimes it is a good idea to create a special "query entity" class that includes everything that is needed to respond to a certain kind of client request.
The general idea is like this:
Let's say you'd have two classes in you domain:
#Entity
#Table(name = "table_a")
public class A {
#Id int id;
String propertyA;
int bId;
}
#Entity
public class B {
#Id int id;
String propertyB;
}
And then you'd combine the two two the mentioned "query entity" (outside of the domain).
#Entity
#Table(name = "table_a")
public class QueryEntity {
private #Id private int aId;
private String propertyA;
private B b;
public String propertyA() {
return propertyA;
}
public String propertyB() {
return b.propertyB;
}
}
I'm not quite sure whether this approach is applicable in your case, but hopefully it makes the idea clear.
Firstly, I don't know if this is possible, as i think the solution would combine two existing JPA features into one: Element Collections and Discriminator Columns.
As stated below, i know how to achieve the solution using ONLY discriminators, however this would be a cumbersome implementation given the requirements stated below.
I have a Wiget pojo, that will have optional filters to reduce the content displayed. At the moment, i have two pojos that can be used as filters (there will be more) the classes below are simplified versions of the classes (they contain a lot more, but I've stripped them down to the relevant information):
Cell:
#Entity
#Table(name = "Cell")
public class Cell implements java.io.Serializable {
private Integer cellId;
private String cell;
private String description;
...
}
Task:
#Entity
#Table(name = "Task")
public class Task implements java.io.Serializable {
private Integer taskId;
private String task;
...
}
What i want to achieve is have one Table (a #CollectionTable?) that contains references to both of these pojos without the need for a "join pojo". Unless I've misunderstood, this is the point of an #ElementCollection.
The number of filter pojos to be filtered on will be expanded, and could potentially incorporate most of the pojos already in the system, i don't want to have to create multiple "join" pojos just to maintain all possible filters, when i could instead just maintain some static integers that reference the pojo type in a similar way that a discriminator column works
So in the Widget, it would look like:
Widget:
#Entity
#Table(name = "Widget")
public class Widget {
private Integer widgetId;
private String colour;
private String description;
private Date fromDate;
private Date toDate;
private Set<Cell> cellsFilter = new HashSet<Cell>();
private Set<Task> tasksFilter = new HashSet<Task>();
...
}
The filters (cellsFilter and tasksFilter) are optional (could have no filters at all).
And the Table that represents the #CollectionTable would look like:
╔═════════════════╗
║ WidgetFilters ║
╠═════════════════╣
║ id ║
║ widgetId ║ <- Should join to the Widget.widgetId column
║ pojoType ║ <- Integer, 1 = cell, 2 = task etc...
║ pojoId ║ <- Should join on the id of the cell or task (or other)
╚═════════════════╝
I've done a similar thing with #DiscriminatorColumn, however this would require me to create an abstract Filter object, CellFilter and TaskFilter (and one for every other filter eventually created).
The closest example i can find is https://en.wikibooks.org/wiki/Java_Persistence/ElementCollection however this is only linking one pojo type (Phone) with one join (the employee id), i need to link to pojoId to the correct pojo type based on the pojoType column AND join it to the Widget based on the widgetId.
FYI, I've tried things like joins with where clauses, various combinations:
#ElementCollection(targetClass=Cell.class)
#JoinTable(
name="WidgetFilter",
joinColumns=#JoinColumn(name="widgetId", referencedColumnName="widgetId"),
inverseJoinColumns=#JoinColumn(name="pojoId", referencedColumnName="cellId")
)
#Where(clause = "pojoType = 1")
public Set<Cell> getCellsFilter() {
return cellsFilter;
}
This is a difficult concept to google, given all the "similar, but not quite" answers available
If it's not possible, that's fine, at least i know how to proceed, but if anyone has achieved this already, any help would be appreciated.
My Current Solution:
The only way i can think of doing this would be to create just one Filter pojo that represents the WidgetFilter Table row, and make the Widget pojo include logic that resolves a List<Filters> into the correct types itself (without any fancy annotations) like so...
Widget:
#Entity
#Table(name = "Widget")
public class Widget {
private Integer widgetId;
private String colour;
private String description;
private Date fromDate;
private Date toDate;
private Set<Filter> filters = new HashSet<Filter>();
private Set<Cell> cellsFilter = new HashSet<Cell>();
private Set<Task> tasksFilter = new HashSet<Task>();
...
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "filter")
public List<Filter> getFilters() {
return this.filters;
}
public void setFilters(List<Filter> filters) {
this.filters = filters;
setCellFilters(filters);
setTaskFilters(filters);
}
#Transient
public List<Cell> getCellsFilter() {
return this.cellsFilter;
}
public void setCellsFilter() {
List<Filter> cellFilters = filters.stream().filter(f -> f.getPojoType().intValue() == Filter.CELL).collect(Collectors.toList());
// get List<Cell> from database from List<Filter>
}
#Transient
public List<Task> getTasksFilter() {
return this.tasksFilter;
}
public void setTasksFilter() {
List<Filter> taskFilters = filters.stream().filter(f -> f.getPojoType().intValue() == Filter.TASK).collect(Collectors.toList());
// get List<Task> from database from List<Filter>
}
...
}
Filter:
#Entity
#Table(name = "Filter")
public class Filter{
public static final int CELL = 1;
public static final int TASK = 2;
private Integer filterId;
private Widget widget;
private Integer pojoType;
private Integer pojoId;
...
}
Cheers,
Steve.
In my application, I have a several audited entity classes for example the following.
It contains multiple HAS-IS relations to other entities with various hibernate annotations.
#Entity
#Audited
public class Entity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private Integer Id;
#ManyToMany
private Set < Country> affectedCountries;
#OneToOne
private Impact impact;
#Enumerated(EnumType.STRING)
private Owner owner;
...
}
I am analyzing the the audit-trail with the following code snipplet,
which return all the attribute values from the audit table entity.
public List< AuditTrailForm> getAuditTrailEntries(Class< ?> clazz, Serializable id) {
AuditReader reader = AuditReaderFactory.get(this.getSession());
List < Number> revNumbers = reader.getRevisions(clazz, id);
List< AuditTrailForm> forms = new ArrayList();
Iterator< Number> it = revNumbers.iterator();
while(it.hasNext()) {
Number item = it.next();
Object obj = reader.find(clazz, id, item);
AuditInfo revision = reader.findRevision(AuditInfo.class, item);
BeanMap beanMap = new BeanMap(obj);
HashMap map = new HashMap();
Set keys = beanMap.keySet( );
Iterator keyIterator = keys.iterator( );
while( keyIterator.hasNext( ) ) {
String propertyName = (String) keyIterator.next( );
if (beanMap.getType(propertyName).equals(String.class)) {
String propertyValue = (String) beanMap.get( propertyName );
map.put(propertyName, propertyValue);
}
}
Date createdAt = revision.getTimestamp();
String user = revision.getUser();
AuditTrailForm form = new AuditTrailForm(user, createdAt, map);
forms.add(form);
}
return forms;
}
Works fine, however this doesn't take into account the traversing the relations in the class. Could I somehow develop a recursive algorithm, which would detect the type of the object attribute and then do the recursive call? Is there perhaps a better way to do this altogether?
A simpler approach...
I'm assuming that the saves to the object tree happen in a transaction. You could modify the audit tables to also add some unique identifier for each transaction.
That way you would be able to browse the audit history as a collection of change sets - e.g. see all changes made in a single transaction.
Not quite what you are asking for but would be a lot easier to achieve and give many of the benefits. What you are asking for would be very complicated to implement.
I've omitted some code(package declarations, imports, other fields)
for shortness.
I have here simple One-to-Many relation.
It worked fine till this moment.
#PersistenceCapable(identityType = IdentityType.APPLICATION,
detachable="true")
class Restaurant implements Serializable {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
Key id
#Persistent(mappedBy = "restaurant")
List<RestaurantAddress> addresses = new ArrayList<RestaurantAddress>()
}
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#PersistenceCapable(identityType = IdentityType.APPLICATION,
detachable="true")
class RestaurantAddress implements Serializable {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
Key id
#Persistent
Restaurant restaurant
}
Now i need to get(select) all the Restaurants from DB:
def getRestaurantsToExport(final String dst, final int count) {
String field = restaurantExportFields[dst]
return transactionExecute() { PersistenceManager pm ->
Query q = pm.newQuery(Restaurant.class)
q.filter = "$field == null"
q.setRange(0, count)
return q.execute()
}
}
But there are on problem - query gives me 12 restaurants(as in DB) but
every Restaurant has 0 Address but in Datastore every Restaurant has
minimum 2 addresses.
Have anyone the same problem or knows the solution ?
are you sure the Addresses are not lazy loaded? Just a guess... is there some way to force an "eager" loading of the objects
If someone will have the same problem:
Replace
#Persistent(mappedBy = "restaurant")
List<RestaurantAddress> addresses = new
ArrayList<RestaurantAddress>
with
#Persistent(mappedBy = "restaurant",defaultFetchGroup = "true")
List<RestaurantAddress> addresses = new
ArrayList<RestaurantAddress>
Another method is that you have to "touch" addresses property for
every Restaurant in the retrieved list before closing
PersistentManager. After PersistenManager being closed you cannot
retrieve anything from datastore and Restaurant keeps null.
Solution found with help of google-appengine-java users.