We are building the following method that copies values from one entity to another and then persists the changes which works fine for us.
private void copyProperties(E orig, E dest) {
try {
for (Field origField : orig.getClass().getDeclaredFields()) {
try {
origField.setAccessible(true);
Object value = origField.get(orig);
if (value != null) {
Field destField = dest.getClass().getDeclaredField(origField.getName());
destField.setAccessible(true);
destField.set(dest, value);
}
} catch (IllegalAccessException | NoSuchFieldException ex) {
System.out.println("IllegalAccessException ");
ex.printStackTrace();
}
}
} catch (SecurityException ex) {
System.out.println("SecurityException ");
ex.printStackTrace();
}
}
But now we need to update only the properties that have present a specific annotation called #updatable.
#Documented
#Target({ElementType.FIELD, ElementType.TYPE_USE})#Retention(RetentionPolicy.RUNTIME)public #interface Updatable {
#Nonbindingboolean nullable() default false;
}
private void copyProperties(E orig, E dest) {
try {
for (Field origField : orig.getClass().getDeclaredFields()) {
Updatable annotation = origField.getAnnotation(Updatable.class);
if (annotation != null) {
System.out.println("Field "+origField.getName()+" annotation present");
try {
origField.setAccessible(true);
Object value = origField.get(orig);
if (value != null || annotation.nullable()) {
Field destField = dest.getClass().getDeclaredField(origField.getName());
destField.setAccessible(true);
destField.set(dest, value);
}
} catch (IllegalAccessException | NoSuchFieldException ex) {
System.out.println("IllegalAccessException ");
ex.printStackTrace();
}
}
}
} catch (SecurityException ex) {
System.out.println("SecurityException ");
ex.printStackTrace();
}
}
The problem is that when we analyze the annotation and return by reference the destination entity with the updated properties, the merge method in quarkus does not detect the changes.
Related
To make the mapping between hibernate and my database work, I have this mapping :
<property name="userRolesV2" column="user_roles_v2">
<type name="io.markethero.repository.CommaSeparatedGenericEnumType">
<param name="enumClassName">io.markethero.model.UserLoginRoleV2</param>
<param name="collectionClassName">java.util.Set</param>
</type>
</property>
The idea is to directly get the Collection in my field class instead of doing the mapping each time.
For example, the field in the class could be a Set, a List, or a Queue.
In the database, the value is like "enumValue1,enumValue2,enumValue3".
To do that, my class CommaSeparatedGenericEnumType is like this:
public class CommaSeparatedGenericEnumType implements UserType, ParameterizedType {
private Class enumClass = null;
private Class targetCollection = null;
public void setParameterValues(Properties params) {
String enumClassName = params.getProperty("enumClassName");
String collectionClassName = params.getProperty("collectionClassName");
if (enumClassName == null) {
throw new MappingException("enumClassName parameter not specified");
}
if (collectionClassName == null) {
throw new MappingException("collectionClassName parameter not specified");
}
try {
this.enumClass = Class.forName(enumClassName);
} catch (ClassNotFoundException e) {
throw new MappingException("enumClass " + enumClassName + " not found", e);
}
try {
this.targetCollection = Class.forName(collectionClassName);
} catch (ClassNotFoundException e) {
throw new MappingException("targetCollection " + collectionClassName + " not found", e);
}
}
#Override
public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException {
String commaSeparatedValues = rs.getString(names[0]);
List<Object> result = new ArrayList<>();
if (!rs.wasNull()) {
String[] enums = commaSeparatedValues.split(",");
for (String string : enums) {
result.add(Enum.valueOf(enumClass, string));
}
}
return result;
}
#Override
#SuppressWarnings("unchecked")
public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException {
if (null == value) {
st.setNull(index, Types.VARCHAR);
} else {
List<Object> enums = (List) value;
StringBuilder sb = new StringBuilder("");
for (Object each : enums) {
sb.append(each.toString()).append(",");
}
if (sb.toString().isEmpty()) {
st.setNull(index, Types.VARCHAR);
} else {
String commaSeparatedIds = sb.toString().substring(0, sb.toString().length() - 1);
st.setString(index, commaSeparatedIds);
}
}
}
}
I would like to be able to parametrize which collection nullSafeGet and nullSafeSet are going to use, because for now, it's only working with a list.
Thank you!
Maybe my question was asked was oddly, but here what I did:
For the getter, I used the factory pattern already implemented by Spring : CollectionFactory.
#Override
public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException {
String commaSeparatedValues = rs.getString(names[0]);
Collection<Object> result = CollectionFactory.createCollection(this.targetCollection, 50);
if (!rs.wasNull()) {
String[] enums = commaSeparatedValues.split(",");
for (String string : enums) {
try {
result.add(Enum.valueOf(enumClass, string));
} catch (IllegalArgumentException e) {
throw new MappingException("[CommaSeparatedGenericEnumType::nullSafeGet] No such enum value"+ string +"for enum : " + enumClass, e);
}
}
}
return result;
}
For the setter, I used the reflection API.
public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException {
if (value == null) {
st.setNull(index, Types.VARCHAR);
} else {
if (value instanceof Collection) {
try {
StringBuilder sb = new StringBuilder("");
Constructor<?> c = value.getClass().getConstructor(Collection.class);
Collection<Object> enums = (Collection<Object>) c.newInstance((Collection<Object>) value);
for (Object each : enums) {
sb.append(each.toString()).append(",");
}
String commaSeparatedIds = sb.substring(0, sb.toString().length() - 1);
if (sb.length() > 0) {
st.setString(index, commaSeparatedIds);
} else {
st.setNull(index, Types.VARCHAR);
}
} catch (NoSuchMethodException e) {
throw new MappingException("[CommaSeparatedGenericEnumType::nullSafeSet] No such constructor found for class : " + value.getClass(), e);
} catch (InstantiationException e) {
throw new MappingException("[CommaSeparatedGenericEnumType::nullSafeSet] Class : " + value.getClass() + " cannot be instantiate ", e);
} catch (IllegalAccessException e) {
throw new MappingException("[CommaSeparatedGenericEnumType::nullSafeSet] You cannot access to constructor of class : " + value.getClass(), e);
} catch (InvocationTargetException e) {
throw new MappingException("[CommaSeparatedGenericEnumType::nullSafeSet] InvocationTargetException for class : " + value.getClass(), e);
}
} else {
st.setNull(index, Types.VARCHAR);
throw new IllegalArgumentException();
}
}
}
We have a spring boot Application which makes RESTFul calls to a bunch of backends, one of them returns null reponses at times, and we are observing the connections are not released during these instances because of this code in RestTemplate class:
protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor) throws RestClientException {
Assert.notNull(url, "'url' must not be null");
Assert.notNull(method, "'method' must not be null");
ClientHttpResponse response = null;
try {
ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
response = request.execute();
handleResponse(url, method, response);
if (responseExtractor != null) {
return responseExtractor.extractData(response);
}
else {
return null;
}
}
catch (IOException ex) {
String resource = url.toString();
String query = url.getRawQuery();
resource = (query != null ? resource.substring(0, resource.indexOf('?')) : resource);
throw new ResourceAccessException("I/O error on " + method.name() +
" request for \"" + resource + "\": " + ex.getMessage(), ex);
}
finally {
if (response != null) {
response.close();
}
}
}
Is there a way we can release the connection or consume the contents for when response is null or erring out?
Edited to add code causing errors:
MyHttpClientClass{
private X getResponseBody(RestClient client, URI uri, HttpMethod method, HttpEntity<T> entity, Class<R> responseType, MyErrorHandler errorHandler) {
try
{ String host = this.getHost();
ResponseEntity<X> resp = client.exchange(uri, method, entity, responseType);
return resp.getBody();
} catch (HttpServerErrorException | HttpClientErrorException e)
{ handleHttpException(e, errorHandler);
throw e;
} catch (Exception e) {
log(e);
throw e; } } }
-----------
Class1 implements Callable<T>
{
#Override public T doCall() throws Exception {
try
{ return this.getRestClient().exchange(this.getUri(),
this.getHttpMethod(), this.getEntity(), getResponseType()).getBody(); } catch (HttpClientErrorException ex) { throw ex; } catch (HttpStatusCodeException ex) { if(this.isNeededRetry(ex)) { throw ex; }else { return generateErrorResponse(ex).getBody(); } } catch (RestClientException ex) { throw ex; } catch (Exception ex) { throw ex; } } }
----------
MySpringApplicationClass{
public X get(String api, String params, Class<R> responseType, String path, List<String> pathVariables, MyErrorHandler errorHandler)
{
return getResponseBody(...);
}}
I'm implementing a mail Sender, near 1'6000.000 mails (with images and PDF) in one day per month (closing month extract), the mails are about 12 products...
I need to fill a Message Scratch per product... in order to not read (per email) else per product.
I'm trying to implement cloning javax.mail.Message and javax.mail.Multipart in order to be faster.
AddContent to Multipart
public static void addContent(final Multipart multipart, String contenidoCorreo) throws Exception {
MimeBodyPart mimeBodyPart = new PreencodedMimeBodyPart("8bit");
mimeBodyPart.setText(contenidoCorreo, "iso-8859-1", "html");
multipart.addBodyPart(mimeBodyPart, 0);
}
Add Image per Bytes
public static void addImageToMultipart(final Multipart multipart, byte[] contenidoImagen, String nombreImagen) throws Exception {
MimeBodyPart imagenMimeBodyPart = new MimeBodyPart();
try {
ByteArrayDataSource byteArrayDataSource = new ByteArrayDataSource(contenidoImagen, "image/*");
imagenMimeBodyPart.setDataHandler(new DataHandler(byteArrayDataSource));
imagenMimeBodyPart.setFileName(nombreImagen);
imagenMimeBodyPart.setContentID("<" + nombreImagen + ">");
imagenMimeBodyPart.setDisposition(MimeBodyPart.INLINE);
multipart.addBodyPart(imagenMimeBodyPart);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
}
}
AddPDF per File
public static void addPDF(final Multipart multipart, String ruta, String nombre) throws Exception {
Path path = Paths.get(ruta, nombre);
if (path.toFile().exists()) {
MimeBodyPart preencodedMimeBodyPart = new PreencodedMimeBodyPart("base64");
preencodedMimeBodyPart.attachFile(path.toFile());
preencodedMimeBodyPart.setFileName(nombre);
preencodedMimeBodyPart.setHeader("Content-Type", "application/pdf");
preencodedMimeBodyPart.setDisposition(MimeBodyPart.ATTACHMENT);
multipart.addBodyPart(preencodedMimeBodyPart);
MimeBodyPart pdfMimeBodyPart = new MimeBodyPart();
}
My Cloning Message
public static Message cloneMessage(Message source) {
//Multiple and Separated Exceptions because maybe not all properties are defined in some time.
Message target = new MimeMessage(source.getSession());
try {
if (source.getFrom() != null && source.getFrom().length > 0) {
Address address = (source.getFrom()[0]);
target.setFrom(new InternetAddress(((InternetAddress) address).getAddress(), ((InternetAddress) address).getPersonal()));
}
} catch (Exception ex) {
//Handle Exception
}
try {
target.setSentDate((Date) (source.getSentDate().clone()));
} catch (MessagingException ex) {
//Handle Exception
}
try {
target.setRecipients(Message.RecipientType.TO, target.getRecipients(Message.RecipientType.TO).clone());
} catch (MessagingException ex) {
//Handle Exception
}
try {
Enumeration numerationHeaders = source.getAllHeaders();
while (numerationHeaders.hasMoreElements()) {
Header header = (Header) numerationHeaders.nextElement();
target.addHeader(header.getName(), header.getValue());
}
} catch (MessagingException ex) {
//Handle Exception
}
try {
target.setSubject(new String(source.getSubject()));
} catch (MessagingException ex) {
//Handle Exception
}
try {
target.setContent(cloneMultipart((Multipart)(source.getContent())));
} catch (Exception ex) {
//Handle Exception
}
return target;
}
Cloning Multipart
public static Multipart cloneMultipart(Multipart source) {
MimeMultipart target = new MimeMultipart();
try {
for (int i = 0; i < source.getCount(); i++) {
MimeBodyPart mimeBodyPart = (MimeBodyPart)source.getBodyPart(i);
mimeBodyPart //?????
}
} catch (MessagingException ex) {
//Handle Exception
}
return target;
}
How cloning Multipart?
some advice to clone Message?
How detect the Content (the used with addContent method) has been added?
Using the below code to select Combobox Option. Combo box is clicked but option is not selected.
window.Get<ComboBox>(SearchCriteria.ByAutomationId("cbotire")).Select("Three");
public static bool TrySelect(ComboBox combo, string val)
{
TryCollapse(combo.AutomationElement);
try
{
TryExpand(combo.AutomationElement);
Thread.Sleep(200);
combo.Select(val);
TryCollapse(combo.AutomationElement);
}
catch (Exception e) { }
if (combo.SelectedItemText == candidate)
{
TryCollapse(combo.AutomationElement);
return true;
}
TryCollapse(combo.AutomationElement);
return false;
}
public static void TryCollapse(AutomationElement ae)
{
object invoke;
if (ae.TryGetCurrentPattern(ExpandCollapsePattern.Pattern, out invoke))
{
try
{
(invoke as ExpandCollapsePattern).Collapse();
}
catch (Exception e) { }
}
}
public static void TryExpand(AutomationElement ae)
{
object invoke;
if (ae.TryGetCurrentPattern(ExpandCollapsePattern.Pattern, out invoke))
{
try
{
(invoke as ExpandCollapsePattern).Expand();
}
catch (Exception e) { }
}
}
How can i get list of tiket with its categories through Jpa Specification
Example model:
#Entity
#Table(name = "tickets")
public class Ticket {
#Id
private Integer id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "category_id")
private Category
}
Method of service:
public Page<Ticket> findAll(Pageable pageable) {
return ticketRepository.findAll((root, query, cb) -> {
root.join("category");
return query.getRestriction();
}, pageable);
}
I was able to eager load the collection by using a fetch instead of a join.
public Page<Ticket> findAll(Pageable pageable) {
return ticketRepository.findAll((root, query, cb) -> {
root.fetch("category");
return query.getRestriction();
}, pageable);
}
The fetch method will use the default join type (inner). If want to load tickets with no category, you'll have to pass JoinType.LEFT as the second parameter.
as they say, stackoverflow giveth, stackoverflow taketh..
I took this class off SO quite some time ago, feel free to recycle it..
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import org.apache.commons.beanutils.PropertyUtils;
import org.hibernate.Hibernate;
public class BeanLoader {
/**
* StackOverflow safe, if called before json creation, cyclic object must be avoided
*/
public static void eagerize(Object obj) {
if(!Hibernate.isInitialized(obj))
Hibernate.initialize(obj);
PropertyDescriptor[] properties = PropertyUtils.getPropertyDescriptors(obj);
for (PropertyDescriptor propertyDescriptor : properties) {
Object origProp = null;
try {
origProp = PropertyUtils.getProperty(obj, propertyDescriptor.getName());
} catch (IllegalAccessException e) {
// Handled, but hopefully dead code
origProp=null;
} catch (InvocationTargetException e) {
// Single catch for obsolete java developers!
origProp=null;
} catch (NoSuchMethodException e) {
// Single catch for obsolete java developers!
origProp=null;
}
if (origProp != null
&& origProp.getClass().getPackage().toString().contains("domain")) {
eagerize(origProp, new ArrayList<String>());
}
if (origProp instanceof Collection) {
for (Object item : (Collection) origProp) {
if (item.getClass().getPackage().toString().contains("domain")){
eagerize(item, new ArrayList<String>());
}
}
}
}
}
/**
* StackOverflows if passed a bean containing cyclic fields. Call only if sure that this won't happen!
*/
public static void eagerizeUnsafe(Object obj) {
if(!Hibernate.isInitialized(obj))
Hibernate.initialize(obj);
PropertyDescriptor[] properties = PropertyUtils.getPropertyDescriptors(obj);
for (PropertyDescriptor propertyDescriptor : properties) {
Object origProp = null;
try {
origProp = PropertyUtils.getProperty(obj, propertyDescriptor.getName());
} catch (IllegalAccessException e) {
// Handled, but hopefully dead code
origProp=null;
} catch (InvocationTargetException e) {
// Single catch for obsolete java developers!
origProp=null;
} catch (NoSuchMethodException e) {
// Single catch for obsolete java developers!
origProp=null;
}
if (origProp != null
&& origProp.getClass().getPackage().toString().contains("domain")) {
eagerize(origProp);
}
if (origProp instanceof Collection) {
for (Object item : (Collection) origProp) {
if (item.getClass().getPackage().toString().contains("domain")){
eagerize(item);
}
}
}
}
}
private static void eagerize(Object obj, ArrayList<String> visitedBeans) {
if (!visitedBeans.contains(obj.getClass().getName())){
visitedBeans.add(obj.getClass().getName());
} else {
return;
}
if(!Hibernate.isInitialized(obj))
Hibernate.initialize(obj);
PropertyDescriptor[] properties = PropertyUtils.getPropertyDescriptors(obj);
for (PropertyDescriptor propertyDescriptor : properties) {
Object origProp = null;
try {
origProp = PropertyUtils.getProperty(obj, propertyDescriptor.getName());
} catch (IllegalAccessException e) {
// Handled, but hopefully dead code
origProp=null;
} catch (InvocationTargetException e) {
// Single catch for obsolete java developers!
origProp=null;
} catch (NoSuchMethodException e) {
// Single catch for obsolete java developers!
origProp=null;
}
if (origProp != null
&& origProp.getClass().getPackage().toString().contains("domain")) {
eagerize(origProp, visitedBeans);
}
if (origProp instanceof User){
((User) origProp).setRelatedNra(null);
User u=(User) origProp;
if (u.getRelatedMps()!=null)
u.getRelatedMps().clear();
if (u.getRelatedDps()!=null)
u.getRelatedDps().clear();
}
if (origProp instanceof Collection) {
for (Object item : (Collection) origProp) {
if (item.getClass().getPackage().toString().contains("domain")){
eagerize(item, (ArrayList<String>) visitedBeans.clone());
}
}
}
}
}
}
modify it as you see fit.. the method you'll call is "eagerizeUnsafe".
YMMV, but this should to the trick to eagerize all the collections of a lazily initialized bean.