I have a field declared as a Map<MyEnum, String>, which is audited. When a change is made to one of the elements in the map, envers is generated two edits, an ADD and a DEL, rather that a single MOD, which in turn means a constraint violation when trying to insert into the audit table, since there are two edits for a single field in a single entity in the same revision.
I'm guessing I could probably work around the problem by making revision_type part of the table's key (which would permit one edit of each type per field per entity), but that seems like an ugly hack, besides the fact that I lose track of which happened first, and the fact that it's just wrong.
The field is being persisted properly, by the way... it's just the audit records that fail.
The field's declaration:
#ElementCollection
#CollectionTable(name = "configuration_property", joinColumns = #JoinColumn(name = "configuration_id"))
#MapKeyColumn(name = "property_name")
#Column(name = "property_value", columnDefinition = "longtext")
#MapKeyEnumerated(EnumType.STRING)
private Map<ConfigurationProperty, String> properties = new EnumMap<ConfigurationProperty, String>(ConfigurationProperty.class);
I'm using Hibernate v3.5.6.
I haven't been able to find any bug reports regarding this, so I'm thinking I'm doing something wrong... any ideas?
If you look at AbstractCollectionMapper.mapCollectionChanges, the current implementation only does additions and removals. I think the map is viewed as a collection of tuples: (key, value), so there are never any modifications. The bug is then in the schema generation, which should generate the keys properly. Please file a JIRA bug - https://hibernate.onjira.com, Envers component.
Related
As far I can understand from reading this part of the documentation of
https://docs.jboss.org/hibernate/search/6.0/reference/en-US/html_single/#mapper-orm-reindexing-basics
there's no automatic reindexing on #IndexedEmbedded fields that doesn't have a bidirectional mapping. Am I correct? And if so I'm curious to know what leaded to introduce this, because in Hibernate search automatic reindexing happened when updating a field in an #IndexedEmbedded field. Does this mean that now I'm responsible to update the index?
Here's an example of my use case which leads to a not updated index:
#Indexed(index = "foo_index")
#Entity
public class Foo {
private Long id;
#IndexedEmbedded
#ManyToOne(fetch = LAZY)
private Bar bar;
}
#Entity
public class Bar {
private Long id;
#GenericField
private String barFieldOne;
#GenericField
private String barFieldTwo;
}
Then let's say I retrieve the Foo from the db and change a bar field like this:
Foo foo = repository.findById(1);
foo.getBar().setBarFieldOne("newValue");
repository.save(foo);
This will not trigger index update of the foo index despite I'm working through the #Indexed object(Foo in our case). I have a lot of uni directional relations and I don't want to make them bidirectional because I don't need them and they can lead to performance problems. I understand that if I update the bar entity by itself it won't update the index but here I'm updating it through the main #Indexed entity and I expect the index to be updated.
This use case worked flawlessly in hibernate search 5 and in my honest opinion this is an important. Is there a way to make it work here, because this will make my life a lot easier.
You understood well, Hibernate Search cannot trigger reindexing when there's just an unidirectional association between the modified entity and the indexed entity.
There are plans to address that, maybe, one day, but that will still require some configuration: https://hibernate.atlassian.net/browse/HSEARCH-1937
This use case worked flawlessly in hibernate search 5 and in my honest opinion this is an important
I'm going to need a reproducer for that. I would be very, very surprised if you managed to make it work. If it worked, it was probably just a side-effect of something else: you disabled dirty checking, or you had a transient property on your entity that caused it to be reindexed every single time.
All we did in Search 6 was to make sure we throw an error when you try to use #IndexedEmbedded on an uni-directional association, and force you to explicitly disable automatic reindexing for that association.
It didn't work in Hibernate Search 5 either, but Hibernate Search 5 would ignore these problems silently and you would end up thinking it worked, but it did not.
So really, the only change is that you are now aware of the problem. It existed before.
Hi I have one table VariantValidityBE
It has a relationship column like this
#OneToMany(mappedBy = "variantValidityBE", fetch = FetchType.LAZY)
private List<VariantValidityValueBE> variantValidityBEList;
And in another table
#ManyToOne
#JoinColumn(name = "CATEGORY_ID", referencedColumnName = "ID")
private VariantValidityBE variantValidityBE;
And my method is like this
List<VariantValidityBE> resultList = getResultList(VariantValidityBE.FIND_ALL);
for (VariantValidityBE variantValidityBE : resultList) {
List<VariantValidityValueBE> options = variantValidityBE.getVariantValidityBEList();
}
the value of option is coming old value, newly inserted child record is not coming
Values are inserted into DB correctly.
But if I restart the application its giving the updated records.
The same type of relations I used so many times, never get such type problem.
Since JPA entities are treated as regular java objects, you are required to keep both sides of bidirectional relationships in synch with each other when making changes. JPA will not perform magic to mirror changes made to one side of a bidirectional relationship to the other for you. So when you add a new VariantValidityValueBE instance and set its variantValidityBE, you must also add the VariantValidityValueBE to the variantValidityBEList. Otherwise, the variantValidityBEList will remain unchanged and stale until it is refreshed from the database.
After struggling for days attempting to get back collections that are linked to a table via a foreign key, I just realized that the tables I am linking to are actually LINKING tables to other tables with the actual data (chock one up for normalized tables).
I am still struggling to get collections out of ManyToOne annotated variables with references to foreign keys, but is there a way I can pull the data back from the table that actually contains the information? Has anyone run into an instance of this?
UPDATE: AS per request I will be posting some code instances... This would be my named query in the entity that I will be calling...
#NamedQuery(name="getQuickLaunchWithCollections", query = "SELECT q FROM QuickLaunch q LEFT JOIN FETCH q.quickLaunchDistlistCollection LEFT JOIN FETCH q.quickLaunchPermCollection LEFT JOIN FETCH q.quickLaunchProviderCollection")})
These would be the collections that I am looking to fill...
#OneToMany(mappedBy="quickLaunchId", fetch=FetchType.EAGER)
private List<QuickLaunchPerm> quickLaunchPermCollection;
#OneToMany(mappedBy="quickLaunchId", fetch=FetchType.EAGER)
private List<QuickLaunchProvider> quickLaunchProviderCollection;
#OneToMany(mappedBy="quickLaunchId", fetch=FetchType.EAGER)
private List<QuickLaunchDistlist> quickLaunchDistlistCollection;
As you can see, I have the fetch type set to eager. So technically, I should be getting some data back? But in actuality those are just linking tables the data that I actually want to pull back. I will need to figure out how to get that data back eventually.
This is how I am calling that named query...
listQL = emf.createNamedQuery("getQuickLaunchWithCollections").getResultList();
Alright, it appears as though LEFT JOIN FETCH
is causing my runtime to throw an expception of some kind. It is pretty unclear as to what it is. But I feel as though I am getting no where with that technique. I am going to try something slightly different.
I would suggest simplifying example, to face the problem, since you are going worldwide now.
Specifying mappedBy="quickLaunchId" attribute, you are saying, that QuickLaunchPerm entity has QuickLaunch as its property named "quickLaunchId". Is this true?
If it is not, then you need to define it in QuickLaunchPerm:
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "QUICK_LAUNCH_ID")
private QuickLaunch quickLaunchId;
//getters setters
Sure this is a simple answer, but I cannot find the right source to give the details.
I have a ManyToOne relationship. Because of a synchronization system, when a child is removed a field named 'removed' is set to 'true', and will automatically be deleted only a month later.
However, in the meanwhile, I would like it not to appear in the List in the parent. Is there an easy way to specify a select statement in the definition of the field or so?
#OneToMany(mappedBy = "parent")
#OrderBy("level")
public List<MenuItem> children;
As you are using hibernate you can use the #where annotation. I never used it myself but it seems quite straight forward. Have a look here: http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#entity-hibspec-collection
I'm just starting to learn MyBatis and I'm wondering, when I'm creating insert or update queries, is there a way that I can make property names a bit more friendly to refactoring? I'll explain in more detail:
I have constants in all of my domain classes that can be used to reference property names. In my opinion, it cuts down on typos and makes refactoring a bit easier.
public static final String FIRST_NAME = "firstName";
private String firstName = "";
When I create a MyBatis select statement using annotations, I can do something like this:
#Select("SELECT ID, FIRST_NAME, LAST_NAME FROM CUSTOMERS WHERE ID = #{id}")
#Results({
#Result(property = CustomerDetail.ID, column = "ID"),
#Result(property = CustomerDetail.FIRST_NAME, column = "FIRST_NAME"),
#Result(property = CustomerDetail.LAST_NAME, column = "LAST_NAME")
})
CustomerDetail selectById(final int id);
If I refactor my domain object (CustomerDetail) and change property names, it ends up being fairly simple.
However, when I create a MyBatis insert statement using annotations, I have to do something like this:
#Insert("INSERT INTO CUSTOMERS (ID, FIRST_NAME, LAST_NAME) VALUES (#{id}, #{firstName}, #{lastName})")
void insertCustomerDetail(final CustomerDetail customerDetail);
In this case, if I refactor my domain object (CustomerDetail) and change property names, it's much more error prone. Is there a way I can use my constants without resorting to a bunch of string concatenation? Is it something I should even concern myself with?
As a total newbie, I was expecting the #Insert and #Update annotations to mimic the structure of the #Select annotation. For example (please note, the below are NOT valid MyBatis annotations):
#Insert("INSERT INTO CUSTOMERS (ID, FIRST_NAME, LAST_NAME)")
#Params({
#Param(property = CustomerDetail.ID, column = "ID"),
#Param(property = CustomerDetail.FIRST_NAME, column = "FIRST_NAME"),
#Param(property = CustomerDetail.LAST_NAME, column = "LAST_NAME")
})
void insertCustomerDetail(final CustomerDetail customerDetail);
Have I missed any options that would have the same effect as my above sample? Alternatively, is it possible for me to unit test MyBatis mappings to ensure no one is using property names that don't exist in my domain objects? Testing may be a better solution since it would also apply to XML based mappings. Thoughts?
Is it something I should even concern
myself with?
I don't think so. I understand your concern, and I see how such a feature could be beneficial to you, especially early in development when POJO's tend to change so often.
I don't think your objects fields will be refactored enough to justify the investment needed to provide this functionality.
I suggest you read about the #Param tag in the mybatis user guide. It's not exactly what your looking for here, but it is a way to decouple object field names to mybatis sql map variables. In my mind, I would take your approach over passing in individual feilds with a #Param.
In regards to unit testing your sql maps, i'm fairly certain that if you use an ognl expression that doesn't have a corresponding get method in the object you'll get an exceptions. i.e if you use #{someField}, and the object your passing in doesn't have a getSomeField() method, then you get exception.