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.
Related
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.
I have two entities:
public class Document implements java.io.Serializable {
private Long id;
private String info;
private Set<Tag> tags = new HashSet<Tag>(0);
}
public class Tag implements java.io.Serializable {
private Long id;
private String name;
private Set<Document> documents = new HashSet<Document>(0);
}
A document may have more than one tag, and each tag can contain many items.
Now I want to do a filter function to find out all the documents that has both tag1(id = 1) and tag2(id = 2).
I tried to use these restrictions:
Criteria criteria = session.createCriteria(Document.class, "doc")
.createAlias("doc.tags", "tag");
List<Document> docList = criteria.add(Restrictions.eq("tag.id", 1))
.add((Restrictions.eq("tag.id", 2)).list();
but they're not working, the list is empty. Is there a good solution?
You are looking for a tag with id equal to 1 AND 2. It's not possible use Restrictions.in("tag.id", Arrays.asList(1, 2))
Assuming theses Entities
#Entity
public class EntityNote implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name="SeqEntityNote", sequenceName="SeqEntityNote", allocationSize = 1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SeqEntityNote")
private long id;
private Date date;
private String subject;
private String content;
#ManyToMany
private List<EntityTopic> listEntityTopic;
//setters/getters
#Entity
public class EntityTopic implements Serializable {
#Id
#SequenceGenerator(name="SeqEntityTopic", sequenceName="SeqEntityTopic", allocationSize = 1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SeqEntityTopic")
private long id;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
In my DB, a join table named "entity_note_list_entity_topic" records the ManyToMany relation.
This works correctly so far.
But I'd like to perform a count query like 'how many EntityNotes per EntitityTopic'
Unfortunatly I'm quite lost in this situation.
How this query can be written ?
Do I need other elements in my two entities ?
(In many examples I see a reverse relation using mappedBy attribute on ManyToMany.. Do I need this ?)
It will be the easiest if you make the many to many relation bidirectional. There are no serious extra costs involved, as it uses the same db structure, and the list are lazy loaded so if the relation is not being used the lists are not populated (you can hide the second direction by making accessors private).
Simply change:
#Entity
public class EntityTopic implements Serializable {
...
#ManyToMany(mappedBy="listEntityTopic")
private List<EntityNote> notes;
}
You can issue normal count jpql queries, for example:
SELECT count(n) from EntityTopic t INNER JOIN t.notes n where t.name =:name
so you don't neet to retrieve the notes and topics if don't need to.
But I also believe that your original mapping can also be queries with:
SELECT COUNT(n) FROM EntityNote n INNER JOIN n.listEntityTopic t WHERE t.name = :name
If you have the following code:
#Entity
public class EntityNote implements Serializable {
#ManyToMany(fetch = FetchType.LAZY)
private List<EntityTopic> topics;
}
#Entity
public class EntityTopic implements Serializable {
#ManyToMany(fetch = FetchType.LAZY)
private List<EntityNote> notes;
}
Then, topic.getNotes().size() will give you the number of notes associated with a topic. When using Hibernate as the JPA provider, a SELECT COUNT(...) query is issued for this instead of loading all the associated notes. If this does not work for you out-of-the-box, mark the collections as extra lazy using the instructions in this post.
Lets say we have User entity class. User can be friends with other users. How can i map this self-reference collection field without creating a new entity called Connection or creating multiple entries in the database?
#Entity
public class User {
...
#ManyToMany
private Collection<User> friends;
...
}
USER_ID-FRIEND_ID
1 - 2
2 - 1 (duplicate... I don't need it)
Following is snapshot from my code for ElementEntity:
#OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
private List<ElementEntity> children;
#JoinColumn(name = "ParentId", referencedColumnName = "ElementId")
#ManyToOne(fetch = FetchType.LAZY)
private ElementEntity parent;
Where on database there are fields:
ElementId - primary key;
ParentId relation with parent
You can't - you need both records in the database.
Actually, for friendship relations, I'd say that a graph database like neo4j is the proper thing to use. There you have the two users and simply add an edge "friends".
At least you will need a relational table.
So you have a USER table and a FRIENDS:
user_id friend_id
1 2
But #Bozho answer is way better than mine (neo4j).
Well, in fact you can.
You can use annotations like #PreUpdate, #PrePersists, #PostUpdate and so to convert manually the elements of a collection. This way your entity can render then them way you want while in database you only store a raw text.
A more pausible alternative will be to use #Convert annotation, available since jpa 2.1 (#UserType in hibernate). It tells jpa to convert the field into another type everytime it read/save in database.
For it you should use #Convert anotation, specifying and AttributeConverter object.
For example
public class Parent {
#Id
private Integer id;
#Convert(converter = FriendConverter.class)
private Set<Parent>friends;
}
And converter class like the following:
#Component
public class FriendConverter implements AttributeConverter<List, String>{
#Autowired
private SomeRepository someRepository;
#Override
public String convertToDatabaseColumn(List attribute) {
StringBuilder sb = new StringBuilder();
for (Object object : attribute) {
Parent parent = (parent) object;
sb.append(parent.getId()).append(".");
}
return sb.toString();
}
#Override
public List convertToEntityAttribute(String dbData) {
String[] split = dbData.split(".");
List<Parent>friends = new ArrayList<>();
for (String string : split) {
Parent parent = someRepository.findById(Integer.valueOf(string));
friends.add(accion);
}
return friends;
}
}
It is a dummy implementation but it gives you the idea.
As a personal comment, I do recommend to map the relationship as it should. In the future it will avoid you problems. AttributeConverter comes in handy when working with enums
I'm using java persistence to save a list of entities that are associated to another entity. Here's a quick rundown of where I'm having some problems.
#Entity public class Offer implements Serializable {
#Id private Long offerId;
#OneToMany
#Column List<OfferCategory> offerCategories;
}
#Entity public class OfferCategory implements Serializable {
#Embeddable public static class Id implements Serializable
{
#Column(name="offer_id")
private Long offerId;
#Column(name="category_id")
private Long categoryId;
public Id() {}
public Id(Long offerId, Long categoryId) {
this.offerId = offerId;
this.categoryId = categoryId;
}
public boolean equals(Object o) {
if(o != null && o instanceof Id) {
Id other = (Id) o;
return this.offerId.equals(other.offerId) &&
this.categoryId.equals(other.categoryId);
}
else
return false;
}
public int hashCode() {
return offerId.hashCode() + categoryId.hashCode();
}
}
#EmbeddedId private Id id = new Id();
}
Essentially, due to an architecture I cannot change, I need to save a list of Categories as being assigned to an Offer.
Right now, I'm getting a list of Categories from the user and then putting them into the offerCategories field of Offer. However, this doesn't work for new Offers, because there's no way for me to set the ID of a new item.
I'm new to JPA and Seam, so if someone could give me a nudge in the right direction it would be greatly appreciated.
I have not tried using a composite ID before, but one thing to note is that #Column is only used to change the properties of the database column the field is using. It doesn't stipulate a relation, so you still need something like this:
#OneToMany
List<OfferCategory> offerCategories;
As I looked into tutorial I found this:
You cannot use an IdentifierGenerator to generate composite keys. Instead the application must assign its own identifiers.
So you have to assign the id by yourself. Maybe you can make a DB sequence and fetch its values with native query?
And one remark - if you want to use List mapping (order of Categories in Offer is defined by database), you need an index column to contain the index in list. If the order of categories is not important, Set would be more convenient.
Well, my solution for now is that I persist each one (creating keys for new entries), then stuff them into a list, then stuff them into their container object, then persist that object.