MyBatis mapping properties to database columns when inserting & updating (using annotations) - annotations

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.

Related

Is it possible to use SQL syntax like FROM DUAL in JPA?

I am trying to create an independent project regardless of the type of DB using JPQL's #Query .
How do I implement the syntax below in JPA?
SELECT MY_SEQ.NEXTVAL FROM DUAL
Short answer:
For this kind of query, it's more productive to just use nativeQuery = true and use it normally.
To make it database agnostic as you want, you could theoretically just create a DUAL table in the new database.
Long answer:
The short answer is what you are actually looking for. But for the sake of other people who might fall here through Google for other related reasons though, if you really want it, you could theoretically transform Oracle's DUAL table into an entity by using #Subselect like so:
#Table
#Entity
#Subselect("SELECT dummy FROM DUAL")
public class DualEntity {
#Id
#Column(name = "dummy")
private String dummy;
}
Where dummy is the sole column in the DUAL table.
Although this doesn't seem very useful at first glance because most of this kind of queries are much easier to run as native queries, this does let us create repository interfaces for database functions when they aren't related to any actual tables in the database. Example:
#Repository
public interface DualRepository extends JpaRepository<DualEntity, String> {
#Query(nativeQuery = true, value = "SELECT MY_OWNER.MY_FUNCTION(:myParameter) FROM DUAL")
String myFunction(Long myParameter);
}
Which again, this function should probably be part of your code and not part of the database, but older applications tend to have this kind of stuff around. Procedures could also be called through #Query and could also benefit from this kind of mapping.

Is there a possibility to check if multiple columns of an entity contain one search key in JPA

Is there a possibility to check if multiple or every column of an entity contains one key.
So I have multiple places which contain multiple addresses.
An address does contain the id of its place, a name and an area. I want to filter out the addresses which contain a searchTerm s1 in either its name or area.
I did already try this, but sadly it didnt work for me.
fun findByPlaceIdAndNameIgnoreCaseOrAreaIgnoreCaseContaining(placeId: UUID, searchTerm: String)
Sure this is possible. Just but the appropriate query in a #Query annotation.
I don't know Kotlin so the syntax is guess work on my side but I'm sure you figure it out based on this:
#Query("SELECT p FROM Place p WHERE p.placeId = :placeID AND (UCASE(p.name) = UCASE(:searchTerm) OR UCASE(p.name) = UCASE(:searchTerm))")
fun search(placeId: UUID, searchTerm: String)

Eloquent Friendly Column Name

We're currently transitioning from one database to another. A table in our legacy database has column names that are less than ideal, for example:
some_crazy_name__xyz
In our new database, we'd like to have a column name like:
someCrazyName
In the short term, we have to work with data from our legacy database. At some point in the near future, we'd like to switch over without having to refactor all of our Eloquent code to query for different column names. For example:
$model = MyModel::where('someCrazyName', '=', 1);
I'm currently extending the Model class, where all implementing models provide a map of terrible names to friendly names:
class MyModel extends BaseModel {
$columnMap = array(
'someCrazyName' => 'some_crazy_name__xyz'
);
}
This works well where I can use __get and __set in BaseModel to lookup properties in my map, for example:
$myModel = new MyModel;
// ...
echo $myModel->someCrazyName;
However, this obviously doesn't work well with queries without having to always use my map to look up column names. I'm wondering if it's possible without having to override all of the methods within Illuminate\Database\Eloquent\Model, Illuminate\Database\Query\Builder and Illuminate\Database\Eloquent\Builder that deal with columns, where the underlying query that is built always maps to the correct column? Then after we transition databases, we can remove that one piece of code rather then remove potentially thousands of column name mappings.
This is what you need: https://github.com/jarektkaczyk/eloquence/wiki/Mappable
It's not only for mapping badly_named_columns to something_useful, but also can be used for relational mappings:
// simple aliasing
User::where('cool_name', 'value') // where badName = ?
// relations, eg. User hasOne Profile
User::where('first_name', 'Jon') // search through related profiles table
// and obviously mutators:
$user->first_name == $user->profile->first_name
$user->cool_name = 'Jon' // becomes $user->badName = 'value'
$user->cool_name; // 'Jon'
One way to do it would be with accessors.
For example, in MyModel you could define an accessor for the some_crazy_name__xyz column like this:
public function getSomeCrazyNameAttribute()
{
return $this->attributes['some_crazy_name__xyz'];
}
You can then transparently refer to that column with $mymodel->someCrazyName. You can also define a mutator to set the value.
Admittedly, this may not be the best solution if you have MANY values like this. But it does have one important benefit: later on, if you refactor your database so that the column some_crazy_name__xyz is actually called someCrazyName, all you need to do is remove that function from your model. And, to my mind at least, it's simpler than trying to override a bunch of methods on the various classes involved.
And unfortunately, it doesn't adequately address the use of column names in queries. For that, you might want to look at the repository pattern. But in any event, it looks like there's going to be a lot of coding involved.
Finally, you haven't mentioned what database you're using. If it's MySQL, it is possible to create updatable and insertable views. Using a view, you could simply map old column names to new, and point your Eloquent model at the view instead of a table. Other database servers may provide similar functionality.

Attempting to use EF/Linq to Entities for dynamic querying and CRUD operations

(as advised re-posting this question here... originally posted in msdn forum)
I am striving to write a "generic" routine for some simple CRUD operations using EF/Linq to Entities. I'm working in ASP.NET (C# or VB).
I have looked at:
Getting a reference to a dynamically selected table with "GetObjectByKey" (But I don't want anything from cache. I want data from database. Seems like not what this function is intended for).
CRM Dynamic Entities (here you can pass a tablename string to query) looked like the approach I am looking for but I don't get the idea that this CRM effort is necessarily staying current (?) and/or has much assurance for the future??
I looked at various ways of drilling thru Namespaces/Objects to get to where I could pass a TableName parameter into the oft used query syntax var query = (from c in context.C_Contacts select c); (for example) where somehow I could swap out the "C_Contacts" TEntity depending on which table I want to work with. But not finding a way to do this ??
Slightly over-simplyfing, I just want to be able to pass a tablename parameter and in some cases some associated fieldnames and values (perhaps in a generic object?) to my routine and then let that routine dynamically plug into LINQ to Entity data context/model and do some standard "select all" operations for parameter table or do a delete to parameter table based on a generic record id. I'm trying to avoid calling the various different automatically generated L2E methods based on tablename etc...instead just trying to drill into the data context and ultimately the L2E query syntax for dynamically passed table/field names.
Has anyone found any successful/efficient approaches for doing this? Any ideas, links, examples?
The DbContext object has a generic Set() method. This will give you
from c in context.Set<Contact>() select c
Here's method when starting from a string:
public void Test()
{
dynamic entity = null;
Type type = Type.GetType("Contract");
entity = Activator.CreateInstance(type);
ProcessType(entity);
}
public void ProcessType<TEntity>(TEntity instance)
where TEntity : class
{
var result =
from item in this.Set<TEntity>()
select item;
//do stuff with the result
//passing back to the caller can get more complicated
//but passing it on will be fine ...
}

Envers generating "add"+"delete" edits instead of "modify" for Map<>

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.