I want to create a sequence with prefix "CID_00001" (example):
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE)
private String id;
Is there any way to do this ?
You can do that using a custom id generator.
The easiest way is to extend Hibernate's SequenceStyleGenerator, which implements the access to the database sequence (incl. a few interesting optimizations)
public class StringPrefixedSequenceIdGenerator extends SequenceStyleGenerator {
public static final String VALUE_PREFIX_PARAMETER = "valuePrefix";
public static final String VALUE_PREFIX_DEFAULT = "";
private String valuePrefix;
public static final String NUMBER_FORMAT_PARAMETER = "numberFormat";
public static final String NUMBER_FORMAT_DEFAULT = "%d";
private String numberFormat;
#Override
public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
return valuePrefix + String.format(numberFormat, super.generate(session, object));
}
#Override
public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) throws MappingException {
super.configure(LongType.INSTANCE, params, serviceRegistry);
valuePrefix = ConfigurationHelper.getString(VALUE_PREFIX_PARAMETER, params, VALUE_PREFIX_DEFAULT);
numberFormat = ConfigurationHelper.getString(NUMBER_FORMAT_PARAMETER, params, NUMBER_FORMAT_DEFAULT);
}
}
After you've implemented your own id generator, you can reference it in a #GenericGenerator annotation.
#Entity
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "book_seq")
#GenericGenerator(
name = "book_seq",
strategy = "org.thoughts.on.java.generators.StringPrefixedSequenceIdGenerator",
parameters = {
#Parameter(name = StringPrefixedSequenceIdGenerator.INCREMENT_PARAM, value = "50"),
#Parameter(name = StringPrefixedSequenceIdGenerator.VALUE_PREFIX_PARAMETER, value = "CID_"),
#Parameter(name = StringPrefixedSequenceIdGenerator.NUMBER_FORMAT_PARAMETER, value = "%05d") })
private String id;
...
}
I think you are using the database sequence for generating the PK. So add a trigger before inserting into the table. e.g., (in oracle )
CREATE OR REPLACE TRIGGER sometable_trigger
BEFORE INSERT ON SomeTable
FOR EACH ROW
BEGIN
SELECT 'CID_' || to_char(sometable_seq.NEXTVAL, "00009")
INTO :new.id
FROM dual;
END;
/
assumption: sequence name as "sometable_seq" and id column name id
Related
I have table as below:
CREATE TABLE recipes
(
id INT AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
components JSON,
active BOOLEAN NULL DEFAULT TRUE,
PRIMARY KEY (id),
UNIQUE KEY (name)
)
CHARACTER SET "UTF8"
ENGINE = InnoDb;
I have created pojo class like below:
#JsonIgnoreProperties(ignoreUnknown = true)
public class CValueRecipeV2
{
#JsonProperty("components")
#JsonAlias("matcher.components")
#Column(name = "components")
#Valid
private List<CComponentV2> mComponents;
#JsonProperty("name")
#Column(name = "name")
private String name;
public List<CComponentV2> getComponents()
{
return mComponents;
}
public void setComponents(List<CComponentV2> mComponents)
{
this.mComponents = mComponents;
}
public String getName()
{
return mName;
}
public void setName(String mName)
{
this.mName = mName;
}
}
another class
#JsonIgnoreProperties(ignoreUnknown = true)
public class CComponentV2
{
#JsonProperty("shingle_size")
#JsonAlias("shingleSize")
#CShingleField
private Integer mShingleSize;
public Integer getmShingleSize()
{
return mShingleSize;
}
public void setmShingleSize(Integer mShingleSize)
{
this.mShingleSize = mShingleSize;
}
}
Now I am trying to fetch the record from the database using JOOQ.
But I am not able to convert json component string into component class.
I am reading the data from the table as mentioned below:
context.dsl().select(RECIPES.asterisk())
.from(RECIPES)
.where(RECIPES.NAME.eq(name))
.fetchInto(CValueRecipeV2.class);
In the database, I have the following record.
ID name components active
1 a [{"shingle_size=2"}] true
While fetching the data, I am receiving the following error
Caused by: org.jooq.exception.DataTypeException: Cannot convert from {shingle_size=2} (class java.util.HashMap) to class com.ac.config_objects.CComponentV2
I am new to JOOQ. Please let me know if I missing anything.
Thanks in advance.
I have solved my problem using the jooq converter.
var record = context.dsl().select(RECIPES.asterisk())
.from(RECIPES)
.where(RECIPES.NAME.eq(name))
.fetchOne();
record.setValue(RECIPES.COMPONENTS, record.get(RECIPES.COMPONENTS, new CComponentV2Converter()));
var recipe = record.into(CValueRecipeV2.class);
and my converter lools like below:
public class CComponentV2Converter implements Converter<Object, List<CComponentV2>>
{
static final long serialVersionUID = 0;
#Override
public List<CComponentV2> from(Object databaseObject)
{
var componentList = CObjectCaster.toMapList(databaseObject);
List<CComponentV2> cComponentV2s = new ArrayList<>();
componentList.forEach(e -> {
CComponentV2 cComponentV2 = new CComponentV2();
cComponentV2.setmShingleSize(CObjectCaster.toInteger(e.get("shingle_size")));
cComponentV2s.add(cComponentV2);
});
return cComponentV2s;
}
}
jOOQ doesn't understand your #JsonProperty and other annotations out of the box. You will have to implement your own record mapper to support them:
https://www.jooq.org/doc/latest/manual/sql-execution/fetching/pojos-with-recordmapper-provider/
I am trying to have a class that has a certain list of objects (specified by another class) persisted in the database as a string (use JPA Converter - all good).
And then I want to use Specification to search inside that string.
What is the best way to create the predicates? I don't seem to understand the connection bettween the AttributeConverter and the Expression in the Specification.
The parent class:
#Entity #Table
public class A {
#Column #Id #GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#Column
private String name;
#Enumerated(EnumType.STRING)
private SomeType type;
#Column(length=1000) #Convert(converter = BConverter.class)
private List<B> bList;
private Integer no;
}
The listed object class:
public class B{
private String type;
private Integer quantity;
}
The converter:
#Converter
public class BConverter implements AttributeConverter<List<B>, String> {
private static final String SEPARATOR = ":";
private static final String LIST_SEPARATOR = ";";
#Override public String convertToDatabaseColumn(List<B> bList) {
return bList.stream().map(e->convertToString(e)).collect(Collectors.joining(LIST_SEPARATOR));
}
#Override public List<B> convertToEntityAttribute(String str) {
if(str==null || str.isEmpty() ) return null;
return Arrays.stream(str.split(LIST_SEPARATOR)).map(e->convertFromString(e)).collect(Collectors.toList());
}
private String convertToString(B b){
if(entity==null) return null;
return b.getType().toString() +SEPARATOR+ b.getQuantity().toString();
}
private B convertFromString(String subStr){
if(subStr==null || subStr.isEmpty() ) return null;
String[] pair = subStr.split(SEPARATOR);
return new B(pair[0],Integer.valueOf(pair[1]));
}
}
In the database should look something like:
Table A:
id: 1;
name: "Some Name";
type: "THISTYPE";
blist: "TYPE1:11;TYPE2:22";
no: 0;
id: 2;
name: "Other Name";
type: "THISTYPE";
blist: "TYPE1:45;TYPE2:56";
no: 12;
I would then like to create Specifications to search over this table for the attributes inside the bList.
For example, search by an entity that contains a B object where type=TYPE1 and a quantity>=30.
public static Specification<A> customSpecification(String type, Integer value) {
return (root, query, builder) -> ///?????????
}
Is there a way to use such specifications where the DB attribute is a String but JAVA only sees the objects?
I'm currently working on a project where I'm trying to get a list of enities from table which does not have a primary key (dk_systemtherapie_merkmale). This table is 1:n related to another table (dk_systemtherapie). See the screenshot for the table structure.
When getting an entry for dk_systemtherapie, the program fetches the Collection "dkSystemtherapieMerkmalesById". However, the first table entry is fetched as often as the number of actual entries in the table is. It never fetches the other entries from dk_systemtherapie_merkmale. I assume it has something to do with the fact that hibernate can't differ between the entries, but I don't know how to fix it.
Table schema
I've created two corresponding entity classes, dk_systemtherapie:
#Entity
#Table(name = "dk_systemtherapie", schema = "***", catalog = "")
public class DkSystemtherapieEntity {
private int id;
private Collection<DkSystemtherapieMerkmaleEntity> dkSystemtherapieMerkmalesById;
#Id
#Column(name = "id")
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#OneToMany(mappedBy = "dkSystemtherapieByEintragId")
public Collection<DkSystemtherapieMerkmaleEntity> getDkSystemtherapieMerkmalesById() {
return dkSystemtherapieMerkmalesById;
}
public void setDkSystemtherapieMerkmalesById(Collection<DkSystemtherapieMerkmaleEntity> dkSystemtherapieMerkmalesById) {
this.dkSystemtherapieMerkmalesById = dkSystemtherapieMerkmalesById;
}
}
Here the second one, which is accessing the table without a primary key, dk_systhemtherapie_merkmale:
#Entity #IdClass(DkSystemtherapieMerkmaleEntity.class)
#Table(name = "dk_systemtherapie_merkmale", schema = "***", catalog = "")
public class DkSystemtherapieMerkmaleEntity implements Serializable {
#Id private Integer eintragId;
#Id private String feldname;
#Id private String feldwert;
private DkSystemtherapieEntity dkSystemtherapieByEintragId;
#Basic
#Column(name = "eintrag_id")
public Integer getEintragId() {
return eintragId;
}
public void setEintragId(Integer eintragId) {
this.eintragId = eintragId;
}
#Basic
#Column(name = "feldname")
public String getFeldname() {
return feldname;
}
public void setFeldname(String feldname) {
this.feldname = feldname;
}
#Basic
#Column(name = "feldwert")
public String getFeldwert() {
return feldwert;
}
public void setFeldwert(String feldwert) {
this.feldwert = feldwert;
}
#Id
#ManyToOne
#JoinColumn(name = "eintrag_id", referencedColumnName = "id")
public DkSystemtherapieEntity getDkSystemtherapieByEintragId() {
return dkSystemtherapieByEintragId;
}
public void setDkSystemtherapieByEintragId(DkSystemtherapieEntity dkSystemtherapieByEintragId) {
this.dkSystemtherapieByEintragId = dkSystemtherapieByEintragId;
}
}
I assume the problem is releated to the fact that Hibernate is using the following annotation as the one and only id for fetching data from database.
#Id
#ManyToOne
#JoinColumn(name = "eintrag_id", referencedColumnName = "id")
public DkSystemtherapieEntity getDkSystemtherapieByEintragId() {
return dkSystemtherapieByEintragId;
}
This leads to the problem that when getting more than one entry with the same id (as the id is not unique), you will get the number of entries you would like to but hibernate is always fetching the first entry for this id. So in fact you are getting dublicate entries.
So how to fix this?
According to this question: Hibernate and no PK, there are two workarounds which are actually only working when you don't have NULL entries in your table (otherwise the returning object will be NULL as well) and no 1:n relationship. For my understanding, hibernate is not supporting entities on tables without primary key (documentation). To make sure getting the correct results, I would suggest using NativeQuery.
Remove the Annotations and private DkSystemtherapieEntity dkSystemtherapieByEintragId; (incl. beans) from DkSystemtherapieMerkmaleEntity.java und add a constructor.
public class DkSystemtherapieMerkmaleEntity {
private Integer eintragId;
private String feldname;
private String feldwert;
public DkSystemtherapieMerkmaleEntity(Integer eintragId, String feldname, String feldwert) {
this.eintragId = eintragId;
this.feldname = feldname;
this.feldwert = feldwert;
}
public Integer getEintragId() {
return eintragId;
}
public void setEintragId(Integer eintragId) {
this.eintragId = eintragId;
}
public String getFeldname() {
return feldname;
}
public void setFeldname(String feldname) {
this.feldname = feldname;
}
public String getFeldwert() {
return feldwert;
}
public void setFeldwert(String feldwert) {
this.feldwert = feldwert;
}
}
Remove private Collection<DkSystemtherapieMerkmaleEntity> dkSystemtherapieMerkmalesById; (incl. beans) from DkSystemtherapieEntity.java.
Always when you need to get entries for a particular eintrag_id, use the following method instead of the Collection in DkSystemtherapieEntity.java.
public List<DkSystemtherapieMerkmaleEntity> getDkSystemtherapieMerkmaleEntities(int id) {
Transaction tx = session.beginTransaction();
String sql = "SELECT * FROM dk_systemtherapie_merkmale WHERE eintrag_id =:id";
List<Object[]> resultList;
resultList = session.createNativeQuery(sql)
.addScalar("eintrag_id", IntegerType.INSTANCE)
.addScalar("feldname", StringType.INSTANCE)
.addScalar("feldwert", StringType.INSTANCE)
.setParameter("id", id).getResultList();
tx.commit();
List<DkSystemtherapieMerkmaleEntity> merkmale = new ArrayList<>();
for (Object[] o : resultList) {
merkmale.add(new DkSystemtherapieMerkmaleEntity((Integer) o[0], (String) o[1], (String) o[2]));
}
return merkmale;
}
Call getDkSystemtherapieMerkmaleEntities(dkSystemtherapieEntityObject.getid()) instead of getDkSystemtherapieMerkmalesById().
I'm just start to learning this ORM, so maybe I've done something wrong. In entity I write OneToOne relation but greendao doesn't generate it. If I'm writing in entity constructor arguments for foreign key it just ignores this and makes it like this. So there is no property and column in table. Thank you.
Public:
#Entity(active = true)
public class Public {
#Id(autoincrement = true)
Long id;
int publicId;
#ToOne(joinProperty = "id")
private Category category; ...
#Generated(hash = 12945501)
public Public(Long id, int publicId) {
this.id = id;
this.publicId = publicId;
}
PublicDao:
public class PublicDao extends AbstractDao<Public, Long> {
public static final String TABLENAME = "PUBLIC";
/**
* Properties of entity Public.<br/>
* Can be used for QueryBuilder and for referencing column names.
*/
public static class Properties {
public final static Property Id = new Property(0, Long.class, "id", true, "_id");
public final static Property PublicId = new Property(1, int.class, "publicId", false, "PUBLIC_ID");
} ...
/** Creates the underlying database table. */
public static void createTable(Database db, boolean ifNotExists) {
String constraint = ifNotExists? "IF NOT EXISTS ": "";
db.execSQL("CREATE TABLE " + constraint + "\"PUBLIC\" (" + //
"\"_id\" INTEGER PRIMARY KEY AUTOINCREMENT ," + // 0: id
"\"PUBLIC_ID\" INTEGER NOT NULL );"); // 1: publicId
}
My mistake. I should to add another field for it, and to write it in joinProperty.
I'm actually trying to use the Hibernate ORM with java annotations for the mapping. I use PostgreSQL for my database and its UUID type. As I have seen on others posts, when I want to map the UUID pgsql type to the UUID Java type, I should add #Type(type="pg-uuid") to every UUID fields. The problem is that it doesn't seem to be recognized by hibernate as I get this:
org.hibernate.MappingException: Could not determine type for: pg-uuid, at table: ev_session, for columns: [org.hibernate.mapping.Column(user_id)]
I can't find anything on Google mentionning that, so I really have no clue of where I should look.
Here is my mapped class. The table uses two UUID as primary key, that's why I had to create a nested class representing it. I'm not sure that I did it right though.
#Entity
#Table(name="ev_session")
public class SessionDb {
////////////////////////
// VARIABLES
////////////////
#Id
private PrimaryKey primaryKey;
#Column(name="date")
private Date timestamp;
////////////////////////
// NESTED CLASSES
////////////////
#Embeddable
private class PrimaryKey implements Serializable {
private static final long serialVersionUID = 7124577164356450734L;
#Type(type="pg-uuid")
#Column(name="user_id")
public UUID userID;
#Type(type="pg-uuid")
#Column(name="key")
public UUID token;
}
////////////////////////
// CONSTRUCTORS
////////////////
public SessionDb() {
this.primaryKey = new PrimaryKey();
}
////////////////////////
// METHODS
////////////////
#Override
public String toString() {
return this.primaryKey.token + " associated to " + this.primaryKey.userID + " at " + this.timestamp;
}
////////////////////////
// GETTERS/SETTERS
////////////////
public final UUID getUserID() {
return this.primaryKey.userID;
}
public final void setUserID(UUID userID) {
this.primaryKey.userID = userID;
}
public final UUID getToken() {
return this.primaryKey.token;
}
public final void setToken(UUID token) {
this.primaryKey.token = token;
}
public final Date getTimestamp() {
return timestamp;
}
public final void setTimestamp(Date timestamp) {
this.timestamp = timestamp;
}
}
Thanks for your help
I guess you should use a Generator:
#Id #GeneratedValue(generator="system-uuid")
#GenericGenerator(name="system-uuid", strategy = "uuid")
Take a look here.
Some more documentation here.
In case of xml mapping you can add
<typedef class="org.hibernate.type.PostgresUUIDType" name="pg-uuid" />
Like described in https://developer.jboss.org/thread/229747 but I don't know how to configure this with annotations.