I have in DB String column with values
0101
0111
1110
I want create enum in my entity and add value.
public enum MyEnum{
NEW("0101","created new"),
SUCCESS("0111", "created success),
ERROR("1110", "created with error");
}
And I want mapped values from DB by first argument of MyEnum and get second argument in my code. Something like this:
hz.getMyEnum().value() // I need return created new or created success
I know that the question was about JPA – and the other answer states correctly, that JPA 2.1 does not support this kind of mapping... but jOOQ supports it through org.jooq.impl.EnumConverter without problems, see https://www.jooq.org/doc/3.10/manual/sql-execution/fetching/data-type-conversion/
unfortunately jpa currently (till 2.1) doesn't support mapping to enum scoped variables
You can either :
1- store the values as NEW,SUCCESS,ERROR instead of 0101,0111,1110
2- keep it as is
3- (inspired from Kovacs answer) you can create a #Converter <Enum,String> and insert the string variable name corresponding to the enum and vice versa ... the only thing to consider is that this converter will be tightly coupled to your enum
Related
I have a class with different enums, for example:
class EligibilityRule{
ProductEligibility productEligibility;
CountryEligibility countryEligibility
}
enum ProductEligibility{
PRODUCT_X,
PRODUCT_Y
}
enum CountryEligibility{
US,
IN,
CN
..
}
I want to store these different enum class name and their value to database table called eligibility rule, and my table schema looks likes this,
String id => auto_increment id
String ruleType => enum class name (ex: ProductEligibility)
String ruleValue => enum value (ex: PRODUCT_X)
I am using JOOQ, in the past I had used forced type to just store the enum value. But, in this case I want to store enum class name and enum value. I also want to reconstruct the enum object when I query the records from db.
Are there any patterns I can follow or is there any functionality in JOOQ which I can extend to solve this problem?
JOOQ supports custom data types. This means that you can define a converter for your database field which is then automatically mapped to your custom type upon loading. The database field will still be a String, but the generated Record for the field will contain a Field<EligibilityRule>. This means that you do not have to explicitly store the class name.
To do this, you must register your converter in the code generator (taken from the above docs page):
<database>
<!-- Then, associate custom types with database columns -->
<forcedTypes>
<forcedType>
<!-- Specify the Java type of your custom type. This corresponds to the Converter's <U> type. -->
<userType>java.util.GregorianCalendar</userType>
<!-- Associate that custom type with your converter. -->
<converter>com.example.CalendarConverter</converter>
<!-- Add a Java regular expression matching fully-qualified columns. Use the pipe to separate several expressions.
If provided, both "expressions" and "types" must match. -->
<expression>.*\.DATE_OF_.*</expression>
<!-- Add a Java regular expression matching data types to be forced to
have this type.
Data types may be reported by your database as:
- NUMBER regexp suggestion: NUMBER
- NUMBER(5) regexp suggestion: NUMBER\(5\)
- NUMBER(5, 2) regexp suggestion: NUMBER\(5,\s*2\)
- any other form
It is thus recommended to use defensive regexes for types.
If provided, both "expressions" and "types" must match. -->
<types>.*</types>
</forcedType>
</forcedTypes>
</database>
See also the custom data type binding that JOOQ supports.
I see what you mean, in my case I am not creating new database field for each enum instead they will be stored as rule_type and rule_value(key/value) pair, where the key is the class name and the value is the enum value.
I see. Unfortunately, I do not think that it would be possible to do this in a type-safe manner. I believe this answer is pretty much what you're asking - a binding of a special type based on column value.
Since you are using enums however, you cannot have them extend a superclass as in the above answer (since all Enums implicitly extend java.lang.Enum and Java does not support multiple-inheritance). You might however try to slightly refactor your code and have all your enums implement some interface, i.e. :
enum ProductEligibility implements Rule {...};
enum CountryEligibility implements Rule {...};
With Rule being:
interface Rule { String getRuleType(); String getRuleValue(); }
And then creating a converter like in the example in the docs page or in the separate linked answer.
Of course, this also means that your Records would have a Field<Rule> field in them, not the specific type of the enum. If this is acceptable for you, it might be a possible way to go.
I did not get this part, Of course, this also means that your Records would have a Field<Rule> field in them, not the specific type of the enum.. Does this mean I will still store two fields in db, rule_type and rule_value with CustomConverter for each one of them?
No. You would still have only one converter, of type Converter<Object, Rule>. This converter would return either ProductEligibility or CountryEligibility, but it can't return both.
So, if your database table had something like:
eligiblity_rules
------------------
id user type value
234 223 com.foo.bar.ProductEligibility PRODUCT_Y
856 855 com.foo.bar.CountryEligibility US
Your converter would look something like this:
public Converter<Object, Rule> converter() {
return new Converter<Object, Rule>() {
#Override
public Rule from(Object t) {
// t here refers to the "value" db field above
if (checkIsProductEligibilityRule())
return ProductEligibility.fromValue(...);
else
return CountryEligibility.fromValue(...)
}
// Other converter methods
};
}
So in your JOOQ-based code, you would end up having:
EligibilityRuleRecord record = dslContext.selectFrom(ELIGIBILITY_RULE).where(...).fetchOne();
Rule rule = record.getValue();
Afterwards, if you want to use the specific rule type, you would need a check and a cast:
if (rule instanceof ProductEligibility) {
ProductEligibility peRule = (ProductEligibility) rule;
...
}
if (rule instanceof CountryEligibility) {
CountryEligibility ceRule = (CountryEligibility) rule;
...
}
...
The only reason for the type database field is for selecting the correct data. Unfortunately, the Java code does not know (at compile-time) what the type is going to be, hence you will need the class checks at runtime every time you wish to do know that field's specific type.
I'm reading over the docs custom generators and data type rewrites. It seems like it has a lot of flexibility. But is it possible to combine two db fields into one?
For example, for foreign exchange, I'd like to store two fields
budget.value, budget.currency
Could I somehow have JOOQ convert this to
Value getMonetaryValue() {
return Value(this.value, this.currency);
}
Where in this case Value is a custom enum that I've created to support both value and currency.
As of jOOQ 3.11, this isn't possible out of the box. There's a pending feature request and related features that will allow for supporting what JPA calls #Embeddable types:
https://github.com/jOOQ/jOOQ/issues/6124
in hibernate to create criteria we use
Criteria criterea=session.createCritera(SomeClass.class)
It may be available in some other examples too but I am not able to understand the structure of these type of methods.
NOTE this is an example I am trying to put to understand use of SomeClass.class like arguments
my question here is what is purpose of SomeClass.class ? why do we need it, what is the advantages of using it as argument.
Edit its not a duplicate but have string connection to this question
What is this .class syntax?
If you attach .class to the end of a class name, you get the Class<T> object corresponding to the class.
Examples:
String.class returns an instance of Class<String>
Integer.class returns an instance of Class<Integer>
What can you do with a class object
Reflection! If you have access to a class object, you can do all kinds of cool stuff! You can call methods, get and set values of fields...
Why is this used in Hibernate?
I haven't used hibernate before, but this syntax is used in other libraries as well, especially in ORMs or JSON serializers. I'll use JSON serializers as an example as I'm more familiar with those.
In a JSON serializer, you need to give it a class object because it needs to get all the fields that you want to serialize to JSON. It uses reflection to get and set the values of those fields, then convert them to JSON. When it deserializes JSON, it finds the field that needs to be set with the name in the JSON. These operations require a Class object because without it, how can Java know which class should it find the field? Also, to create a new object with reflection, a Class is needed as well!
Hibernate provides many ways to handle the objects in relation with RDBMS tables.
One way is Session interface providing createCriteria() method which can be used to create a Criteria object.
As name says criteria, it is useful to execute queries by applying filtration rules and logical conditions of programmer wish.
For example:
Criteria obj=session.createCritera(Galaxy.class) // say SomeClass is Galaxy.class
List results = obj.list();
Here, criteria query is one which will simply return every object that corresponds to the Galaxy class.
We even can restrict the results with criteria, example add() method available for Criteria object to add restriction for a criteria query.
Following is the restriction to return the records with planet which has 7.4 billion population from Galaxy class:
Criteria cr = session.createCriteria(Galaxy.class);
cr.add(Restrictions.eq(“planet”, 75000000000));
List results = cr.list();
This is a problem about historical data handling.
Suppose you have a class MyClass like the following one:
class MyClass {
String field1;
Integer field2;
Long field3;
getField1() {...}
setField1(String ...) {...}
...
}
Now, suppose I need to make MyClass able to store and retrieve old data, what's the best way to do this?
The requirements are to persist the classes through Hibernate, too. And to have at most two tables per "entity": only one table or one table for the "continuity" class (the one which represents the entity which evolves over the time) and another table for the historical data (as it's suggested here)
Please note that I have to be able to assign an arbitrary valid time to the values of the fields.
The class should have an interface like:
class MyClass {
// how to store the fields????
getField1At(Instant i) {...}
setField1At(Instant i, String ...) {...}
...
}
I'm currently using the JTemporal library, and it has a TemporalAttribute<T> class, which is like a map: you can do things like T myAttr.get(Instant i) to get the version of myAttr at Instant i. I know how to persist a TemporalAttribute in a table with Hibernate (it's simple: I persist the SortedMap used by the TemporalAttribute and you get a table with start and end valid time and the value of the attribute).
The real problem is that here we have multiple attributes.
I have a solution in mind but it's not clear, and I'd like to hear your ideas.
Your project reminds me of Hibernate Envers.
The Envers project aims to enable easy
auditing of persistent classes. All
that you have to do is annotate your
persistent class or some of its
properties, that you want to audit,
with #Audited. For each audited
entity, a table will be created, which
will hold the history of changes made
to the entity. You can then retrieve
and query historical data without much
effort.
choose what you want to audit (on a per attribute basis)
make your own Revision Entity (that stores informations such as revision number, author, timestamp...)
Using Hibernate Envers for this decouples entities and revision data (in database and in your code).
You can do something like this simply by adding a version number to your domain class. I did something like this where the Id was a composite between an db assigned number and the version number, but I would advise against that. Use a normal surrogate key, and if you really want, make the [id, version] tuple a natural key.
You can actually version entire object graphs that way, just by ensuring that the version number is the same for all elements on the graph. You can then easily go back to any previous version.
You should write a lot of service tests to insure the integrity of the code that manages the version.
how can I have a field with variable type in my class? I use hibernate annotation for mapping to DB. I tried to use java.io.Serializable as field's type, but it mapped to database as a 01 amount which is true if I get it's object and cast it to it's true type. but I need to run a query on this objects that needs true amount of field.(right now I can't compare an integer field with a number)
Does anyone have any idea how can I do this?
Thanks in advance.
You can't have variable types in Java - it is statically typed language.
I guess you can achieve something like this with groovy (+ grails), which is dynamically typed. Using something like def someField you can later use someField as whatever you expect it to be.