I have an entity with a private field countryCode. I want to add a convenient method in my entity class for setting the country code: it's possible to set the country code either with an CountryCode object or a string.
If the country code is set by string, a CountryCode repository is necessary. However, I can't get Spring to initialize the repository field. Even if I put #Component and #Scope("prototype") on my entity...
What am I missing ?
private CountryCode countryCode;
public void setCountryCode(String code) {
this.countryCode = getByCode(code);
}
#Autowired
#Transient
private CountryCodeRepository countryCodeRepository;
private CountryCode getByCode(String code) {
if (code == null) {
throw new NullPointerException("The country code cannot be null.");
}
// countryCodeRepository is NULL below...
CountryCode finalCC = countryCodeRepository.findByAlpha2OrAlpha3(code);
// ...
}
I guess your entity class is instantiated by some ORM framework and therefore not instantiated by spring. So spring can not autowire the fields automatically.
If so you have these option
Using AspectJ to dependency inject domain objects with Spring
if you are using hibernate you can write an PostLoadListener which also implements ApplicationContextAware
get the AutowireCapableBeanFactory and autowire the loaded entities on post load.
Please provide more information.
Related
I'm using #Value to inject parameters from my properties file to variable in my application.
Because I'm not the only user in the application and I want to make the injection safety I need to validate the parameter before the injection.
Example:
properties file:
example.string=str1,str2
app.java
#Value(${example.string})
public String example;
the expected behavior in this case for example is to throw an exception because I assume "," id delimiter in array case
I don't think you can directly with #Value. But you could do something like this, which will fail on startup if validation fails:
#Validated
#ConfigurationProperties(prefix="my.prefix")
public class AppProperties {
//Validation annotations here
//#NotEmpty
//#MyCustomValidation
private String exampleString;
// getters / setters
}
I don't think you can do this before the injection, try to use the post construct method
you can do some thing like that :
#PostConstruct
public void validateValue() {
if (someProperty.contains(",")) {
throw new MyException("error");
}
}
I know similar questions have been asked so many times here before, but I am still confused by the mechanisms.
Here is my problem. There is a null pointer exception coming from the CategoryDAO object in the CategoryService.
#Service
public class CategoryService {
#Autowired
private CategoryDAO categoryDAO;
public List<Category> list(){
List<Category> categories = categoryDAO.list();
for (Category category : categories){
List<Record> rs = recordDAO.list(category.getID());
category.setRecordNumber(rs.size());
}
return categories;
}
public void add(String name){
Category newCategory = new Category();
newCategory.setName(name);
categoryDAO.add(newCategory);
}
}
#Repository
public class CategoryDAO {
#Autowired
private SqlSessionFactory sqlSessionFactory;
public int getTotal(){
SqlSession sqlSession = sqlSessionFactory.openSession();
List<Category> categories = sqlSession.selectList("category.selectAll");
return categories.size();
}
}
In this top rated post and this one, both of the top answers mentioned that The most preferable option is to let Spring autowire all of your beans.
Does it mean I have to also autowire the CategoryService in other classes once I need it? Which means I cannot use new operator to initialise a class if it contains autowired object?
If yes, could you please explain the reason behind it?
Thanks
UPDATE
Here is an example about using the autowired class CategoryService:
public class RecordListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
RecordPanel panel = RecordPanel.getInstance();
if (new CategoryService().list().size() == 0){
JOptionPane.showMessageDialog(panel, "NO category is recorded, set category first");
MainPanel.getInstance().getPanel().display(CategoryPanel.getInstance());
return;
}
}
The CategoryService is used in new CategoryService().list().size() == 0. If I autowire it as a property of this class here then this class will also need to be injected once I need it. I would like to avoid that so things could be easier. How can I achieve that?
Does it mean I have to also autowire the CategoryService in other classes once I need it?
Yes.
Which means I cannot use new operator to initialise a class if it contains autowired object?
Yes.
The #Autowire annotation enables you to use Dependency Injection. A technique (or good practice, actually) that makes it easy to change the implementations you use for your interfaces in your application. You define beans/component/services that will get injected whenever you use the #Autowire annotation over an attribute or a constructor parameter.
Instead of using new all over your code you just declare which concrete class should be used for an interface (or maybe the class itself) annotated with #Autowire.
Imagine you create an interface RemoteAccess and an implementation FtpRemoteAccess and then every time you need it you write RemoteAccess remoteAccess = new FtpRemoteAccess();
After a while you might end up with that line over several places. Now, if you need to change this to HttpRemoteAccess because you have this new, better alternative, you have to review all your code base. Instead, if you used dependency injection you would just change the bean (there is more than one way to do that using Spring).
For all this to work, Spring must be able to inject all the dependencies of a bean. If you create a bean, all its attributes must be injected too because Spring will create that object for you.
Clarification:
Inside your bean (namely, you classes that will be injected) you can create objects using new provided that makes sense and those object are not injected types. You are already doing that in CategoryService::add() and it is ok. Dependency injection doesn't mean you will not ever write new again. You will just avoid it for objects that will be managed by Spring dependency injection.
Then, there are other good practices that disencourage using new like the static factory method that recommend putting a static method in your class to build complete objects and letting the constructor to be private. But you don't need to apply all the patterns all the time.
UPDATE:
For your RecordListener class you have to add a CategoryService attribute and then be sure it is initialized. There are two options: you can convert RecordListener in a bean itself and have it autowired where you need that. This way Spring will construct a RecordListener object for injecting it and will also add any other bean that is needed (like CategoryService)
#Component
public class RecordListener implements ActionListener {
#Autowire
private CategoryService categoryService;
#Override
public void actionPerformed(ActionEvent e) {
RecordPanel panel = RecordPanel.getInstance();
if (categoryService.list().size() == 0) {
JOptionPane.showMessageDialog(panel, "NO category is recorded, set category first");
MainPanel.getInstance().getPanel().display(CategoryPanel.getInstance());
return;
}
}
}
The other option is you inject CategoryService in the class that is currently creating the RecordListener and then pass it as constructor argument. In that case RecordListener will not be a bean.
I have an annotation set over objects of type dto, the same as over objects of type Entity. The annotation works on entities, but it does not work on objects of type dto.
I work in SpringBoot.
application.properties
validate.packageid.size = "The field 'PACKAGEID' can contain only {max} symbols.";
config file
#Configuration
public class ServiceConfig implements WebMvcConfigurer {
#Bean
public MessageSource messageSource() {
ResourceBundleMessageSource source = new ResourceBundleMessageSource();
source.setDefaultEncoding("UTF-8");
source.setBasename("classpath:ValidationMessages");
return source;
}
#Nullable
#Override
public Validator getValidator() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setValidationMessageSource(messageSource());
return validator;
}
}
dto
#Size(message = "{validate.packageid.size}", max = 36)
private String documentId
entity
#Column(name = "DOCUMENTID")
#Size(message = "{validate.packageid.size}", max = 36)
private String documentId;
I cannot use the annotation #Valid because I fill an object of dto type with reflection technology.
public static <S> S fillData (S object, List<Object> values){
return obtainMetadataOfObject(object, values);
}
I need to be able to get the constraint annotation, or rather its parameters, set on the fields of the dto object (but in the case of the dto object I get null, since Spring may not know what to use the constraint annotations set on the fields of the dto object), in the case of entity - it turns out, but the entity validator is engaged by Spritg, since it manages the entity as a component from the Application context.
To validate the dto on the web client side, I use the #Valid annotation in the parameters of the method that handles the request from the client
For validation dto from
Update
I put the annotation #Validation over dto and after that I got the data I need.
It work for classes that don't have classes-heir.
I get data of annotation #Size
private static int getMaxLimitSize(Field field){
field.setAccessible(true);
Size annotation = field.getAnnotation(Size.class);
int zero = 0;
if(annotation == null) return zero;
return annotation.max();
}
But this does not work for objects whose fields are split into several classes : several abstract and one produce.
Validation does not work for composite objects of type DTO, Any help is appreciated?
The validation needs to be triggered somewhere, for entities in your case the spring framework does it (or jpa perhaps). DTOs never make it there. So you have to trigger the validation (validator.validate) on your own, as per the documentation. Here's a related question asking at which application layer to do it.
#PostMapping("/test")
public void test( #RequestBody #Valid final UserDto dto) {
// logic
}
u should add #Valid annotation in controller.
If u want validate dto in service layers u should add #Validate and #Valid :
#Service
#Validated
public class Service {
public void test(#Valid UserDto dto){
// logic
}
}
I am using AWS ECS to host my application and using DynamoDB for all database operations. So I'll have same database with different table names for different environments. Such as "dev_users" (for Dev env), "test_users" (for Test env), etc.. (This is how our company uses same Dynamo account for different environments)
So I would like to change the "tableName" of the model class using the environment variable passed through "AWS ECS task definition" environment parameters.
For Example.
My Model Class is:
#DynamoDBTable(tableName = "dev_users")
public class User {
Now I need to replace the "dev" with "test" when I deploy my container in test environment. I know I can use
#Value("${DOCKER_ENV:dev}")
to access environment variables. But I'm not sure how to use variables outside the class. Is there any way that I can use the docker env variable to select my table prefix?
My Intent is to use like this:
I know this not possible like this. But is there any other way or work around for this?
Edit 1:
I am working on the Rahul's answer and facing some issues. Before writing the issues, I'll explain the process I followed.
Process:
I have created the beans in my config class (com.myapp.users.config).
As I don't have repositories, I have given my Model class package name as "basePackage" path. (Please check the image)
For 1) I have replaced the "table name over-rider bean injection" to avoid the error.
For 2) I printed the name that is passing on to this method. But it is Null. So checking all the possible ways to pass the value here.
Check the image for error:
I haven't changed anything in my user model class as beans will replace the name of the DynamoDBTable when the beans got executed. But the table name over riding is happening. Data is pulling from the table name given at the Model Class level only.
What I am missing here?
The table names can be altered via an altered DynamoDBMapperConfig bean.
For your case where you have to Prefix each table with a literal, you can add the bean as such. Here the prefix can be the environment name in your case.
#Bean
public TableNameOverride tableNameOverrider() {
String prefix = ... // Use #Value to inject values via Spring or use any logic to define the table prefix
return TableNameOverride.withTableNamePrefix(prefix);
}
For more details check out the complete details here:
https://github.com/derjust/spring-data-dynamodb/wiki/Alter-table-name-during-runtime
I am able to achieve table names prefixed with active profile name.
First added TableNameResolver class as below,
#Component
public class TableNameResolver extends DynamoDBMapperConfig.DefaultTableNameResolver {
private String envProfile;
public TableNameResolver() {}
public TableNameResolver(String envProfile) {
this.envProfile=envProfile;
}
#Override
public String getTableName(Class<?> clazz, DynamoDBMapperConfig config) {
String stageName = envProfile.concat("_");
String rawTableName = super.getTableName(clazz, config);
return stageName.concat(rawTableName);
}
}
Then i setup DynamoDBMapper bean as below,
#Bean
#Primary
public DynamoDBMapper dynamoDBMapper(AmazonDynamoDB amazonDynamoDB) {
DynamoDBMapper mapper = new DynamoDBMapper(amazonDynamoDB,new DynamoDBMapperConfig.Builder().withTableNameResolver(new TableNameResolver(envProfile)).build());
return mapper;
}
Added variable envProfile which is an active profile property value accessed from application.properties file.
#Value("${spring.profiles.active}")
private String envProfile;
We have the same issue with regards to the need to change table names during runtime. We are using Spring-data-dynamodb 5.0.2 and the following configuration seems to provide the solutions that we need.
First I annotated my bean accessor
#EnableDynamoDBRepositories(dynamoDBMapperConfigRef = "getDynamoDBMapperConfig", basePackages = "my.company.base.package")
I also setup an environment variable called ENV_PREFIX which is Spring wired via SpEL.
#Value("#{systemProperties['ENV_PREFIX']}")
private String envPrefix;
Then I setup a TableNameOverride bean:
#Bean
public DynamoDBMapperConfig.TableNameOverride getTableNameOverride() {
return DynamoDBMapperConfig.TableNameOverride.withTableNamePrefix(envPrefix);
}
Finally, I setup the DynamoDBMapperConfig bean using TableNameOverride injection. In 5.0.2, we had to setup a standard DynamoDBTypeConverterFactory in the DynamoDBMapperConfig builder to avoid NPE.:
#Bean
public DynamoDBMapperConfig getDynamoDBMapperConfig(DynamoDBMapperConfig.TableNameOverride tableNameOverride) {
DynamoDBMapperConfig.Builder builder = new DynamoDBMapperConfig.Builder();
builder.setTableNameOverride(tableNameOverride);
builder.setTypeConverterFactory(DynamoDBTypeConverterFactory.standard());
return builder.build();
}
In hind sight, I could have setup a DynamoDBTypeConverterFactory bean that returns a standard DynamoDBTypeConverterFactory and inject that into the getDynamoDBMapperConfig() method using the DynamoDBMapperConfig builder. But this will also do the job.
I up voted the other answer but here is an idea:
Create a base class with all your user details:
#MappedSuperclass
public abstract class AbstractUser {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
Create 2 implentations with different table names and spirng profiles:
#Profile(value= {"dev","default"})
#Entity(name = "dev_user")
public class DevUser extends AbstractUser {
}
#Profile(value= {"prod"})
#Entity(name = "prod_user")
public class ProdUser extends AbstractUser {
}
Create a single JPA respository that uses the mapped super classs
public interface UserRepository extends CrudRepository<AbstractUser, Long> {
}
Then switch the implentation with the spring profile
#RunWith(SpringJUnit4ClassRunner.class)
#DataJpaTest
#Transactional
public class UserRepositoryTest {
#Autowired
protected DataSource dataSource;
#BeforeClass
public static void setUp() {
System.setProperty("spring.profiles.active", "prod");
}
#Test
public void test1() throws Exception {
DatabaseMetaData metaData = dataSource.getConnection().getMetaData();
ResultSet tables = metaData.getTables(null, null, "PROD_USER", new String[] { "TABLE" });
tables.next();
assertEquals("PROD_USER", tables.getString("TABLE_NAME"));
}
}
Good evening, I'm trying to use Hibernate Validator, in the following scenario:
public class Car {
#NotNull
private String manufacturer;
#NotNull
#Size(min = 2, max = 14)
private String licensePlate;
#Min(2)
private int seatCount;
//setters and getters....
}
and I am trying to validate its attributes as follows:
public class CarMain {
public static Validator validator;
public static void main(String[] args) {
ValidatorFactory factory = Validation. buildDefaultValidatorFactory() ;
validator = factory. getValidator();
Car car = new Car(null,null,0);
Set<ConstraintViolation<Car>> st= validator.validate(car);
while(st.iterator.hasNext()){
ConstraintViolation<Car> cv = st.iterator.next();
System.out.println("Value: ("+cv.getInvalidValue()+") -->"+cv.getMessage());
System.out.println("Attribute: "+cv.getPropertyPath());
}
}
Here the whole entity is validated and the invalid values with the validation message and property path are displayed.
My question is:"Is it possible to validate only one attribute at a time with Hibernate Validator? Like I don't have to work with the whole object to validate it.
The Validator interface defines also a [Validator.validateProperty][1] method where you explicitly specify the property to validate. Mind you, you still need the object instance and you need to know the property you want to validate. This method is for example used by the integration of Bean Validation into JSF. Whether it makes sense to use it inm your case, will depend on your use case? Why don't you want to validate the whole object?
BTW, there is also Validator.validateValue which does not require an actual bean instance.