JPA persistence.xml - java

Is there a way to make the data on the persistence.xml dynamic?
I was thinking of adding a database name property on my properties file, then the tables are created, if not existing.
Is this possible?
I'm using EclipseLink(JPA2.0) and MySQL.

If you use JPA in standalone environment, you can pass additional properties to Persistence.createEntityManagerFactory().
In application server environments you can use datasource obtained from JNDI.

If you are using spring, you can use the spring property-placeholder-configurer mechanism to do so. Just extend your EclipseLink vendor adapter:
public class ExtendedJpaVendorAdapter extends XJpaVendorAdapter {
private Map<String, Object> vendorProperties;
#Override
public Map<String, Object> getJpaPropertyMap() {
Map<String, Object> properties = super.getJpaPropertyMap();
properties.putAll(vendorProperties);
return properties;
}
public Map<String, Object> getVendorProperties() {
return vendorProperties;
}
public void setVendorProperties(Map<String, Object> vendorProperties) {
this.vendorProperties = vendorProperties;
}
}
And then you can configure these in the spring xml file.

Related

Merge property file into application.properties

I have two properties files. Let's say a.properties and b.properties. these file values has been stored in maps created, let say aMap and bMap.
#PropertySource(value={ "classpath:a.properties", "classpath:b.properties"})
Class propFile{
Private Map<String, String> aMap;
Private Map<String, String> bMap;
}
I have to merge these property file into application.properties such that it works same way. Please provide me solution for this.
You will be able to retrieve your properties by annotating your properties class with #Configuration and #ConfigurationProperties:
#Configuration
#ConfigurationProperties(prefix="maps")
public class ConfigProperties {
private Map<String, String> a;
private Map<String, String> b;
// getters and setters
}
The corresponding application.yml would look as follows:
maps:
a:
key:
test1
b:
key:
test2
Or alternatively with an application.properties file:
maps.a.key=test1
maps.b.key=test2

JPA: Hibernate: updating of column with Converter class doesn't work

I'm using a Converter class to store my entity field (type - Map<String, Object>) in MySql DB as a JSON text:
#Entity
#Table(name = "some_table")
public class SomeEntity {
...
#Column(name = "meta_data", nullable = false)
#Convert(converter = MetaDataConverter.class)
Map<String, Object> metaData = null;
...
}
Here is the MetaDataConverter class:
#Converter
public class MetaDataConverter implements AttributeConverter<Map<String, Object>, String> {
private final ObjectMapper objectMapper = new ObjectMapper();
#Override
public String convertToDatabaseColumn(Map<String, Object> metadata) {
try {
return objectMapper.writeValueAsString(metadata);
} catch (Exception e) {
...
}
}
#Override
public Map<String, Object> convertToEntityAttribute(String dbData) {
try {
return objectMapper.readValue(dbData, Map.class);
} catch (Exception e) {
...
}
}
}
Here is the my service class:
#Service
#RequiredArgsConstructor
#Transactional
public class MetaDataService {
private final JpaRepository<MetaDataEntity, String> metaDataRepository;
public MetaDataEntity updateOnlyMetadata(String someParameter, Map<String, Object> newMetaData) {
MetaDataEntity metaDataEntity = metaDataRepository.findBySomeParameter(someParameter);
metaDataEntity.setMetaData(newMetaData);
return metaDataRepository.save(metaDataEntity);
}
}
For the creation it works fine, but it doesn't work with the updating of the converted field. If i try to update only the
metaData field the appropriate column in database is not updated. However, the metaData is updated in case of updating with the other entity fields.
I've already seen the same questions (JPA not updating column with Converter class and Data lost because of JPA AttributeConverter?), but i have not found the answer. Is there something like a standard or best practice for such a case?
FYI: For the CRUD operations i'm using the Spring Data JpaRepository class and its methhods.
Implement proper equals and hashcode methods on your map value objects. JPA can then use those methods to identify that your Map is dirty.
I had a similar problem. i figure out that hibernate used the equals method to determine if one of attribute is dirty and then make an update.
you have two choices : implement correctly the equals method for the entity including the converted attribute
or
instead of using "Map" try to use "HashMap" as attribute type.
the HashMap has an already implemented equals method and hibernate will use it

Java REST endpoint to return any database columns

Lets say I have a postgres table named Employee with the following columns:
ID
FirstName
LastName
Employment
Date
Manager
Department
I am interested in having a REST endpoint such that /employee/{ID} will return all information for that particular employee in JSON format, but if I specify /employee/{ID}/FirstName then it'd return particular employee's first name only in JSON format, /employee/{ID}/LastName would return the employee's last name in JSON format, and so on. Is there a good way to implement this instead of implementing an endpoint for accessing each column? Thanks.
A simple way to solve this, is to use a request param instead of querying for the URL. Using a param like fields you would have an URL like /employee/{id}?fields=FirstName,LastName. Using the code below you could have a Map<String, Object> that would be serialized to a JSON with your data. Like this:
#ResponseBody
public Map<String, Object> getPerson(#PathVariable("id") long id, #RequestParam("fields") String fields) throws Exception {
return personService.getPersonFields(id, fields);
}
#Service
class PersonService {
public Map<String, Object> getPersonFields(Long personId, String fields) throws Exception {
final Person person = personRepository.findById(personId);
if (person == null) {
throw new Exception("Person does not exists!");
}
String[] fieldsArray = fields.split(",");
Map<String, Field> personFields = Arrays.stream(person.getClass().getFields()).collect(Collectors.toMap(Field::getName, field -> field);
Map<String, Object> personFieldsReturn = new HashMap<>();
for (String field : fieldsArray) {
if (personFields.containsKey(field)) {
personFields.get(field).setAccessible(true);
personFieldsReturn.put(field, personFields.get(field).get(person));
personFields.get(field).setAccessible(false);
}
}
return personFieldsReturn;
}
}
This is not a good solution though. But it should work.
So as #dave mentioned in a comment you can have a REST endpoint /employee/{ID}/{column} and in your controller you will have a mapping between value of {column} argument and actual column name in database. If you do not want to redeploy your application when mapping changes you can put it in a separate properties file on a server outside of your jar/war and you can also add an additional endpoint to either reload mapping form file on a server or an endpoint that will allow to upload and parse a file with mapping directly to your application.
I would suggest you to use RepositoryRestResource from Spring Data.
First of all, create your entity:
public class Employee {
//props
}
Afterthat create Employee Repository:
#RepositoryRestResource(collectionResourceRel = "employee", path = "employee")
public interface EmployeeRepository extends PagingAndSortingRepository<Employee, Long> {
List<Employee> findByLastName(#Param("firstName") String firstName);
}
And that's all, you will get:
discoverable REST API for your domain model using HAL as media type.
collection, item and association resources representing your mode.
paginating and sorting
and so on.
Check out the docs:
Spring Guide
Spring Dc
Spring Data Rest Project

Spring bind application properties Map<MyEnum, List<CustomObject>>

I'm trying to bind application properties to a Map<MyEnum, List<CustomObject>> in Spring.
I'm aware of following bindings for maps:
foo.keyValueMap.EnumKey=value
foo.keyListValueMap.EnumKey=value1,value2
which respectively creates a Map<EnumKey, String> keyValueMap and Map<EnumKey, List<String>> keyListValueMap.
What I try to accomplish is as the titles says, a map Map<MyEnum, List<CustomObject>>, whereCustomObject having multiple properties including a Set.
class CustomObject {
private Set<String> set;
private String property;
// ...
}

Inject all keys and Values from property file as Map in Spring

Can someone provide some idea to inject all dynamic keys and values from property file and pass it as Map to DBConstants class using Setter Injection with Collection.
Keys are not known in advance and can vary.
// Example Property File that stores all db related details
// db.properties
db.username.admin=root
db.password.admin=password12
db.username.user=admin
db.password.user=password13
DBConstants contains map dbConstants for which all keys and values need to be injected.
Please provide bean definition to inject all keys and values to Map dbConstants.
public class DBConstants {
private Map<String,String> dbConstants;
public Map<String, String> getDbConstants() {
return dbConstants;
}
public void setDbConstants(Map<String, String> dbConstants) {
this.dbConstants = dbConstants;
}
}
You can create PropertiesFactoryBean with your properties file and then inject it with #Resource annotation where you want to use it as a map.
#Bean(name = "myProperties")
public static PropertiesFactoryBean mapper() {
PropertiesFactoryBean bean = new PropertiesFactoryBean();
bean.setLocation(new ClassPathResource("prop_file_name.properties"));
return bean;
}
Usage:
#Resource(name = "myProperties")
private Map<String, String> myProperties;
you can use #Value.
Properties file:
dbConstants={key1:'value1',key2:'value2'}
Java code:
#Value("#{${dbConstants}}")
private Map<String,String> dbConstants;
you have to give spaces its like
hash.key = {indoor: 'reading', outdoor: 'fishing'}
Read map like below as i mentioned.
#Value("#{${hash.key}}")
private Map<String, String> hobbies;

Categories

Resources