001/*
002 * Copyright 2012-2017 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.boot.orm.jpa.hibernate;
018
019import java.util.Locale;
020
021import org.hibernate.boot.model.naming.Identifier;
022import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
023import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
024
025/**
026 * Hibernate {@link PhysicalNamingStrategy} that follows Spring recommended naming
027 * conventions.
028 *
029 * @author Phillip Webb
030 * @author Madhura Bhave
031 * @since 1.4.0
032 */
033public class SpringPhysicalNamingStrategy implements PhysicalNamingStrategy {
034
035        @Override
036        public Identifier toPhysicalCatalogName(Identifier name,
037                        JdbcEnvironment jdbcEnvironment) {
038                return apply(name, jdbcEnvironment);
039        }
040
041        @Override
042        public Identifier toPhysicalSchemaName(Identifier name,
043                        JdbcEnvironment jdbcEnvironment) {
044                return apply(name, jdbcEnvironment);
045        }
046
047        @Override
048        public Identifier toPhysicalTableName(Identifier name,
049                        JdbcEnvironment jdbcEnvironment) {
050                return apply(name, jdbcEnvironment);
051        }
052
053        @Override
054        public Identifier toPhysicalSequenceName(Identifier name,
055                        JdbcEnvironment jdbcEnvironment) {
056                return apply(name, jdbcEnvironment);
057        }
058
059        @Override
060        public Identifier toPhysicalColumnName(Identifier name,
061                        JdbcEnvironment jdbcEnvironment) {
062                return apply(name, jdbcEnvironment);
063        }
064
065        private Identifier apply(Identifier name, JdbcEnvironment jdbcEnvironment) {
066                if (name == null) {
067                        return null;
068                }
069                StringBuilder builder = new StringBuilder(name.getText().replace('.', '_'));
070                for (int i = 1; i < builder.length() - 1; i++) {
071                        if (isUnderscoreRequired(builder.charAt(i - 1), builder.charAt(i),
072                                        builder.charAt(i + 1))) {
073                                builder.insert(i++, '_');
074                        }
075                }
076                return getIdentifier(builder.toString(), name.isQuoted(), jdbcEnvironment);
077        }
078
079        /**
080         * Get an identifier for the specified details. By default this method will return an
081         * identifier with the name adapted based on the result of
082         * {@link #isCaseInsensitive(JdbcEnvironment)}
083         * @param name the name of the identifier
084         * @param quoted if the identifier is quoted
085         * @param jdbcEnvironment the JDBC environment
086         * @return an identifier instance
087         */
088        protected Identifier getIdentifier(String name, boolean quoted,
089                        JdbcEnvironment jdbcEnvironment) {
090                if (isCaseInsensitive(jdbcEnvironment)) {
091                        name = name.toLowerCase(Locale.ROOT);
092                }
093                return new Identifier(name, quoted);
094        }
095
096        /**
097         * Specify whether the database is case sensitive.
098         * @param jdbcEnvironment the JDBC environment which can be used to determine case
099         * @return true if the database is case insensitive sensitivity
100         */
101        protected boolean isCaseInsensitive(JdbcEnvironment jdbcEnvironment) {
102                return true;
103        }
104
105        private boolean isUnderscoreRequired(char before, char current, char after) {
106                return Character.isLowerCase(before) && Character.isUpperCase(current)
107                                && Character.isLowerCase(after);
108        }
109
110}