Spring data redis sort/order by - spring

I'm currently working on a project which require redis as its main repository for caching data, using references from spring-data-redis-example
i'm also require to do things like get and sort the list retrieved from redis, to achieve such cases, what i have tried is like below :
PageRequest.of(1, 10, Sort.Direction.ASC, "rank")
and
findBySomeAndThingOrderByRankAsc
but none of the above seem to resolve my issue to be able to sort the data retrieved from redis
here is my redis configuration
#Configuration
#EnableRedisRepositories(basePackages = "com.example.db.redis.repository")
public class RedisConfiguration {
#Bean
JedisConnectionFactory jedisConnectionFactory() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(Properties.getString("redis.hostname"));
redisStandaloneConfiguration.setPort(Integer.parseInt(Properties.getString("redis.port")));
return new JedisConnectionFactory(redisStandaloneConfiguration);
}
#Bean
public RedisTemplate<?, ?> redisTemplate() {
RedisTemplate<?, ?> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(jedisConnectionFactory());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericToStringSerializer<>(Object.class));
return redisTemplate;
}
#Bean
public StringRedisTemplate strRedisTemplate() {
StringRedisTemplate redisTemplate = new StringRedisTemplate();
redisTemplate.setConnectionFactory(jedisConnectionFactory());
return redisTemplate;
}
}
and here is the pojo
#RedisHash("somehash")
public class SortOnRedisExample implements Serializable {
private static final long serialVersionUID = -895893308381522209L;
#Id
private String id;
#Indexed
private String some;
#Indexed
private String thing;
#Indexed
private Integer rank;
public SortOnRedisExample() {}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Integer getRank() {
return rank;
}
public void setRank(Integer rank) {
this.rank = rank;
}
public String getSome() {
return some;
}
public void setSome(String some) {
this.some = some;
}
public String getThing() {
return thing;
}
public void setThing(String thing) {
this.thing = thing;
}
}
my question is, is sort/order by supported by spring-data-redis in jpa style? if not, what is the alternative to do so?
any pointer would be highly appreciated, thanks!

Related

#RefreshScope annotated Bean registered through BeanDefinitionRegistryPostProcessor not getting refreshed on Cloud Config changes

I've a BeanDefinitionRegistryPostProcessor class that registers beans dynamically. Sometimes, the beans being registered have the Spring Cloud annotation #RefreshScope.
However, when the cloud configuration Environment is changed, such beans are not being refreshed. Upon debugging, the appropriate application events are triggered, however, the dynamic beans don't get reinstantiated. Need some help around this. Below is my code:
TestDynaProps:
public class TestDynaProps {
private String prop;
private String value;
public String getProp() {
return prop;
}
public void setProp(String prop) {
this.prop = prop;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("TestDynaProps [prop=").append(prop).append(", value=").append(value).append("]");
return builder.toString();
}
}
TestDynaPropConsumer:
#RefreshScope
public class TestDynaPropConsumer {
private TestDynaProps props;
public void setProps(TestDynaProps props) {
this.props = props;
}
#PostConstruct
public void init() {
System.out.println("Init props : " + props);
}
public String getVal() {
return props.getValue();
}
}
BeanDefinitionRegistryPostProcessor:
public class PropertyBasedDynamicBeanDefinitionRegistrar implements BeanDefinitionRegistryPostProcessor, EnvironmentAware {
private ConfigurableEnvironment environment;
private final Class<?> propertyConfigurationClass;
private final String propertyBeanNamePrefix;
private final String propertyKeysPropertyName;
private Class<?> propertyConsumerBean;
private String consumerBeanNamePrefix;
private List<String> dynaBeans;
public PropertyBasedDynamicBeanDefinitionRegistrar(Class<?> propertyConfigurationClass,
String propertyBeanNamePrefix, String propertyKeysPropertyName) {
this.propertyConfigurationClass = propertyConfigurationClass;
this.propertyBeanNamePrefix = propertyBeanNamePrefix;
this.propertyKeysPropertyName = propertyKeysPropertyName;
dynaBeans = new ArrayList<>();
}
public void setPropertyConsumerBean(Class<?> propertyConsumerBean, String consumerBeanNamePrefix) {
this.propertyConsumerBean = propertyConsumerBean;
this.consumerBeanNamePrefix = consumerBeanNamePrefix;
}
#Override
public void setEnvironment(Environment environment) {
this.environment = (ConfigurableEnvironment) environment;
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0) throws BeansException {
}
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefRegistry) throws BeansException {
if (environment == null) {
throw new BeanCreationException("Environment must be set to initialize dyna bean");
}
String[] keys = getPropertyKeys();
Map<String, String> propertyKeyBeanNameMapping = new HashMap<>();
for (String k : keys) {
String trimmedKey = k.trim();
String propBeanName = getPropertyBeanName(trimmedKey);
registerPropertyBean(beanDefRegistry, trimmedKey, propBeanName);
propertyKeyBeanNameMapping.put(trimmedKey, propBeanName);
}
if (propertyConsumerBean != null) {
String beanPropertyFieldName = getConsumerBeanPropertyVariable();
for (Map.Entry<String, String> prop : propertyKeyBeanNameMapping.entrySet()) {
registerConsumerBean(beanDefRegistry, prop.getKey(), prop.getValue(), beanPropertyFieldName);
}
}
}
private void registerConsumerBean(BeanDefinitionRegistry beanDefRegistry, String trimmedKey, String propBeanName, String beanPropertyFieldName) {
String consumerBeanName = getConsumerBeanName(trimmedKey);
AbstractBeanDefinition consumerDefinition = preparePropertyConsumerBeanDefinition(propBeanName, beanPropertyFieldName);
beanDefRegistry.registerBeanDefinition(consumerBeanName, consumerDefinition);
dynaBeans.add(consumerBeanName);
}
private void registerPropertyBean(BeanDefinitionRegistry beanDefRegistry, String trimmedKey, String propBeanName) {
AbstractBeanDefinition propertyBeanDefinition = preparePropertyBeanDefinition(trimmedKey);
beanDefRegistry.registerBeanDefinition(propBeanName, propertyBeanDefinition);
dynaBeans.add(propBeanName);
}
private String getConsumerBeanPropertyVariable() throws IllegalArgumentException {
Field[] beanFields = propertyConsumerBean.getDeclaredFields();
for (Field bField : beanFields) {
if (bField.getType().equals(propertyConfigurationClass)) {
return bField.getName();
}
}
throw new BeanCreationException(String.format("Could not find property of type %s in bean class %s",
propertyConfigurationClass.getName(), propertyConsumerBean.getName()));
}
private AbstractBeanDefinition preparePropertyBeanDefinition(String trimmedKey) {
BeanDefinitionBuilder bdb = BeanDefinitionBuilder.genericBeanDefinition(PropertiesConfigurationFactory.class);
bdb.addConstructorArgValue(propertyConfigurationClass);
bdb.addPropertyValue("propertySources", environment.getPropertySources());
bdb.addPropertyValue("conversionService", environment.getConversionService());
bdb.addPropertyValue("targetName", trimmedKey);
return bdb.getBeanDefinition();
}
private AbstractBeanDefinition preparePropertyConsumerBeanDefinition(String propBeanName, String beanPropertyFieldName) {
BeanDefinitionBuilder bdb = BeanDefinitionBuilder.genericBeanDefinition(propertyConsumerBean);
bdb.addPropertyReference(beanPropertyFieldName, propBeanName);
return bdb.getBeanDefinition();
}
private String getPropertyBeanName(String trimmedKey) {
return propertyBeanNamePrefix + trimmedKey.substring(0, 1).toUpperCase() + trimmedKey.substring(1);
}
private String getConsumerBeanName(String trimmedKey) {
return consumerBeanNamePrefix + trimmedKey.substring(0, 1).toUpperCase() + trimmedKey.substring(1);
}
private String[] getPropertyKeys() {
String keysProp = environment.getProperty(propertyKeysPropertyName);
return keysProp.split(",");
}
The Config class:
#Configuration
public class DynaPropsConfig {
#Bean
public PropertyBasedDynamicBeanDefinitionRegistrar dynaRegistrar() {
PropertyBasedDynamicBeanDefinitionRegistrar registrar = new PropertyBasedDynamicBeanDefinitionRegistrar(TestDynaProps.class, "testDynaProp", "dyna.props");
registrar.setPropertyConsumerBean(TestDynaPropConsumer.class, "testDynaPropsConsumer");
return registrar;
}
}
Application.java
#SpringBootApplication
#EnableDiscoveryClient
#EnableScheduling
public class Application extends SpringBootServletInitializer {
private static Class<Application> applicationClass = Application.class;
public static void main(String[] args) {
SpringApplication sa = new SpringApplication(applicationClass);
sa.run(args);
}
}
And, my bootstrap.properties:
spring.cloud.consul.enabled=true
spring.cloud.consul.config.enabled=true
spring.cloud.consul.config.format=PROPERTIES
spring.cloud.consul.config.watch.delay=15000
spring.cloud.discovery.client.health-indicator.enabled=false
spring.cloud.discovery.client.composite-indicator.enabled=false
application.properties
dyna.props=d1,d2
d1.prop=d1prop
d1.value=d1value
d2.prop=d2prop
d2.value=d2value
Here are some guesses:
1) Perhaps the #RefreshScope metadata is not being passed to your metadata for the bean definition. Call setScope()?
2) The RefreshScope is actually implemented by https://github.com/spring-cloud/spring-cloud-commons/blob/master/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/refresh/RefreshScope.java, which itself implements BeanDefinitionRegistryPostProcessor. Perhaps the ordering of these two post processors is issue.
Just guesses.
We finally resolved this by appending the #RefreshScope annotation on the proposed dynamic bean classes using ByteBuddy and then, adding them to Spring Context using Bean Definition Post Processor.
The Post Processor is added to spring.factories so that it loads before any other dynamic bean dependent beans.

Java Spring 4 (Annotated) Rest Controller not being hit by REST Client tool in Firefox

Hi,
I have a problem that is very confusing for me because the mapping should work and it looks like it does map when the Spring Boot is started in debug mode. I don't know where else I can check for an obvious solution to this problem.
Here is the application.properties:
server.port=8082
server.contextPath = /
Here is the SpringBootInitializer class that adds a further "/api" to the >Servlet registration:
public class App extends SpringBootServletInitializer {
#Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
#Bean
public ServletRegistrationBean dispatcherServletRegistration() {
final ServletRegistrationBean registration = new ServletRegistrationBean(dispatcherServlet(), "/api/*");
final Map<String, String> params = new HashMap<String, String>();
params.put("contextClass", "org.springframework.web.context.support.AnnotationConfigWebApplicationContext");
params.put("contextConfigLocation", "org.spring.sec2.spring");
params.put("dispatchOptionsRequest", "true");
registration.setInitParameters(params);
registration.setLoadOnStartup(1);
return registration;
}
//
#Override
protected SpringApplicationBuilder configure(final SpringApplicationBuilder application) {
return application.initializers(new MyApplicationContextInitializer()).sources(App.class);
}
public static void main(final String... args) {
new SpringApplicationBuilder(App.class).initializers(new MyApplicationContextInitializer()).run(args);
}
}
Here is the Controler which adds a further "users" to the mapping. The method >which I have set a debug point is the findAll and requires no futher mapping to >get to it (i.e. the root of /users/:
#Controller
#RequestMapping(value = users)
public class UserController extends AbstractController<User> {
#Autowired
private IUserService userService;
public UserController() {
super(User.class);
}
// API
// find
#RequestMapping(method = RequestMethod.GET)
#ResponseBody
public void getItsWorking() {
System.out.println("It's Working!!!");
}
}
Here is the User entity:
#Entity
public class User implements IEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="user_id")
private Long user_id;
#Column(name = "username", unique = true, nullable = false)
private String name;
#Column(unique = true, nullable = false)
private String email;
#Column(nullable = false)
private String password;
#Column(nullable = false)
private Boolean locked;
public User() {
super();
}
public User(final String nameToSet, final String passwordToSet, /*final
Set<Role> rolesToSet,*/ final Boolean lockedToSet) {
super();
name = nameToSet;
password = passwordToSet;
locked = lockedToSet;
}
// API
public Long getId() {
return user_id;
}
public void setId(final Long idToSet) {
user_id = idToSet;
}
public String getName() {
return name;
}
public void setName(final String nameToSet) {
name = nameToSet;
}
public String getEmail() {
return email;
}
public void setEmail(final String emailToSet) {
email = emailToSet;
}
public String getPassword() {
return password;
}
public void setPassword(final String passwordToSet) {
password = passwordToSet;
}
public Boolean getLocked() {
return locked;
}
public void setLocked(final Boolean lockedToSet) {
locked = lockedToSet;
}
}
Here is the output on my Spring Boot debug when it starts up:
Mapped "{[/users],methods=[GET]}" onto public
java.util.List<org.um.persistence.model.User>
org.um.web.controller.UserController.findAll(javax.servlet.http.HttpServletRequest)
So, it looks like it is mapping correctly, but when I hit it using the Rest >Client tool add on in Firefox, I get the following when doing a "GET" on the >following url: http://localhost:8082/api/users using Content-Type: application/json in my header .
What is going on? Very confused.
You should put a #RequestMapping("/api") on you class, and a #RequestMapping("/users") on your method (that should preferably return something to the client).
This ways your endpoint will be exposed as /api/users and you will be able to easily add further endpoints under /api/* into this class.

Axon Event Handler not Working

I am developing a small cqrs implementation and I am very new to it.
I want to segregate each handlers(Command and Event) from aggregate and
make sure all are working well. The command handler are getting triggered
from controller but from there event handlers are not triggered. Could
anyone Please help on this.
public class User extends AbstractAnnotatedAggregateRoot<String> {
/**
*
*/
private static final long serialVersionUID = 1L;
#AggregateIdentifier
private String userId;
private String userName;
private String age;
public User() {
}
public User(String userid) {
this.userId=userid;
}
#Override
public String getIdentifier() {
return this.userId;
}
public void createuserEvent(UserCommand command){
apply(new UserEvent(command.getUserId()));
}
#EventSourcingHandler
public void applyAccountCreation(UserEvent event) {
this.userId = event.getUserId();
}
}
public class UserCommand {
private final String userId;
public UserCommand(String userid) {
this.userId = userid;
}
public String getUserId() {
return userId;
}
}
#Component
public class UserCommandHandler {
#CommandHandler
public void userCreateCommand(UserCommand command) {
User user = new User(command.getUserId());
user.createuserEvent(command);
}
}
public class UserEvent {
private final String userId;
public UserEvent(String userid) {
this.userId = userid;
}
public String getUserId() {
return userId;
}
}
#Component
public class UserEventHandler {
#EventHandler
public void createUser(UserEvent userEvent) {
System.out.println("Event triggered");
}
}
#Configuration
#AnnotationDriven
public class AppConfiguration {
#Bean
public SimpleCommandBus commandBus() {
SimpleCommandBus simpleCommandBus = new SimpleCommandBus();
return simpleCommandBus;
}
#Bean
public Cluster normalCluster() {
SimpleCluster simpleCluster = new SimpleCluster("simpleCluster");
return simpleCluster;
}
#Bean
public ClusterSelector clusterSelector() {
Map<String, Cluster> clusterMap = new HashMap<>();
clusterMap.put("com.user.event.handler", normalCluster());
//clusterMap.put("exploringaxon.replay", replayCluster());
return new ClassNamePrefixClusterSelector(clusterMap);
}
#Bean
public EventBus clusteringEventBus() {
ClusteringEventBus clusteringEventBus = new ClusteringEventBus(clusterSelector(), terminal());
return clusteringEventBus;
}
#Bean
public EventBusTerminal terminal() {
return new EventBusTerminal() {
#Override
public void publish(EventMessage... events) {
normalCluster().publish(events);
}
#Override
public void onClusterCreated(Cluster cluster) {
}
};
}
#Bean
public DefaultCommandGateway commandGateway() {
return new DefaultCommandGateway(commandBus());
}
#Bean
public Repository<User> eventSourcingRepository() {
EventStore eventStore = new FileSystemEventStore(new SimpleEventFileResolver(new File("D://sevents.txt")));
EventSourcingRepository eventSourcingRepository = new EventSourcingRepository(User.class, eventStore);
eventSourcingRepository.setEventBus(clusteringEventBus());
AnnotationEventListenerAdapter.subscribe(new UserEventHandler(), clusteringEventBus());
return eventSourcingRepository;
}
}
As far as I can tell, the only thing missing is that you aren't adding the User Aggregate to a Repository. By adding it to the Repository, the User is persisted (either by storing the generated events, in the case of Event Sourcing, or its state otherwise) and all Events generated by the Command Handler (including the Aggregate) are published to the Event Bus.
Note that the Aggregate's #EventSourcingHandlers are invoked immediately, but any external #EventHandlers are only invoked after the command handler has been executed.

Spring Data Jpa Test returns null list even after child is saved

#Test
public void testAddPlayerToGame() {
gameRepository.save(createTestGame());
Game game = gameRepository.findOne(1l);
assertTrue(game.getId() > 0);
Player p = new Player();
p.setName("test 1");
p.setGame(game);
p.setChips(5000);
assertTrue(p.getId() == null);
playerRepository.saveAndFlush(p);
assertTrue(p.getId() != null);
flushAndClear();
Game game2 = gameRepository.findOne(1l);
assertEquals(1, game2.getPlayers().size());
}
The above test fails because game2.getPlayers() returns null.
Already went through JpaRepository caches newly created object. How to refresh it? but couldn't figure out how to solve.
The method flushAndClear used in the above code is blank, as follows :
protected void flushAndClear() {
// sessionFactory.getCurrentSession().flush();
// sessionFactory.getCurrentSession().clear();
}
Any help is really appreciated.
Update
Game & Player mapping code :
#Entity
#Table(name="game")
public class Game implements Serializable {
private static final long serialVersionUID = -495064662454346171L;
private long id;
private int playersRemaining;
private Player playerInBTN;
private GameType gameType;
private String name;
private boolean isStarted;
private Set<Player> players;
private HandEntity currentHand;
private GameStructure gameStructure;
#Column(name="game_id")
#Id
#GeneratedValue(strategy=GenerationType.TABLE)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#Column(name="players_left")
public int getPlayersRemaining() {
return playersRemaining;
}
public void setPlayersRemaining(int playersRemaining) {
this.playersRemaining = playersRemaining;
}
#OneToOne
#JoinColumn(name="btn_player_id")
public Player getPlayerInBTN(){
return playerInBTN;
}
public void setPlayerInBTN(Player playerInBTN){
this.playerInBTN = playerInBTN;
}
#Column(name="game_type")
#Enumerated(EnumType.STRING)
public GameType getGameType() {
return gameType;
}
public void setGameType(GameType gameType) {
this.gameType = gameType;
}
#OneToMany(mappedBy="game", fetch=FetchType.LAZY)
public Set<Player> getPlayers() {
return players;
}
public void setPlayers(Set<Player> players) {
this.players = players;
}
#Column(name="name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Column(name="is_started")
public boolean isStarted() {
return isStarted;
}
public void setStarted(boolean isStarted) {
this.isStarted = isStarted;
}
#OneToOne(fetch=FetchType.EAGER)
#JoinColumn(name="current_hand_id")
public HandEntity getCurrentHand() {
return currentHand;
}
public void setCurrentHand(HandEntity currentHand) {
this.currentHand = currentHand;
}
#OneToOne(fetch=FetchType.EAGER, cascade={CascadeType.ALL})
#JoinColumn(name="game_structure_id")
public GameStructure getGameStructure() {
return gameStructure;
}
public void setGameStructure(GameStructure gameStructure) {
this.gameStructure = gameStructure;
}
}
#Entity
#Table(name="player")
public class Player implements Comparable<Player>, Serializable{
private static final long serialVersionUID = -1384636077333014255L;
private String id;
private Game game;
private String name;
private int chips;
private int gamePosition;
private int finishPosition;
private boolean sittingOut;
#JsonIgnore
#Column(name="player_id")
#Id
#GeneratedValue(generator = "system-uuid")
#GenericGenerator(name = "system-uuid", strategy = "uuid2")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#JsonIgnore
#ManyToOne
#JoinColumn(name="game_id")
public Game getGame() {
return game;
}
public void setGame(Game game) {
this.game = game;
}
#Column(name="name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Column(name="chips")
public int getChips() {
return chips;
}
public void setChips(int chips) {
this.chips = chips;
}
#Column(name="game_position")
public int getGamePosition() {
return gamePosition;
}
public void setGamePosition(int gamePosition) {
this.gamePosition = gamePosition;
}
#Column(name="finished_place")
public int getFinishPosition() {
return finishPosition;
}
public void setFinishPosition(int finishPosition) {
this.finishPosition = finishPosition;
}
#Column(name="sitting_out")
public boolean isSittingOut() {
return sittingOut;
}
public void setSittingOut(boolean sittingOut) {
this.sittingOut = sittingOut;
}
#Override
public boolean equals(Object o){
if(o == null || !(o instanceof Player)){
return false;
}
Player p = (Player) o;
if(this.getId() == null){
return this.getName().equals(p.getName());
}
return this.getId().equals(p.getId());
}
#Override
public int hashCode(){
if(id == null){
return name.hashCode();
}
return id.hashCode();
}
#Override
#Transient
public int compareTo(Player p){
return this.getGamePosition() - p.getGamePosition();
}
}
I am using HSQL db in the testing environment. Following is the configuration :
#Configuration
#EnableJpaRepositories(basePackages = "com.nitinsurana.repos")
#EnableTransactionManagement
class TestDataConfig {
#Bean(name = "transactionManager")
#Autowired
public PlatformTransactionManager getTransactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setEntityManagerFactory(entityManagerFactory);
return jpaTransactionManager;
}
#Bean
public EntityManagerFactory entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan("com.nitinsurana.domain");
em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
em.setJpaProperties(getHibernateProperties());
em.afterPropertiesSet();
return em.getObject();
}
// #Bean
// public EntityManager entityManager(HibernateEntityManagerFactory entityManagerFactory) {
// HibernateEntityManager entityManager = (HibernateEntityManager) entityManagerFactory.createEntityManager();
// entityManager.setFlushMode(FlushModeType.AUTO FlushMode.ALWAYS);
// return entityManager;
// }
private Properties getHibernateProperties() {
Properties prop = new Properties();
prop.put("hibernate.show_sql", "false");
prop.put("hibernate.hbm2ddl.auto", "create");
prop.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
return prop;
}
#Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
// .addScript("classpath:com/bank/config/sql/schema.sql")
// .addScript("classpath:com/bank/config/sql/test-data.sql")
.build();
}
}
You need to write a FlushAndClear method that works. If not game2 gets not loaded for the database but from the internal cache. And if it is not loaded then the releationship become not updated.
class TestXXX {
#PersistenceContext
private EntityManager em.
private flushAndClear() {
em.flush();
em.clear();
}
}

Generate static map from database using a singleton class also using spring configuration #Autowired

I need to create an unmodifiable map generated from data obtained by querying a database. How, or can I, or is there a better way to do this using spring annotations?
I ran into a problem when creating a singleton for my Regions class and then trying to #Autowire in a RegionService to grab the object from the DAO. The problem is that spring can't instantiate the RegionService because it needs to instantiate the static singleton class Regions which needs to get data from the database as shown below in the constructor.
Please see me classes below (I've removed multiple unneeded methods that don't pertain to this question):
public final class Region {
private static final String DEFAULT_SEPERATOR = "-";
private final Integer key;
private final String description;
public Region(Integer pKey, String pDescription) {
this.key = pKey;
this.description = pDescription;
}
public Integer getKey() {
return this.key;
}
public String getValue() {
return this.description;
}
}
Here is my singleton:
public final class Regions {
private static Regions regionsInstance = null;
#Autowired
private RegionService regionService;
static Map<Integer, Region> regions;
private Regions() {
final Map<Integer, Region> tempRegions = new HashMap<Integer, Region>();
for (final Region region : this.regionService.retrieveAll()) {
tempRegions.put(region.getKey(), region);
}
regions = Collections.unmodifiableMap(tempRegions);
}
public static synchronized Regions getRegionsInstance() {
if (regionsInstance == null) {
regionsInstance = new Regions();
}
return regionsInstance;
}
public Region getRegion(final Integer pKey) {
return regions.get(pKey);
}
public List<Region> getRegions() {
return (List<Region>) regions.values();
}
}
My DAO and Service are just interfaces, no need to post those, here are my Impls:
#Service
public class RegionServiceImpl implements RegionService {
#Autowired
private RegionDAO regionDao;
#Override
public List<Region> retrieveAll() {
return this.regionDao.retrieveAll();
}
}
My DAOImpl (tested and works, just posting to give you the full picture):
#Repository
public class RegionDAOImpl implements RegionDAO {
private static final String SQL_RETRIEVE_REGIONS = "some random SQL";
#Autowired
private JdbcTemplate jdbcTemplate;
#Override
public List<Region> retrieveAll() {
try {
return this.jdbcTemplate.query(SQL_RETRIEVE_REGIONS, new ResultSetExtractor<List<Region>>() {
#Override
public List<Region> extractData(ResultSet rs) throws SQLException, DataAccessException {
return RegionDAOImpl.this.mapRegionData(rs);
}
});
} catch (final DataAccessException dae) {
throw new DaoException("Could not retrieve regionList from database. " + dae);
}
}
protected final List<Region> mapRegionData(ResultSet rs) throws SQLException {
final List<Region> regionList = new ArrayList<Region>();
while (rs.next()) {
regionList.add(new Region(rs.getInt("REGION_CD"), rs.getString("REGION_TXT")));
}
return Collections.unmodifiableList(regionList);
}
}
Then I run my test(I took out unneeded crap):
#..annotated with things you don't need to know
public class RetrieveRegionsTest {
#Autowired
private Regions r;
#Test
public void getAndLogRegion() {
final List<Region> regionDescriptions = new ArrayList<Region>(this.r.getRegions());
for (final Region region : regionDescriptions) {
LOGGER.info(region.getValue());
}
}
Yes my configuration and classpaths are set up properly. I can get this to work other ways, just not by accessing the Regions singleton which is what I want. Now I know I could take off the #Autowired on the RegionService in my Regions singleton and just create a new instance of RegionService, but that would defeat the purpose of springs #Autowired feature.
Any thoughts, ideas, comments?

Resources