SpringBoot 3 extend PostgresSQL Dialect - spring-boot

We have the custom postgres dialect, by extending Postgres Dialect, and registerFunctions, but looks like this is not supported in spring boot 3.
What are the alternative ways to implement this in spring boot 3.
public class GlobalPostgresDialect extends PostgreSQL10Dialect {
public static final String STRING_AGG = "string_agg";
public static final String STRING_AGG_ORDER_BY = "string_agg_order_by";
public static final String STRING_AGG_DISTINCT = "string_agg_distinct";
public static final String STRING_AGG_DISTINCT_ORDER_BY = "string_agg_distinct_order_by";
public static final String ARRAY_AGG = "array_agg";
public static final String ARRAY_AGG_DISTINCT = "array_agg_distinct";
public static final String ARRAY_AGG_ORDER_BY = "array_agg_order_by";
public static final String ARRAY_AGG_DISTINCT_ORDER_BY = "array_agg_distinct_order_by";
public static final String COUNT_DISTINCT_5_ARGS = "count_distinct_5_args";
public GlobalPostgresDialect() {
super();
registerFunction(STRING_AGG, new SQLFunctionTemplate(StandardBasicTypes.STRING, "string_agg(?1, ?2)"));
registerFunction(STRING_AGG_ORDER_BY, new SQLFunctionTemplate(StandardBasicTypes.STRING, "string_agg(?1, ?2 order by ?3)"));
registerFunction(STRING_AGG_DISTINCT, new SQLFunctionTemplate(StandardBasicTypes.STRING, "string_agg(distinct ?1, ?2)"));
registerFunction(STRING_AGG_DISTINCT_ORDER_BY, new SQLFunctionTemplate(StandardBasicTypes.STRING, "string_agg(distinct ?1, ?2 order by ?3)"));
registerFunction(ARRAY_AGG, new SQLFunctionTemplate(StandardBasicTypes.STRING, "array_agg(?1)"));
registerFunction(ARRAY_AGG_DISTINCT, new SQLFunctionTemplate(StandardBasicTypes.STRING, "array_agg(distinct ?1)"));
registerFunction(ARRAY_AGG_ORDER_BY, new SQLFunctionTemplate(StandardBasicTypes.STRING, "array_agg(?1 order by ?2)"));
registerFunction(ARRAY_AGG_DISTINCT_ORDER_BY, new SQLFunctionTemplate(StandardBasicTypes.STRING, "array_agg(?1, ?2 order by ?2)"));
registerFunction(COUNT_DISTINCT_5_ARGS, new SQLFunctionTemplate(LongType.INSTANCE, "count(distinct(?1, ?2, ?3, ?4, ?5))"));
}
}

You can use implement MetadataBuilderContributor and register
Documentation:https://docs.jboss.org/hibernate/stable/orm/javadocs/org/hibernate/boot/spi/MetadataBuilderContributor.html
Example:
public class HibernateMetadataBuilderContributor implements MetadataBuilderContributor {
public static final String I_LIKE_FN = "i_like";
public static final String PERSON_TYPES_CONTAINS_FN = "person_types_contains";
#Override
public void contribute(final MetadataBuilder metadataBuilder) {
metadataBuilder.applySqlFunction(I_LIKE_FN, new SQLFunctionTemplate(BooleanType.INSTANCE, "(?1 ilike ?2)"));
metadataBuilder.applySqlFunction(PERSON_TYPES_CONTAINS_FN, new SQLFunctionTemplate(BooleanType.INSTANCE, "(?1 && ?2::person_type_enum[])", true));
}
}
However I am not sure if you still need to register it by properties as well:
spring:
jpa:
properties:
hibernate: your.class.complete.package.HibernateMetadataBuilderContributor
Reference: https://github.com/spring-cloud-portfolio/person-service/blob/d2320f569eebb4c73e10f435dee9ff8618e6bf37/src/main/java/com/doroshenko/serhey/person/repository/core/jpa/HibernateMetadataBuilderContributor.java
You can also read this to see why doing it the way you have done is a bad idea. https://vladmihalcea.com/hibernate-sql-function-jpql-criteria-api-query/

Related

Can I return DTO and domain entities from services?

I have a spring-boot application and I use DTO like that:
Service
#Service
public class UnitOfMeasureServiceImpl implements IUnitOfMeasureService {
private final IUnitsOfMeasureRepository unitOfMeasureRepository;
#Autowired
public UnitOfMeasureServiceImpl(IUnitsOfMeasureRepository unitOfMeasureRepository) {
this.unitOfMeasureRepository = unitOfMeasureRepository;
}
#Override
public UnitOfMeasureDTO getUnitOfMeasureById(UUID id) {
Optional<UnitOfMeasure> optionalUnitOfMeasure = unitOfMeasureRepository.findById(id);
if (!optionalUnitOfMeasure.isPresent()){
// throw new ComponentNotFoundException(id);
return null;
}
return UnitOfMeasureDTO.factory(optionalUnitOfMeasure.get());
}
dto:
#Data
#JsonInclude(JsonInclude.Include.NON_NULL)
public class UnitOfMeasureDTO {
private String id;
private String name;
private String description;
private String sourceInfoCompanyName;
private String originalId;
public static UnitOfMeasureDTO factory(UnitOfMeasure unitOfMeasure) {
UnitOfMeasureDTO dto = new UnitOfMeasureDTO();
dto.id = unitOfMeasure.getId().toString();
dto.name = unitOfMeasure.getName();
dto.description = unitOfMeasure.getDescription();
dto.sourceInfoCompanyName = unitOfMeasure.getSourceInfo().getSourceCompany().getName();
dto.originalId = unitOfMeasure.getOriginalId();
return dto;
}
}
controller:
#RestController
#RequestMapping(UnitOfMeasureController.BASE_URL)
public class UnitOfMeasureController {
public static final String BASE_URL = "/api/sust/v1/unitOfMeasures";
private final IUnitOfMeasureService unitOfMeasureService;
public UnitOfMeasureController(IUnitOfMeasureService unitOfMeasureService) {
this.unitOfMeasureService = unitOfMeasureService;
}
#GetMapping(path = "/{id}")
#ResponseStatus(HttpStatus.OK)
public UnitOfMeasureDTO getUnitOfMeasureDTO(#PathVariable("id") UUID id) {
UnitOfMeasureDTO unitOfMeasureDTO = unitOfMeasureService.getUnitOfMeasureById(id);
return unitOfMeasureDTO;
}
So in my service I have getUnitOfMeasureById(UUID id) that return a UnitOfMeasureDTO.
Now I need to call, from another service, getUnitOfMeasureById(UUID id) that return the domain entity UnitOfMeasure. I think it's correct to call a service method from another service (not a controller method!) and the separation between business logic is at the service layer. So is it correct to have 2 methods: getUnitOfMeasureDTOById and getUnitOfMeasureById in the service? (getUnitOfMeasureDTOById call getUnitOfMeasureById to avoid code duplication)

How to reference a properties value inside the schema attribute of an entity?

There is an entity :
#Entity
#Table(name = "ITMMASTER" , schema = "TOMCTB")
public class Article {
#Id
#Column(name = "ITMREF_0")
private String code_article;
#Column(name = "ACCCOD_0")
private String acccod;
public String getCode_article() {
return code_article;
}
public void setCode_article(String code) {
this.code_article = code;
}
public String getAcccod() {
return acccod;
}
public void setAcccod(String acccod) {
this.acccod = acccod;
}
}
I want to make the schema attribute to be dynamic depending on a properties file property value , for example : env.schema = TOMEXPL.
How to achieve that ?
I didn't tried it but I guess this should work.
public class DynamicNamingStrategy extends DefaultNamingStrategy {
#Value("db.table.name")
private String name;
#Override
public String tableName(String tableName) {
return name;
}
...
}
SessionFactory sessionFactory;
Configuration config = new AnnotationConfiguration()
.configure("hibernate.cfg.xml")
.setNamingStrategy( new DynamicNamingStrategy() );
sessionFactory = config.buildSessionFactory();
session = sessionFactory.openSession();

Ignite : select query returns null

I am new to ignite , I am trying to fetch data using ignite repository but below query returns 'null'.
my repository
#Component
#RepositoryConfig(cacheName = "UserCache")
#Repository
public interface UserRepository extends IgniteRepository<UserEntity, Long> {
#Query("select a.* from UserEntity a where a.lastname=? ")
UserEntity selectUserlastName(String plastName);
My cache configuration as
CacheConfiguration<Long, UserEntity> lUserCacheConfig =
createCacheConfigurationStore("UserCache", UserCacheStore.class);
CacheJdbcPojoStoreFactory<Long, UserEntity> lUserJdbcStoreFactory = new
CacheJdbcPojoStoreFactory<>();
UserJdbcPojoStoreFactory<? super Long, ? super UserEntity>
lUserJdbcPojoStoreFactory = new UserJdbcPojoStoreFactory<>();
lUserJdbcStoreFactory.setDataSource(datasource);
lUserJdbcStoreFactory.setDialect(new OracleDialect());
lUserJdbcStoreFactory.setTypes(lUserJdbcPojoStoreFactory.
configJdbcContactType());
lUserCacheConfig.setCacheStoreFactory(lUserJdbcStoreFactory);
// Configure Cache..
cfg.setCacheConfiguration(lUserCacheConfig);
My PojoStore is as below:
public class UserJdbcPojoStoreFactory<K, V> extends
AnstractJdbcPojoStoreFactory<Long, UserEntity> {
private static final long serialVersionUID = 1L;
#Autowired
DataSource datasource;
#Override
public CacheJdbcPojoStore<Long, UserEntity> create() {
// TODO Auto-generated method stub
setDataSource(datasource);
return super.create();
}
#Override
public JdbcType configJdbcContactType() {
JdbcType jdbcContactType = new JdbcType();
jdbcContactType.setCacheName("UserCache");
jdbcContactType.setKeyType(Long.class);
jdbcContactType.setValueType(UserEntity.class);
jdbcContactType.setDatabaseTable("USER");
jdbcContactType.setDatabaseSchema("ORGNITATION");
jdbcContactType.setKeyFields(new JdbcTypeField(Types.INTEGER, "id",
Long.class, "id"));
jdbcContactType.setValueFields(
new JdbcTypeField(Types.VARCHAR, "NAME", String.class, "NAME"), //
new JdbcTypeField(Types.VARCHAR, "LASTNAME", String.class, "lastname"),
//
return jdbcContactType;
}
}
Please suggest ..
Please check that #Query annotation imported from ignite-spring-data library and test your query using SqlFieldsQuery.

How to add an object to existing List using Spring Mongo db?

this is class A
#Document
class User{
private String id ;
private String name;
#Dbref
private List<Socity> Socitys;
}
and this is class Socity
#Document
class Socity{
private String id ;
private String name;
}
and this is the add user function
public User addUser(User user) {
List<Socity> socity = new ArrayList<>();
user.setsocitys (socity );
return userRepository.save(user);
}
I want to add a socity to an existing user
i try this but it doesn't work
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run (App.class, args);
SocityDao SDao = ctx.getBean(SocityDao .class);
UserRepository userRepository = ctx.getBean(UserRepository.class);
User u = userRepository.findOne("");
Socity s = new Socity("soc1");
SDao .addSocity(e);
u.getSocitys().add(e);
}
this is the rest service
#RequestMapping(value = "up/{id}", method = RequestMethod.POST ,produces =
"application/json")
public User addSocityToUser(#RequestBody Socity, #PathVariable String id)
{
return SocityDAO.addSocityToUser(e, id);
}
In the end of your code add userRepository.save(u) to persist your changes to the DB.
As long as it as an ID (because it is a persisted object) it will be updated. if it has no ID it will be saved as a new object in the DB.
Looks like you forget to save user, after you add new socity. Please check my updates
#Document
public class Socity {
private String id ;
private String name;
}
#Document
public class User {
private String id;
private String name;
#DBRef
private List<Socity> socitys = new ArrayList<>();
}
Then you don't need to use your addUser() method. When you want to add new user just use
userRepository.save(user);
You also need two repositories
public interface SocityRepository extends MongoRepository<Socity, String> {
}
and
public interface UserRepository extends MongoRepository<User, String> {
}
And what you need in the main method
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run (App.class, args);
UserRepository userRepository = ctx.getBean(UserRepository.class);
SocityRepository socityRepository = ctx.getBean(SocityRepository.class);
User u = userRepository.findOne("");
Socity s = socityRepository.save(new Socity("soc1"));
u.getSocitys().add(s);
userRepository.save(u);
}
It is always better to Use the MongoTemplate to write an update Query and use Push function to add to a list.
#Autowired
private MongoTemplate mongoTemplate;
function(String id, Socity socity){
Query query = new Query(Criteria.where("id").is(id));
Update update = new Update();
update.push("Socitys", socity);
mongoTemplate.updateFirst(query, update, User.class);
}

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.

Resources