I have a bean:
#Data
class Sample {
#NonNull
private final String name,
#NonNull
private final String rollNumber,
#NonNull
private final String standard,
}
I am saving this bean data to database in the table sample.
The problem:
I am using jdbcOperation to fetch data from the database. In my use case I want to retrieve only two fields from the database say name and standard for a particular view(I'm using Spring MVC). As all the three fields in the bean are marked with NonNull annotation from Lombok, I can not create an object inside the RowMapper overriden implementation.
private static final class SampleRowMapper implements RowMapper<Sample> {
#Override
#Nonnull
public Sample mapRow(ResultSet rs, int rowNum) throws SQLException {
Sample sample =
new Sample(
rs.getString("name"),
rs.getString("standard")
null); // error as annotation doesn't allow null values
return sample;
}
}
If I remove the annotation there are other cases that will be missed out(say saving an object of Sample with no null values, I don't want rollNumber to be null). What should be done to fulfill all use cases?
Related
I'm having trouble converting between java.sql.Timestamp and java.time.Instant using JOOQ converters.
Here's a simplified version of the code I'm working with.
public class User {
private static final Converter<Timestamp, Instant> MY_CONVERTER= Converter.of(
Timestamp.class,
Instant.class,
t -> t == null ? null : t.toInstant(),
i -> i == null ? null : Timestamp.from(i)
)
public static Table<?> table = DSL.table("user");
public static Field<String> name = DSL.field(DSL.name(table.getName(), "name"), String.class);
public static Field<Instant> name = DSL.field(DSL.name(table.getCreated(), "created"), SQLDataType.TIMESTAMP.asConvertedDataType(Converter.of(MY_CONVERTER)));
}
private class UserDto {
private String name;
private Instant created;
// getters, setters, etc.
}
public class UserWriter {
// constructor with injected DefaultDSLContext etc..
public void create(UserDto user) {
dslContext.insertInto(User.table, User.firstName, User.lastName)
.values(user.getName(), user.getCreated())
.execute();
}
}
public class UserReader {
// constructor with injected DefaultDSLContext etc..
public Result<Record> getAll() {
return dslContext.select().from(User.table).fetch();
}
}
public class UserService {
// constructor with injected UserReader etc..
public Collection<UserDto> getAll() {
return userReader
.getAll()
.stream()
.map(Users::from)
.collect(Collectors.toList());
}
}
public class Users {
public static UserDto from(Record record) {
UserDto user = new UserDto();
user.setName(record.get(User.name));
user.setCreated(record.get(User.created);
return user;
}
}
When I create a new User the converter is called and the insertion works fine. However, when I select the Users the converter isn't called and the record.get(User.created) call in the Users::from method returns a Timestamp (and therefore fails as UserDto.setCreated expects an Instant).
Any ideas?
Thanks!
Why the converter isn't applied
From the way you phrased your question (you didn't post the exact SELECT statement that you've tried), I'm assuming you didn't pass all the column expressions explicitly. But then, how would jOOQ be able to find out what columns your table has? You declared some column expressions in some class, but that class isn't following any structure known to jOOQ. The only way to get jOOQ to fetch all known columns is to make them known to jOOQ, using code generation (see below).
You could, of course,let User extend the internal org.jooq.impl.TableImpl class and use internal API to register the Field values. But why do that manually, if you can generate this code?
Code generation
I'll repeat the main point of my previous question, which is: Please use the code generator. I've now written an entire article on why you should do this. Once jOOQ knows all of your meta data via code generation, you can just automatically select all columns like this:
UserRecord user = ctx
.selectFrom(USER)
.where(USER.ID.eq(...))
.fetchOne();
Not just that, you can also configure your data types as INSTANT using a <forcedType>, so you don't need to worry about data type conversion every time.
I cannot stress this enough, and I'm frequently surprised how many projects try to use jOOQ without code generation, which removes so much of jOOQ's power. The main reason to not use code generation is if your schema is dynamic, but since you have that User class, it obviously isn't dynamic.
i have the following TableStructure in a PostgreSQL DB which is supposed to be the DB Backend for my WebApp:
init_db.sql
CREATE TABLE article (
id integer NOT NULL,
name character varying NOT NULL,
type_id integer NOT NULL
);
CREATE TABLE article_type (
id integer NOT NULL,
type_desc character varying NOT NULL
);
ALTER TABLE ONLY article
ADD CONSTRAINT
article_type_id_fkey FOREIGN KEY (type_id) REFERENCES article_type(id);
The basic access to this works (via DataSource Object defined in application.properties and letting Spring Boot handle the rest). I'm having now difficulties in understanding how to access/model this best in Spring Boot. Currently my Model Classes look like this:
ArticleType.java
public class ArticleType {
private Integer id;
private String name;
// Getters and Setters
}
andArticle.java
public class Article {
private Integer id;
private String name;
private String desc;
private ArticleType article_type;
// Getters and Setters
}
Following this example, i was constructing those classes:
ArticleTypeRepository.java
#Repository
public class ArticleTypeRepository {
#Autowired
protected JdbcTemplate jdbc;
public ArticleType getArticleType(int id) {
return jdbc.queryForObject("SELECT * FROM article.article_type WHERE id=?", articleTypeMapper, id);
}
private static final RowMapper<ArticleType> articleTypeMapper = new RowMapper<ArticleType>() {
public ArticleType mapRow(ResultSet rs, int rowNum) throws SQLException {
ArticleType articletype = new ArticleType();
articletype.setId(rs.getInt("id"));
articletype.setName(rs.getString("type_desc"));
return articletype;
}
};
and for the following file my question arises:ArticleRepository.java
#Repository
public class ArticleRepository {
#Autowired
protected JdbcTemplate jdbc;
public Article getArticle(int id) {
return jdbc.queryForObject("SELECT * FROM article.article WHERE id=?", articleMapper, id);
}
private static final RowMapper<Article> articleMapper = new RowMapper<Article>() {
public Article mapRow(ResultSet rs, int rowNum) throws SQLException {
Article article = new Article();
article.setId(rs.getInt("id"));
article.setName(rs.getString("name"));
// The following line is the one in question
// ArticleType at = getArticleType(Integer.parseInt(rs.getString("type_id")));
article.setArticle_type(at);
article.setDesc(rs.getString("description"));
return article;
}
};
What is the best practice to get the ArticleType here for the Article? Is this anyway good practice to retrieve those objects? Or should I just use a plain String object in the Article Object and query this with a view or something? I looked through the internet for "Spring Boot JDBC Nested Object Java Access Modeling" and the alike, but couldn't find any real hints or tutorials to this specific question, which makes me wonder if i'm doing something conceptually completely wrong. Any hints are appreciated (tutorials, doc's, paradigms how to do this properly, etc.)
I'll double post M. Deinum 's answer here, since it got me rolling until i switched to Hibernate/JPA:
By creating a query that returns everything you need. Write a select
statement that joins both tables.
I am considering moving from Hibernate to jOOQ but I can't find e.g.
how to have Pattern-Constraints on a String like this in Hibernate:
#NotEmpty(message = "Firstname cannot be empty")
#Pattern(regexp = "^[a-zA-Z0-9_]*$", message = "First Name can only contain characters.")
private String firstname;
How would I do that in jOOQ?
The "jOOQ way"
The "jOOQ way" to do such validation would be to create either:
A CHECK constraint in the database.
A trigger in the database.
A domain in the database.
After all, if you want to ensure data integrity, the database is where such constraints and integrity checks belong (possibly in addition to functionally equivalent client-side validation). Imagine a batch job, a Perl script, or even a JDBC statement that bypasses JSR-303 validation. You'll find yourself with corrupt data in no time.
If you do want to implement client-side validation, you can still use JSR-303 on your DTOs, which interact with your UI, for instance. But you will have to perform validation before passing the data to jOOQ for storage (as artbristol explained).
Using a Converter
You could, however, use your own custom type by declaring a Converter on individual columns and by registering such Converter with the source code generator.
Essentially, a Converter is:
public interface Converter<T, U> extends Serializable {
U from(T databaseObject);
T to(U userObject);
Class<T> fromType();
Class<U> toType();
}
In your case, you could implement your annotations as such:
public class NotEmptyAlphaNumericValidator implements Converter<String, String> {
// Validation
public String to(String userObject) {
assertNotEmpty(userObject);
assertMatches(userObject, "^[a-zA-Z0-9_]*$");
return userObject;
}
// Boilerplate
public String from(String databaseObject) { return databaseObject; }
public Class<String> fromType() { return String.class; }
public Class<String> toType() { return String.class; }
}
Note that this is more of a workaround, as Converter hasn't been designed for this use-case, even if it can perfectly implement it.
Using formal client-side validation
There's also a pending feature request #4543 to add more support for client-side validation. As of jOOQ 3.7, this is not yet implemented.
I recommend you don't try to use jOOQ in a 'hibernate/JPA' way. Leave the jOOQ generated classes as they are and map to your own domain classes manually, which you are free to annotate however you like. You can then call a JSR validator before you attempt to persist them.
For example, jOOQ might generate the following class
public class BookRecord extends UpdatableRecordImpl<BookRecord> {
private String firstname;
public void setId(Integer value) { /* ... */ }
public Integer getId() { /* ... */ }
}
You can create your own domain object
public class Book {
#NotEmpty(message = "Firstname cannot be empty")
#Pattern(regexp = "^[a-zA-Z0-9_]*$", message = "First Name can only contain characters.")
private String firstname;
public void setId(Integer value) { /* ... */ }
public Integer getId() { /* ... */ }
}
and map by hand once you've retrieved a BookRecord, in your DAO layer
Book book = new Book();
book.setId(bookRecord.getId());
book.setFirstname(bookRecord.getFirstname());
This seems quite tedious (and ORM tries to spare you this tedium) but actually it scales quite well to complicated domain objects, in my opinion, and it's always easy to figure out the flow of data in your application.
I have a database column that needs to be encrypted, when passed from a hibernate backed webapp. The webapp is on tomcat 6, Hibernate 4, and Mysql as the backing store.
The problem however is that the password to encrypt/decrypt this field will only be available at runtime of the program. Initially I had hoped to use the AES_ENCRYPT/DECRYPT methods, outlined quite well here:
DataBase encryption in Hibernate
and here:
http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/mapping.html#mapping-column-read-and-write
(Though this does refer to version 3.6 of hibernate, I believe it should be the same in 4.0).
However, since this uses the following notation:
#Column(columnDefinition= "LONGBLOB", name="encryptedBody")
#ColumnTransformer(
read="AES_DECRYPT(encryptedBody, 'password')",
write="AES_ENCRYPT(?, 'password')")
public byte[] getEncryptedBody() {
return encryptedBody;
}
public void setEncryptedBody(byte[] encryptedBody) {
this.encryptedBody = encryptedBody;
}
This requires that the password be specified in the annotation itself, and cannot be a variable.
Is there a way to use the database methods through hibernate in this manner, but with the password as a variable? Is there a better approach?
Currently there is not a way to parameterize the pieces of the read/write fragments. They are more meant as general purpose solutions. We have discussed adding support for #Encrypted in Hibernate that would roughly act like you suggest. #Encrypted would give more flexibility, like in-vm crypto versus in-db crypto, parameterization, etc.
JPA 2.1 also has a feature you could use, called attribute converters. They would only be able to apply in-vm crypto however.
You can Use Hibernate #Type attribute,Based on your requirement you can customize the annotation and apply on top of the fied. like :
public class PhoneNumberType implements UserType {
#Override
public int[] sqlTypes() {
return new int[]{Types.INTEGER, Types.INTEGER, Types.INTEGER};
}
#Override
public Class returnedClass() {
return PhoneNumber.class;
}
// other methods
}
First, the null SafeGet method:
#Override
public Object nullSafeGet(ResultSet rs, String[] names,
SharedSessionContractImplementor session, Object owner) throws HibernateException,
SQLException {
int countryCode = rs.getInt(names[0]);
if (rs.wasNull())
return null;
int cityCode = rs.getInt(names[1]);
int number = rs.getInt(names[2]);
PhoneNumber employeeNumber = new PhoneNumber(countryCode, cityCode, number);
return employeeNumber;
}
Next, the null SafeSet method:
#Override
public void nullSafeSet(PreparedStatement st, Object value,
int index, SharedSessionContractImplementor session)
throws HibernateException, SQLException {
if (Objects.isNull(value)) {
st.setNull(index, Types.INTEGER);
} else {
PhoneNumber employeeNumber = (PhoneNumber) value;
st.setInt(index,employeeNumber.getCountryCode());
st.setInt(index+1,employeeNumber.getCityCode());
st.setInt(index+2,employeeNumber.getNumber());
}
}
Finally, we can declare our custom PhoneNumberType in our OfficeEmployee entity class:
#Entity
#Table(name = "OfficeEmployee")
public class OfficeEmployee {
#Columns(columns = { #Column(name = "country_code"),
#Column(name = "city_code"), #Column(name = "number") })
#Type(type = "com.baeldung.hibernate.customtypes.PhoneNumberType")
private PhoneNumber employeeNumber;
// other fields and methods
}
This might solve your problem, This will work for all database. if you want more info refer :: https://www.baeldung.com/hibernate-custom-types
similarly you have to do UTF-8 encoding/Decoding and ISO-8859-1 Decoding/encoding
I'm migrating a legacy system over to use Hibernate 3. It currently generates its own identifiers. To keep with what the system currently does before I try and move it over to something a little better, how would I go about specifying (using annotations) my own class that will return the custom generated identifiers when an insert occurs?
Something like:
#Id
#CustomIdGenerator(Foo.class) // obviously this is not a real annotation
public String getId() { ... }
Where the Foo class has one method that generates the identifier.
Currently I'm just calling the setId(String id) method manually but was hoping for a better way to deal with this situation.
I don't think there is out-of-box support for generating custom Ids using custom annotations using pure JPA-2 API. But if you want to use provider specific API, then the job is pretty simple. Sample Example
To be provider independent try any of following tricks....
IdGeneratorHolder
public abstract class IdGeneratorHolder {
/* PersistentEntity is a marker interface */
public static IdGenerator getIdGenerator(Class<? extends PersistentEntity> entityType) {
/* sample impelementation */
if(Product.class.isAssignableFrom(entityType)) {
return new ProductIdGenerator();
}
return null;
}
}
General IdGenerator interface
public interface IdGenerator {
String generate();
}
Specific IdGenerator - Product Id Generator
public class ProductIdGenerator implements IdGenerator {
public String generate() {
/* some complicated logic goes here */
return ${generatedId};
}
}
Now set the generated id either in no-arg constructor OR in #PrePersist method.
Product.java
public class Product implements PersistentEntity {
private String id;
public Product() {
id = IdGeneratorHolder.getIdGenerator(getClass()).generate();
}
#PrePersist
public void generateId() {
id = IdGeneratorHolder.getIdGenerator(getClass()).generate();
}
}
In above example all the ids are of the same type i.e. java.lang.String. If the persistent entities have ids of different types.....
IdGenerator.java
public interface IdGenerator {
CustomId generate();
}
CustomId.java
public class CustomId {
private Object id;
public CustomId(Object id) {
this.id = id;
}
public String toString() {
return id.toString();
}
public Long toLong() {
return Long.valueOf(id.toString());
}
}
Item.java
#PrePersist
public void generateId() {
id = IdGeneratorHolder.getIdGenerator(getClass()).generate().toLong();
}
You can also use your custom annotation...
CustomIdGenerator.java
public #interface CustomIdGenerator {
IdStrategy strategy();
}
IdStrategy.java
enum IdStrategy {
uuid, humanReadable,
}
IdGeneratorHolder.java
public abstract class IdGeneratorHolder {
public static IdGenerator getIdGenerator(Class<? extends PersistentEntity> entityType) {
try { // again sample implementation
Method method = entityType.getMethod("idMethod");
CustomIdGenerator gen = method.getAnnotation(CustomIdGenerator.class);
IdStrategy strategy = gen.strategy();
return new ProductIdGenerator(strategy);
}
One more thing.... If we set id in #PrePersist method, the equals() method cannot rely on id field (i.e. surrogate key), we have to use business/natural key to implement equals() method. But if we set id field to some unique value (uuid or "app-uid" unique within application) in no-arg constructor, it helps us to implement the equals() method.
public boolean equals(Object obj) {
if(obj instanceof Product) {
Product that = (Product) obj;
return this.id ==that.id;
}
return false;
}
If we or someone else call (intentionally or by mistake) the #PrePersist annotated method more than one times, the "unique id will be changed!!!" So setting id in no-arg constructor is preferable. OR to address this issue put a not null check...
#PrePersist
public void generateId() {
if(id != null)
id = IdGeneratorHolder.getIdGenerator(getClass()).generate();
}
}
UPDATE
If we put the id generation in a
no-arg constructor, wouldn't that
cause a problem when loading entities
from the database? because hibernate
will call the no-arg constructor
causing existing ids to be
re-generated
Yeah you are right, I missed that part. :( Actually, I wanted to tell you that:- in my application every Entity object is associated with an Organization Entity; so I've created an abstract super class with two constructors, and every Entity (except Organization) extends this class.
protected PersistentEntityImpl() {
}
protected PersistentEntityImpl(Organization organization) {
String entityId = UUIDGenerator.generate();
String organizationId = organization.getEntityId();
identifier = new EntityIdentifier(entityId, organizationId);
}
The no-arg constructor is for JPA provider, we never invoke no-arg constructor, but the other organization based constructor. As you can see. id is assigned in Organization based constructor. (I really missed this point while writing the answer, sorry for that).
See if you can implement this or similar strategy in your application.
The second option was using the
#PrePersist annotation. I put that in
and the method never got hit and gave
me an exception stating that I needed
to set the id manually. Is there
something else I should be doing?
Ideally, JPA provider should invoke #PrePersist methods (one declared in class and also all the other methods that are declared in super-classes) before persisting the entity object. Can't tell you what is wrong, unless you show some code and console.
You can.
First, implement org.hibernate.id.IdentifierGenerator
Then you'd have to map it in a mapping xml file. I couldn't find a way to do this with annotations:
<!--
<identifier-generator.../> allows customized short-naming
of IdentifierGenerator implementations.
-->
<!ELEMENT identifier-generator EMPTY>
<!ATTLIST identifier-generator name CDATA #REQUIRED>
<!ATTLIST identifier-generator class CDATA #REQUIRED>
Finally, use #GeneratedValue(generator="identifier-name")
Note that this is hibernate-specific (not JPA)
Update: I took a look at the sources of Hibernate, and it seems at one place, after failing to resolve the short name, hibernates attempts to call Class.forName(..). The parameter there is called strategy. So Here's what you try:
try setting the class fully-qualified name as string in the generator attribute
try setting the class fqn as string in the #GenericGenerator strategy attribute (with some arbitrary name)
Let me know which (if any) worked